// 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();
}
}