Hidden Markov Curve — The Three Weather Systems
Picture the market as moving among three weather systems: an upward drifting environment, a downward drifting environment, and a quieter neutral environment. The weather cannot be observed directly. Instead, the program sees short and longer horizon returns, much as a forecaster sees temperature and wind readings while trying to infer the larger atmospheric state.

Each weather system has an assumed pattern of observations and an assumed tendency to persist. The program stores transition preferences describing how likely a state is to remain in place or move to another state. It also stores observation profiles describing the return behavior expected under each environment. These settings are fixed priors and emission assumptions, not a fully fitted hidden Markov model.

An older calibration period supplies a return scale. The program then replays a more recent sequence from old observations toward the newest completed bar. At each step, it first predicts the distribution of possible weather systems using the transition preferences. It next compares the observed return pair with the observation profile of every state.

The resulting state weights are normalized into a new posterior distribution. A surprising observation may weaken the previously favored state, but the persistence assumptions prevent every small fluctuation from instantly replacing the entire weather forecast. The process combines continuity with new evidence rather than treating either as sufficient alone.

A buying decision requires a strong posterior preference for the upward environment. A strong preference for the downward environment supports selling. If the neutral environment dominates, or if the evidence remains divided, the program avoids opening a directional position. Existing positions can be closed when support for their corresponding directional state falls below a lower retention threshold.

Model diagnostics report posterior uncertainty, a projected future probability of the upward state, and uncertainty associated with possible transitions. These measures describe the internal state distribution. They can help distinguish a confident directional classification from a market that is moving ambiguously among competing interpretations.

Price stops, profit targets, limited position size, and a maximum lifetime remain active because a confident classification is not equivalent to a reliable profit forecast. The short and longer return observations overlap, so the simplified emission treatment does not represent every statistical dependency.

The weather analogy must therefore remain conditional. The market may develop environments that the three predefined states do not represent. Reported posterior weights are probabilities inside the assumed model, not independently established real world probabilities. Calibration, alternative state definitions, transaction costs, and genuinely unseen historical periods must be examined before these classifications can support strong practical conclusions.

Code
// Independent model 08: three-state Bayesian forward filter on return horizons.
#include <default.c>

var HMPrice[110];
var HMProbability[3];
var HMTransition[9] = {0.94, 0.03, 0.03, 0.03, 0.94, 0.03, 0.03, 0.03, 0.94};
var HMMean[3] = {1., -1., 0.};
var HMVariance[3] = {0.75, 0.75, 0.2};

int HMForwardInference()
{
    int T, State, From;
    var Sigma, R, LongObservation, ShortObservation, Predicted, Error;
    var Evidence, Total;
    var Updated[3];
    for (State = 0; State < 3; State++)
    {
        HMProbability[State] = 1. / 3.;
    }
    for (T = 0; T < 110; T++)
    {
        if (!(HMPrice[T] > 0.))
        {
            return 0;
        }
    }
    Sigma = 0.;
    for (T = 81; T <= 100; T++)
    {
        R = log(HMPrice[T] / HMPrice[T + 1]);
        Sigma += R * R / 20.;
    }
    if (Sigma < 1.e-20)
    {
        return 0;
    }
    Sigma = sqrt(Sigma);
    // Fixed emissions and transition priors; no fitted HMM or hindsight labels.
    for (T = 80; T >= 0; T--)
    {
        ShortObservation = log(HMPrice[T] / HMPrice[T + 1]) / Sigma;
        LongObservation = log(HMPrice[T] / HMPrice[T + 4]) / (4. * Sigma);
        Total = 0.;
        for (State = 0; State < 3; State++)
        {
            Predicted = 0.;
            for (From = 0; From < 3; From++)
            {
                Predicted += HMProbability[From] * HMTransition[From * 3 + State];
            }
            Error = (ShortObservation - HMMean[State]) * (ShortObservation - HMMean[State]);
            Error += (LongObservation - HMMean[State]) * (LongObservation - HMMean[State]);
            Evidence = exp(max(-80., -Error / (2. * HMVariance[State]))) / HMVariance[State];
            Updated[State] = Predicted * Evidence;
            Total += Updated[State];
        }
        if (Total <= 1.e-100)
        {
            return 0;
        }
        for (State = 0; State < 3; State++)
        {
            HMProbability[State] = Updated[State] / Total;
        }
    }
    if (HMProbability[0] > 0.85)
    {
        return 1;
    }
    if (HMProbability[1] > 0.85)
    {
        return -1;
    }
    return 0;
}

void HMCheckPosterior()
{
    int T, Problems;
    var Sum;
    Problems = 0;
    for (T = 0; T < 110; T++)
    {
        HMPrice[T] = 1.;
    }
    if (HMForwardInference() != 0)
    {
        Problems++;
    }
    for (T = 0; T < 110; T++)
    {
        HMPrice[T] = exp(-0.001 * T);
    }
    if (HMForwardInference() != 1)
    {
        Problems++;
    }
    Sum = HMProbability[0] + HMProbability[1] + HMProbability[2];
    if (abs(Sum - 1.) > 1.e-6)
    {
        Problems++;
    }
    for (T = 0; T < 110; T++)
    {
        HMPrice[T] = 1. / HMPrice[T];
    }
    if (HMForwardInference() != -1)
    {
        Problems++;
    }
    file_write("Log/TS08_HiddenMarkovCurve_test.txt", strf("4 checks; %d failures\n", Problems), 0);
}

var HMPosteriorEntropy()
{
    int State;
    var H;
    H = 0.;
    for (State = 0; State < 3; State++)
    {
        if (HMProbability[State] > 1.e-20)
        {
            H -= HMProbability[State] * log(HMProbability[State]) / log(3.);
        }
    }
    return H;
}
var HMFutureBullProbability(int Steps)
{
    int Step, From, To;
    var P[3];
    var Next[3];
    if (Steps < 0 || Steps > 64)
    {
        return 0.;
    }
    for (From = 0; From < 3; From++)
    {
        P[From] = HMProbability[From];
    }
    for (Step = 0; Step < Steps; Step++)
    {
        for (To = 0; To < 3; To++)
        {
            Next[To] = 0.;
            for (From = 0; From < 3; From++)
            {
                Next[To] += P[From] * HMTransition[From * 3 + To];
            }
        }
        for (To = 0; To < 3; To++)
        {
            P[To] = Next[To];
        }
    }
    return P[0];
}
var HMTransitionUncertainty()
{
    int S, T;
    var Result, P;
    Result = 0.;
    for (S = 0; S < 3; S++)
    {
        for (T = 0; T < 3; T++)
        {
            P = HMTransition[S * 3 + T];
            if (P > 0.)
            {
                Result -= HMProbability[S] * P * log(P);
            }
        }
    }
    return Result;
}
void HMResearchDiagnostics()
{
    printf("\nHMM bull %.6g; bear %.6g; neutral %.6g", HMProbability[0], HMProbability[1],
           HMProbability[2]);
    printf("\nHMM entropy %.6g; bull4 %.6g", HMPosteriorEntropy(), HMFutureBullProbability(4));
    printf("\nHMM transition uncertainty %.6g", HMTransitionUncertainty());
}

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

void HMCase_quiet_cycle()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 110; K++)
    {
        HMPrice[K] = 1.0 * exp(0.0005 * sin(0.17 * K));
    }
    Original = HMForwardInference();
    HMExpect(Original >= -1 && Original <= 1, "quiet_cycle: bounded direction");
    Again = HMForwardInference();
    HMExpect(Again == Original, "quiet_cycle: repeatable estimate");
    for (K = 0; K < 110; K++)
    {
        HMPrice[K] *= 7.;
    }
    Scaled = HMForwardInference();
    HMExpect(Scaled == Original, "quiet_cycle: price-unit invariance");
    for (K = 0; K < 110; K++)
    {
        HMPrice[K] = 1. / HMPrice[K];
    }
    Reflected = HMForwardInference();
    HMExpect(Reflected == -Original, "quiet_cycle: directional reflection");
    HMPrice[55] = 0.;
    HMExpect(HMForwardInference() == 0, "quiet_cycle: missing price rejected");
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

void HMExtendedTests()
{
    HMExtendedCount = 0;
    HMExtendedFailures = 0;
    file_write("Log/TS08_HiddenMarkovCurve_extended.txt",
               "TS08_HiddenMarkovCurve: extended properties\n", 0);
    HMCase_quiet_cycle();
    HMCase_mixed_cycles();
    HMCase_persistent_upward();
    HMCase_persistent_downward();
    HMCase_slow_curve();
    HMCase_rough_curve();
    HMCase_accelerating_path();
    HMCase_decelerating_path();
    HMCase_volatility_ramp();
    HMCase_volatility_decay();
    HMCase_cycle_with_drift();
    HMCase_near_zero_prices();
    HMCase_large_price_units();
    HMCase_high_frequency_chop();
    HMCase_long_wave_trend();
    HMCase_opposing_scales();
    HMCase_nonlinear_curve();
    HMCase_amplitude_modulation();
    HMCase_decaying_event();
    HMCase_time_varying_frequency();
    HMCase_slow_fast_cancellation();
    file_append("Log/TS08_HiddenMarkovCurve_extended.txt",
                strf("RESULT: %d checks; %d failures\n", HMExtendedCount, HMExtendedFailures));
}

function run()
{
#ifdef STX_SELFTEST
    if (is(INITRUN))
    {
        HMCheckPosterior();
        HMExtendedTests();
        quit("HMM tests complete");
    }
    return;
#endif
    int T, Regime;
    var Units, AtRisk;
    BarPeriod = 60;
    LookBack = 111;
    Capital = 10000.;
    StartDate = 20240101;
    EndDate = 20241231;
    MaxLong = 1;
    MaxShort = 1;
    Hedge = 0;
    if (!asset("EUR/USD"))
    {
        return;
    }
    algo("HM08");
    if (is(EXITRUN))
    {
        HMResearchDiagnostics();
        return;
    }
    if (is(LOOKBACK))
    {
        return;
    }
    for (T = 0; T < 110; T++)
    {
        HMPrice[T] = priceClose(T + 1);
    }
    Regime = HMForwardInference();
    if (NumOpenLong > 0 && HMProbability[0] < 0.45)
    {
        exitLong();
    }
    if (NumOpenShort > 0 && HMProbability[1] < 0.45)
    {
        exitShort();
    }
    if (Regime == 0 || NumOpenLong + NumOpenShort > 0)
    {
        return;
    }
    if (Balance <= 0. || Equity <= Balance * 0.85)
    {
        return;
    }
    if (MarginCost <= 0. || PIP <= 0. || PIPCost <= 0. || LotAmount <= 0.)
    {
        return;
    }
    Stop = 50. * PIP;
    TakeProfit = 125. * PIP;
    LifeTime = 32;
    AtRisk = PIPCost * (Stop + Spread) / PIP + Commission * LotAmount / 10000.;
    if (AtRisk <= 0.)
    {
        return;
    }
    Units = min(2000. / LotAmount, Balance * 0.005 / AtRisk);
    Units = min(Units, max(0., Equity - MarginVal) * 0.8 / MarginCost);
    if (Units < 1.)
    {
        return;
    }
    Lots = floor(Units);
    Margin = 0.;
    Risk = 0.;
    if (Regime > 0)
    {
        enterLong();
    }
    else
    {
        enterShort();
    }
}