Entropy Analogues — The Historical Pattern Library
Imagine a library whose books describe earlier shapes of the market's return curve. Instead of imposing one fixed trend equation, the program asks whether the current arrangement resembles patterns already present in that library. It then looks at what followed those earlier patterns, using only outcomes that are already known at the current decision time.

Before searching the shelves, the program measures an aspect of local disorder. It records the relative ordering of neighboring returns and counts how frequently different orderings appear. When those orderings are distributed almost uniformly, the market receives a high entropy reading. That suggests weak ordinal structure and reduces the justification for interpreting nearby patterns as useful analogues.

The pattern description itself contains average returns across several horizons. Historical scale estimates keep one horizon from dominating the distance calculation merely because its typical numerical movement is larger. The program compares the current curve with eligible older curves and retains a small collection of the nearest matches.

The historical candidates are separated from the newest observation by a safety gap. Their subsequent short horizon outcomes have already occurred, so the program does not borrow information from the actual future of the current decision. Each selected candidate contributes its known outcome, with closer matches receiving greater weight in the forecast.

An entry requires more than a positive or negative weighted average. The selected matches must be reasonably close, the entropy gate must permit interpretation, and the forecast must be meaningful relative to disagreement among the historical outcomes. A positive accepted forecast supports buying, while a negative accepted forecast supports selling.

The intended prediction horizon is short, and the position lifetime reflects that horizon. Strong disorder can close existing positions, while an accepted forecast in the opposite direction can cause the strategy to exit before considering reversal. Protective price exits and exposure limits remain necessary because even a well matched analogue can lead to a different subsequent path.

Additional diagnostics describe recurrence frequency, dispersion of historical forward outcomes, and the complement of ordinal entropy. These provide context for how frequently similar patterns appear and how consistent their consequences have been.

The library metaphor exposes several weaknesses. Neighboring historical candidates can overlap and therefore fail to represent independent examples. The same apparent shape can arise under different economic conditions. Distance thresholds and the number of selected neighbors are research choices rather than universal constants. The program is a local historical forecasting experiment, not proof that markets repeat themselves or that remembered patterns remain profitable after trading costs.

Code
// Independent model 09: ordinal entropy, return-curve recurrence and analogues.
#include <default.c>

var NNPrices[180];
var NNForecast = 0.;
var NNEntropy = 1.;
int NNUsed = 0;
int NNHorizons[3] = {1, 4, 16};

int NNOrdinal(var A, var B, var C)
{
    if (A <= B && B <= C)
    {
        return 0;
    }
    if (A <= C && C < B)
    {
        return 1;
    }
    if (B < A && A <= C)
    {
        return 2;
    }
    if (B <= C && C < A)
    {
        return 3;
    }
    if (C < A && A <= B)
    {
        return 4;
    }
    return 5;
}

int NNFindRecurrences()
{
    int T, J, Rank, Slot, Pattern;
    int Frequency[6];
    var CurrentCurve[3];
    var Scale[3];
    var Mean[3];
    var Nearest[8];
    var Outcomes[8];
    var A, B, C, Feature, Difference, Distance, Target, Probability, Weight, SumWeight, Variance;
    NNForecast = 0.;
    NNUsed = 0.;
    NNEntropy = 0.;
    for (T = 0; T < 180; T++)
    {
        if (!(NNPrices[T] > 0.))
        {
            return 0;
        }
    }
    for (Pattern = 0; Pattern < 6; Pattern++)
    {
        Frequency[Pattern] = 0;
    }
    for (T = 1; T <= 48; T++)
    {
        A = log(NNPrices[T] / NNPrices[T + 1]);
        B = log(NNPrices[T + 1] / NNPrices[T + 2]);
        C = log(NNPrices[T + 2] / NNPrices[T + 3]);
        Frequency[NNOrdinal(A, B, C)]++;
    }
    for (Pattern = 0; Pattern < 6; Pattern++)
    {
        Probability = Frequency[Pattern] / 48.;
        if (Probability > 0.)
        {
            NNEntropy -= Probability * log(Probability) / log(6.);
        }
    }
    for (J = 0; J < 3; J++)
    {
        Mean[J] = 0.;
        Scale[J] = 0.;
        for (T = 12; T <= 140; T++)
        {
            Mean[J] += log(NNPrices[T] / NNPrices[T + NNHorizons[J]]) / NNHorizons[J] / 129.;
        }
        for (T = 12; T <= 140; T++)
        {
            Feature = log(NNPrices[T] / NNPrices[T + NNHorizons[J]]) / NNHorizons[J];
            Scale[J] += (Feature - Mean[J]) * (Feature - Mean[J]) / 129.;
        }
        if (Scale[J] < 1.e-22)
        {
            return 0;
        }
        CurrentCurve[J] = log(NNPrices[0] / NNPrices[NNHorizons[J]]) / NNHorizons[J];
    }
    for (Rank = 0; Rank < 8; Rank++)
    {
        Nearest[Rank] = 1.e100;
        Outcomes[Rank] = 0.;
    }
    for (T = 12; T <= 140; T++)
    {
        Distance = 0.;
        for (J = 0; J < 3; J++)
        {
            Feature = log(NNPrices[T] / NNPrices[T + NNHorizons[J]]) / NNHorizons[J];
            Difference = CurrentCurve[J] - Feature;
            Distance += Difference * Difference / Scale[J];
        }
        // The forward target of every historical analogue is already observable.
        Target = log(NNPrices[T - 4] / NNPrices[T]);
        for (Rank = 0; Rank < 8; Rank++)
        {
            if (Distance < Nearest[Rank])
            {
                for (Slot = 7; Slot > Rank; Slot--)
                {
                    Nearest[Slot] = Nearest[Slot - 1];
                    Outcomes[Slot] = Outcomes[Slot - 1];
                }
                Nearest[Rank] = Distance;
                Outcomes[Rank] = Target;
                break;
            }
        }
    }
    SumWeight = 0.;
    Variance = 0.;
    for (Rank = 0; Rank < 8; Rank++)
    {
        Weight = 1. / (0.1 + Nearest[Rank]);
        NNForecast += Weight * Outcomes[Rank];
        SumWeight += Weight;
        NNUsed++;
    }
    if (SumWeight <= 0.)
    {
        return 0;
    }
    NNForecast /= SumWeight;
    for (Rank = 0; Rank < 8; Rank++)
    {
        Difference = Outcomes[Rank] - NNForecast;
        Variance += Difference * Difference / 8.;
    }
    if (NNEntropy > 0.97 || Nearest[7] > 4.)
    {
        return 0;
    }
    if (abs(NNForecast) < sqrt(max(1.e-22, Variance)) * 0.5)
    {
        return 0;
    }
    if (NNForecast > 0.)
    {
        return 1;
    }
    if (NNForecast < 0.)
    {
        return -1;
    }
    return 0;
}

void NNTestHistory()
{
    int T, One, Mirror, Mistakes;
    Mistakes = 0;
    for (T = 0; T < 180; T++)
    {
        NNPrices[T] = 1.;
    }
    if (NNFindRecurrences() != 0)
    {
        Mistakes++;
    }
    for (T = 0; T < 180; T++)
    {
        NNPrices[T] = exp(0.003 * sin(0.17 * T) + 0.001 * cos(0.41 * T));
    }
    One = NNFindRecurrences();
    if (NNUsed != 8 || One < -1 || One > 1)
    {
        Mistakes++;
    }
    for (T = 0; T < 180; T++)
    {
        NNPrices[T] = 1. / NNPrices[T];
    }
    Mirror = NNFindRecurrences();
    if (Mirror != -One)
    {
        Mistakes++;
    }
    NNPrices[0] = -1.;
    if (NNFindRecurrences() != 0)
    {
        Mistakes++;
    }
    file_write("Log/TS09_EntropyAnalogues_test.txt", strf("4 checks; %d failures\n", Mistakes), 0);
}

var NNRecurrenceRate(var MaximumDistance)
{
    int T, J, Hits;
    var Distance, Difference, Current, Historic;
    if (MaximumDistance <= 0.)
    {
        return 0.;
    }
    Hits = 0;
    for (T = 16; T <= 140; T++)
    {
        Distance = 0.;
        for (J = 0; J < 3; J++)
        {
            Current = log(NNPrices[0] / NNPrices[NNHorizons[J]]) / NNHorizons[J];
            Historic = log(NNPrices[T] / NNPrices[T + NNHorizons[J]]) / NNHorizons[J];
            Difference = Current - Historic;
            Distance += Difference * Difference;
        }
        if (sqrt(Distance) < MaximumDistance)
        {
            Hits++;
        }
    }
    return Hits / 125.;
}
var NNForwardTargetDispersion()
{
    int T;
    var Y, MeanTarget, VarTarget;
    MeanTarget = 0.;
    VarTarget = 0.;
    for (T = 12; T <= 140; T++)
    {
        MeanTarget += log(NNPrices[T - 4] / NNPrices[T]) / 129.;
    }
    for (T = 12; T <= 140; T++)
    {
        Y = log(NNPrices[T - 4] / NNPrices[T]) - MeanTarget;
        VarTarget += Y * Y / 129.;
    }
    return sqrt(VarTarget);
}
var NNInformationComplement()
{
    return max(0., 1. - NNEntropy);
}
void NNResearchDiagnostics()
{
    printf(
        "\nAnalogues %d; recurrence rate %.6g; target dispersion %.6g; information complement %.6g",
        NNUsed, NNRecurrenceRate(0.001), NNForwardTargetDispersion(), NNInformationComplement());
}

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

void NNCase_quiet_cycle()
{
    int K, Original, Again, Scaled, Reflected;
    for (K = 0; K < 180; K++)
    {
        NNPrices[K] = 1.0 * exp(0.0005 * sin(0.17 * K));
    }
    Original = NNFindRecurrences();
    NNExpect(Original >= -1 && Original <= 1, "quiet_cycle: bounded direction");
    Again = NNFindRecurrences();
    NNExpect(Again == Original, "quiet_cycle: repeatable estimate");
    for (K = 0; K < 180; K++)
    {
        NNPrices[K] *= 7.;
    }
    Scaled = NNFindRecurrences();
    NNExpect(Scaled == Original, "quiet_cycle: price-unit invariance");
    for (K = 0; K < 180; K++)
    {
        NNPrices[K] = 1. / NNPrices[K];
    }
    Reflected = NNFindRecurrences();
    NNExpect(Reflected == -Original, "quiet_cycle: directional reflection");
    NNPrices[90] = 0.;
    NNExpect(NNFindRecurrences() == 0, "quiet_cycle: missing price rejected");
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

void NNExtendedTests()
{
    NNExtendedCount = 0;
    NNExtendedFailures = 0;
    file_write("Log/TS09_EntropyAnalogues_extended.txt",
               "TS09_EntropyAnalogues: extended properties\n", 0);
    NNCase_quiet_cycle();
    NNCase_mixed_cycles();
    NNCase_persistent_upward();
    NNCase_persistent_downward();
    NNCase_slow_curve();
    NNCase_rough_curve();
    NNCase_accelerating_path();
    NNCase_decelerating_path();
    NNCase_volatility_ramp();
    NNCase_volatility_decay();
    NNCase_cycle_with_drift();
    NNCase_near_zero_prices();
    NNCase_large_price_units();
    NNCase_high_frequency_chop();
    NNCase_long_wave_trend();
    NNCase_opposing_scales();
    NNCase_nonlinear_curve();
    NNCase_amplitude_modulation();
    NNCase_decaying_event();
    NNCase_time_varying_frequency();
    NNCase_slow_fast_cancellation();
    file_append("Log/TS09_EntropyAnalogues_extended.txt",
                strf("RESULT: %d checks; %d failures\n", NNExtendedCount, NNExtendedFailures));
}

function run()
{
#ifdef STX_SELFTEST
    if (is(INITRUN))
    {
        NNTestHistory();
        NNExtendedTests();
        quit("Analogue tests complete");
    }
    return;
#endif
    int T, AnalogueSide;
    var Limit, UnitRisk;
    BarPeriod = 60;
    LookBack = 181;
    StartDate = 20240101;
    EndDate = 20241231;
    Capital = 10000.;
    Hedge = 0;
    MaxLong = 1;
    MaxShort = 1;
    if (!asset("EUR/USD"))
    {
        return;
    }
    algo("NN09");
    if (is(EXITRUN))
    {
        NNResearchDiagnostics();
        return;
    }
    if (is(LOOKBACK))
    {
        return;
    }
    for (T = 0; T < 180; T++)
    {
        NNPrices[T] = priceClose(T + 1);
    }
    AnalogueSide = NNFindRecurrences();
    if (NNEntropy > 0.98)
    {
        exitLong();
        exitShort();
        return;
    }
    if (AnalogueSide > 0)
    {
        exitShort();
    }
    if (AnalogueSide < 0)
    {
        exitLong();
    }
    if (AnalogueSide == 0 || NumOpenLong + NumOpenShort > 0)
    {
        return;
    }
    if (Balance <= 0. || Equity < Balance * 0.85)
    {
        return;
    }
    if (LotAmount <= 0. || MarginCost <= 0. || PIP <= 0. || PIPCost <= 0.)
    {
        return;
    }
    Stop = 35. * PIP;
    TakeProfit = 70. * PIP;
    LifeTime = 4;
    UnitRisk = (Stop + Spread) / PIP * PIPCost + Commission * LotAmount / 10000.;
    if (UnitRisk <= 0.)
    {
        return;
    }
    Limit = min(Balance * 0.005 / UnitRisk, 2000. / LotAmount);
    Limit = min(Limit, max(0., Equity - MarginVal) * 0.8 / MarginCost);
    if (Limit < 1.)
    {
        return;
    }
    Lots = floor(Limit);
    Margin = 0.;
    Risk = 0.;
    if (AnalogueSide > 0)
    {
        enterLong();
    }
    else
    {
        enterShort();
    }
}