Kalman Curve — The Navigator Behind the Fog
Imagine a navigator trying to follow a road whose surface is obscured by fog. The visible clues are returns measured over several horizons. Each clue offers a different perspective, but each is noisy and overlaps with other clues. The navigator's task is not to accept any single reading literally, but to maintain a compact internal map of the road.

The internal map contains three features: a general level, a slope across horizons, and a curvature describing how that slope bends. These are latent features, meaning that the program does not observe them directly. It infers them from completed prices through a sequential Kalman filtering procedure. The current implementation rebuilds the filtering sequence from an older observation window on each decision bar.

The navigator processes observations chronologically, beginning with older information and ending with the newest completed candle. Before accepting a new reading, it allows a small amount of uncertainty to accumulate. It then compares the reading with the internal prediction and adjusts the latent map according to the model's uncertainty and its assumed observation noise.

A surprising reading does not automatically become a trade. The program also asks whether the inferred general direction, the interpretation of the horizon slope, and the newest short horizon innovation agree. A buying opportunity requires an upward latent tendency accompanied by the required short horizon acceleration. Selling requires the corresponding downward alignment. Conflicting clues leave the strategy neutral.

The navigator can abandon an existing long position when the inferred general direction becomes nonpositive, or abandon a short position when that direction becomes nonnegative. A maximum holding period and protective price exits provide additional boundaries. Position sizing considers both the actual stop distance and available capital rather than assuming that filter confidence eliminates risk.

Model specific diagnostics reconstruct expected readings at selected horizons, estimate uncertainty around those reconstructed readings, and measure how closely the inferred curve describes the observed return curve. These outputs help explain whether a trade follows a broad latent change or merely a short lived observation surprise.

The fog analogy also highlights the limitations. A compact map can be wrong, especially when the road changes shape faster than the model permits. Overlapping horizon observations are not fully independent, although the simplified observation updates do not model every dependency. The displayed uncertainty therefore belongs to the chosen filtering assumptions and should not be mistaken for a complete probability statement about future Forex prices.

Code
// Independent model 03: latent level/slope/curvature via sequential Kalman updates.
#include <default.c>

var KFPrices[100];
var KFState[3];
var KFUncertainty[9];
var KFSlopeScore = 0.;
var KFInnovationScore = 0.;
int KFHorizons[5] = {2, 4, 8, 16, 32};

int KFFilterCurve()
{
    int T, H, A, B;
    var Observation, KFCoordinate, R, Variance, Innovation, Predicted;
    var KFObservationVector[3];
    var KFGainVector[3];
    var KFProjectedCov[3];
    var KFPreviousCov[9];
    KFSlopeScore = 0.;
    KFInnovationScore = 0.;
    for (T = 0; T < 100; T++)
    {
        if (!(KFPrices[T] > 0.))
        {
            return 0;
        }
    }
    R = 0.;
    for (T = 1; T <= 48; T++)
    {
        Observation = log(KFPrices[T] / KFPrices[T + 1]);
        R += Observation * Observation / 48.;
    }
    if (R < 1.e-20)
    {
        return 0;
    }
    for (A = 0; A < 3; A++)
    {
        KFState[A] = 0.;
    }
    for (A = 0; A < 9; A++)
    {
        KFUncertainty[A] = 0.;
    }
    for (A = 0; A < 3; A++)
    {
        KFUncertainty[A * 3 + A] = R;
    }
    // Replay old -> new; current decision observations enter only at the end.
    for (T = 48; T >= 0; T--)
    {
        KFUncertainty[0] += R * 0.01;
        KFUncertainty[4] += R * 0.002;
        KFUncertainty[8] += R * 0.001;
        for (H = 0; H < 5; H++)
        {
            KFCoordinate = log(KFHorizons[H]) / log(2.) - 3.;
            KFObservationVector[0] = 1.;
            KFObservationVector[1] = KFCoordinate;
            KFObservationVector[2] = KFCoordinate * KFCoordinate - 2.;
            Observation = log(KFPrices[T] / KFPrices[T + KFHorizons[H]]) / KFHorizons[H];
            Predicted = 0.;
            Variance = R / KFHorizons[H];
            for (A = 0; A < 3; A++)
            {
                Predicted += KFObservationVector[A] * KFState[A];
                KFProjectedCov[A] = 0.;
                for (B = 0; B < 3; B++)
                {
                    KFProjectedCov[A] += KFUncertainty[A * 3 + B] * KFObservationVector[B];
                }
                Variance += KFObservationVector[A] * KFProjectedCov[A];
            }
            if (Variance <= 1.e-25)
            {
                return 0;
            }
            Innovation = Observation - Predicted;
            if (T == 0 && H == 0)
            {
                KFInnovationScore = Innovation / sqrt(Variance);
            }
            for (A = 0; A < 9; A++)
            {
                KFPreviousCov[A] = KFUncertainty[A];
            }
            for (A = 0; A < 3; A++)
            {
                KFGainVector[A] = KFProjectedCov[A] / Variance;
                KFState[A] += KFGainVector[A] * Innovation;
                for (B = 0; B < 3; B++)
                {
                    KFUncertainty[A * 3 + B] =
                        KFPreviousCov[A * 3 + B] - KFGainVector[A] * KFProjectedCov[B];
                }
            }
        }
    }
    KFSlopeScore = -KFState[1] / sqrt(max(1.e-25, KFUncertainty[4]));
    // Short-horizon acceleration must agree with the latent average direction.
    if (KFState[0] > 0. && KFSlopeScore > 1. && KFInnovationScore > 0.25)
    {
        return 1;
    }
    if (KFState[0] < 0. && KFSlopeScore < -1. && KFInnovationScore < -0.25)
    {
        return -1;
    }
    return 0;
}

void KFVerify()
{
    int T, Positive, Negative, Bad;
    Bad = 0;
    for (T = 0; T < 100; T++)
    {
        KFPrices[T] = 1.;
    }
    if (KFFilterCurve() != 0)
    {
        Bad++;
    }
    for (T = 0; T < 100; T++)
    {
        KFPrices[T] = exp(-0.0003 * T + 0.002 * cos(0.21 * T));
    }
    Positive = KFFilterCurve();
    if (Positive < -1 || Positive > 1)
    {
        Bad++;
    }
    for (T = 0; T < 100; T++)
    {
        KFPrices[T] = 1. / KFPrices[T];
    }
    Negative = KFFilterCurve();
    if (Negative != -Positive)
    {
        Bad++;
    }
    KFPrices[0] = 0.;
    if (KFFilterCurve() != 0)
    {
        Bad++;
    }
    file_write("Log/TS03_KalmanCurve_test.txt", strf("4 checks; %d failures\n", Bad), 0);
}

var KFPredictHorizon(int Horizon)
{
    var Coordinate;
    if (Horizon < 1)
    {
        return 0.;
    }
    Coordinate = log(Horizon) / log(2.) - 3.;
    return KFState[0] + Coordinate * KFState[1] + (Coordinate * Coordinate - 2.) * KFState[2];
}
var KFUncertaintyAt(int Horizon)
{
    var H[3];
    var Estimate, X;
    int A, B;
    if (Horizon < 1)
    {
        return 0.;
    }
    X = log(Horizon) / log(2.) - 3.;
    H[0] = 1.;
    H[1] = X;
    H[2] = X * X - 2.;
    Estimate = 0.;
    for (A = 0; A < 3; A++)
    {
        for (B = 0; B < 3; B++)
        {
            Estimate += H[A] * KFUncertainty[A * 3 + B] * H[B];
        }
    }
    return sqrt(max(0., Estimate));
}
var KFReconstructionLoss()
{
    int H;
    var Observed, KFFitError, Sum;
    Sum = 0.;
    for (H = 0; H < 5; H++)
    {
        Observed = log(KFPrices[0] / KFPrices[KFHorizons[H]]) / KFHorizons[H];
        KFFitError = Observed - KFPredictHorizon(KFHorizons[H]);
        Sum += KFFitError * KFFitError / 5.;
    }
    return sqrt(Sum);
}
void KFResearchDiagnostics()
{
    printf("\nKalman level %.6g; slope %.6g; curvature %.6g; horizon8 uncertainty %.6g; fit %.6g",
           KFState[0], KFState[1], KFState[2], KFUncertaintyAt(8), KFReconstructionLoss());
}

// ============================================================================
// EXTENDED REGRESSION SUITE: deterministic properties over market regimes
// These functions run ONLY under STX_SELFTEST; they never place orders.
// ============================================================================
int KFExtendedCount = 0;
int KFExtendedFailures = 0;
void KFExpect(int Passed, string Description)
{
    KFExtendedCount++;
    if (!Passed)
    {
        KFExtendedFailures++;
        file_append("Log/TS03_KalmanCurve_extended.txt", strf("FAIL: %s\n", Description));
    }
}

void KFCase_quiet_cycle()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp(0.0005 * sin(0.17 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "quiet_cycle: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "quiet_cycle: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "quiet_cycle: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "quiet_cycle: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "quiet_cycle: missing price rejected");
}

void KFCase_mixed_cycles()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp(0.003 * sin(0.23 * K) + 0.0007 * cos(0.81 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "mixed_cycles: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "mixed_cycles: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "mixed_cycles: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "mixed_cycles: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "mixed_cycles: missing price rejected");
}

void KFCase_persistent_upward()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp(-0.001 * K + 0.0001 * sin(0.53 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "persistent_upward: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "persistent_upward: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "persistent_upward: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "persistent_upward: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "persistent_upward: missing price rejected");
}

void KFCase_persistent_downward()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp(0.001 * K + 0.0001 * cos(0.47 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "persistent_downward: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "persistent_downward: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "persistent_downward: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "persistent_downward: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "persistent_downward: missing price rejected");
}

void KFCase_slow_curve()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp(0.004 * cos(0.08 * K) + 0.0002 * sin(0.7 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "slow_curve: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "slow_curve: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "slow_curve: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "slow_curve: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "slow_curve: missing price rejected");
}

void KFCase_rough_curve()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp(0.001 * sin(1.7 * K) + 0.0005 * cos(2.3 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "rough_curve: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "rough_curve: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "rough_curve: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "rough_curve: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "rough_curve: missing price rejected");
}

void KFCase_accelerating_path()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp(-0.0001 * K - 0.000002 * K * K + 0.0002 * sin(0.41 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "accelerating_path: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "accelerating_path: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "accelerating_path: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "accelerating_path: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "accelerating_path: missing price rejected");
}

void KFCase_decelerating_path()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp(-0.0008 * K + 0.000002 * K * K + 0.0002 * cos(0.31 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "decelerating_path: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "decelerating_path: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "decelerating_path: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "decelerating_path: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "decelerating_path: missing price rejected");
}

void KFCase_volatility_ramp()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp((0.0001 + K * 0.00001) * sin(0.37 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "volatility_ramp: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "volatility_ramp: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "volatility_ramp: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "volatility_ramp: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "volatility_ramp: missing price rejected");
}

void KFCase_volatility_decay()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp(0.006 * exp(-0.02 * K) * cos(0.7 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "volatility_decay: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "volatility_decay: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "volatility_decay: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "volatility_decay: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "volatility_decay: missing price rejected");
}

void KFCase_cycle_with_drift()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp(-0.0002 * K + 0.003 * sin(0.19 * K) + 0.0005 * cos(0.61 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "cycle_with_drift: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "cycle_with_drift: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "cycle_with_drift: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "cycle_with_drift: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "cycle_with_drift: missing price rejected");
}

void KFCase_near_zero_prices()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 0.0001 * exp(0.002 * sin(0.29 * K) + 0.001 * cos(0.13 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "near_zero_prices: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "near_zero_prices: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "near_zero_prices: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "near_zero_prices: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "near_zero_prices: missing price rejected");
}

void KFCase_large_price_units()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 10000.0 * exp(0.003 * sin(0.11 * K) + 0.0008 * cos(0.43 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "large_price_units: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "large_price_units: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "large_price_units: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "large_price_units: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "large_price_units: missing price rejected");
}

void KFCase_high_frequency_chop()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp(0.0008 * cos(2.8 * K) + 0.001 * sin(0.23 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "high_frequency_chop: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "high_frequency_chop: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "high_frequency_chop: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "high_frequency_chop: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "high_frequency_chop: missing price rejected");
}

void KFCase_long_wave_trend()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp(-0.0005 * K + 0.002 * cos(0.07 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "long_wave_trend: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "long_wave_trend: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "long_wave_trend: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "long_wave_trend: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "long_wave_trend: missing price rejected");
}

void KFCase_opposing_scales()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp(0.005 * sin(0.09 * K) - 0.002 * sin(0.59 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "opposing_scales: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "opposing_scales: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "opposing_scales: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "opposing_scales: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "opposing_scales: missing price rejected");
}

void KFCase_nonlinear_curve()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp(0.002 * sin(0.021 * K * K) + 0.001 * cos(0.33 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "nonlinear_curve: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "nonlinear_curve: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "nonlinear_curve: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "nonlinear_curve: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "nonlinear_curve: missing price rejected");
}

void KFCase_amplitude_modulation()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp(0.002 * sin(0.3 * K) * cos(0.04 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "amplitude_modulation: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "amplitude_modulation: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "amplitude_modulation: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "amplitude_modulation: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "amplitude_modulation: missing price rejected");
}

void KFCase_decaying_event()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp(0.01 * exp(-0.2 * K) + 0.0003 * sin(0.71 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "decaying_event: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "decaying_event: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "decaying_event: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "decaying_event: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "decaying_event: missing price rejected");
}

void KFCase_time_varying_frequency()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp(0.002 * cos(0.004 * K * K + 0.11 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "time_varying_frequency: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "time_varying_frequency: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "time_varying_frequency: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "time_varying_frequency: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "time_varying_frequency: missing price rejected");
}

void KFCase_slow_fast_cancellation()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1.0 * exp(0.004 * sin(0.07 * K) + 0.0013 * cos(0.97 * K));
    }
    Original = KFFilterCurve();
    KFExpect(Original >= -1 && Original <= 1, "slow_fast_cancellation: bounded direction");
    Again = KFFilterCurve();
    KFExpect(Again == Original, "slow_fast_cancellation: repeatable estimate");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] *= 7.;
    }
    Scaled = KFFilterCurve();
    KFExpect(Scaled == Original, "slow_fast_cancellation: price-unit invariance");
    for (K = 0; K < 100; K++)
    {
        KFPrices[K] = 1. / KFPrices[K];
    }
    Reflected = KFFilterCurve();
    KFExpect(Reflected == -Original, "slow_fast_cancellation: directional reflection");
    KFPrices[50] = 0.;
    KFExpect(KFFilterCurve() == 0, "slow_fast_cancellation: missing price rejected");
}

void KFExtendedTests()
{
    KFExtendedCount = 0;
    KFExtendedFailures = 0;
    file_write("Log/TS03_KalmanCurve_extended.txt", "TS03_KalmanCurve: extended properties\n", 0);
    KFCase_quiet_cycle();
    KFCase_mixed_cycles();
    KFCase_persistent_upward();
    KFCase_persistent_downward();
    KFCase_slow_curve();
    KFCase_rough_curve();
    KFCase_accelerating_path();
    KFCase_decelerating_path();
    KFCase_volatility_ramp();
    KFCase_volatility_decay();
    KFCase_cycle_with_drift();
    KFCase_near_zero_prices();
    KFCase_large_price_units();
    KFCase_high_frequency_chop();
    KFCase_long_wave_trend();
    KFCase_opposing_scales();
    KFCase_nonlinear_curve();
    KFCase_amplitude_modulation();
    KFCase_decaying_event();
    KFCase_time_varying_frequency();
    KFCase_slow_fast_cancellation();
    file_append("Log/TS03_KalmanCurve_extended.txt",
                strf("RESULT: %d checks; %d failures\n", KFExtendedCount, KFExtendedFailures));
}

function run()
{
#ifdef STX_SELFTEST
    if (is(INITRUN))
    {
        KFVerify();
        KFExtendedTests();
        quit("Kalman tests complete");
    }
    return;
#endif
    int T, Forecast;
    var KFLotsAllowed, UnitLoss;
    BarPeriod = 60;
    LookBack = 101;
    Capital = 10000.;
    StartDate = 20240101;
    EndDate = 20241231;
    Hedge = 0;
    MaxLong = 1;
    MaxShort = 1;
    if (!asset("EUR/USD"))
    {
        return;
    }
    algo("KF03");
    if (is(EXITRUN))
    {
        KFResearchDiagnostics();
        return;
    }
    if (is(LOOKBACK))
    {
        return;
    }
    for (T = 0; T < 100; T++)
    {
        KFPrices[T] = priceClose(T + 1);
    }
    Forecast = KFFilterCurve();
    if (NumOpenLong > 0 && KFState[0] <= 0.)
    {
        exitLong();
    }
    if (NumOpenShort > 0 && KFState[0] >= 0.)
    {
        exitShort();
    }
    if (Forecast == 0 || NumOpenLong + NumOpenShort > 0)
    {
        return;
    }
    if (Balance <= 0. || Equity < 0.85 * Balance || MarginCost <= 0. || LotAmount <= 0.)
    {
        return;
    }
    if (PIP <= 0. || PIPCost <= 0.)
    {
        return;
    }
    Stop = max(30. * PIP, KFPrices[0] * sqrt(max(0., KFUncertainty[0])) * 4.);
    TakeProfit = Stop * 2.5;
    LifeTime = 24;
    UnitLoss = (Stop + Spread) / PIP * PIPCost + Commission * LotAmount / 10000.;
    if (UnitLoss <= 0.)
    {
        return;
    }
    KFLotsAllowed = min(Balance * 0.005 / UnitLoss, 2000. / LotAmount);
    KFLotsAllowed = min(KFLotsAllowed, max(0., Equity - MarginVal) * 0.8 / MarginCost);
    if (KFLotsAllowed < 1.)
    {
        return;
    }
    Margin = 0.;
    Risk = 0.;
    Lots = floor(KFLotsAllowed);
    if (Forecast > 0)
    {
        enterLong();
    }
    else
    {
        enterShort();
    }
}