// Independent model 02: equilibrium spread, AR(1) dynamics and OU half-life.
#include <default.c>
var OUSample[121];
var OUPrice[160];
var OUZ = 0.;
var OUHalfLife = 0.;
var OUPersistence = 0.;
int OUFitSpread()
{
int T;
var MeanX, MeanY, SumXX, SumXY, Intercept, Innovation, Noise, Current;
OUZ = 0.;
OUHalfLife = 0.;
OUPersistence = 1.;
for (T = 0; T < 160; T++)
{
if (!(OUPrice[T] > 0.))
{
return 0;
}
}
// Contrast a near-horizon average return with a distant age-bucket rate.
for (T = 0; T <= 120; T++)
{
OUSample[T] =
log(OUPrice[T] / OUPrice[T + 2]) / 2. - log(OUPrice[T + 8] / OUPrice[T + 16]) / 8.;
}
MeanX = 0.;
MeanY = 0.;
for (T = 2; T <= 120; T++)
{
MeanX += OUSample[T] / 119.;
MeanY += OUSample[T - 1] / 119.;
}
SumXX = 0.;
SumXY = 0.;
for (T = 2; T <= 120; T++)
{
SumXX += (OUSample[T] - MeanX) * (OUSample[T] - MeanX);
SumXY += (OUSample[T] - MeanX) * (OUSample[T - 1] - MeanY);
}
if (SumXX < 1.e-20)
{
return 0;
}
OUPersistence = SumXY / SumXX;
if (OUPersistence <= 0.05 || OUPersistence >= 0.98)
{
return 0;
}
Intercept = MeanY - OUPersistence * MeanX;
Noise = 0.;
for (T = 2; T <= 120; T++)
{
Innovation = OUSample[T - 1] - Intercept - OUPersistence * OUSample[T];
Noise += Innovation * Innovation / 119.;
}
if (Noise < 1.e-22)
{
return 0;
}
OUHalfLife = -log(2.) / log(OUPersistence);
Current = OUSample[0] - Intercept / (1. - OUPersistence);
OUZ = Current / sqrt(Noise / (1. - OUPersistence * OUPersistence));
if (OUHalfLife < 1. || OUHalfLife > 24. || abs(OUZ) < 1.75)
{
return 0;
}
if (OUZ < 0.)
{
return 1;
}
return -1;
}
void OUChecks()
{
int I, A, B, Errors;
Errors = 0;
for (I = 0; I < 160; I++)
{
OUPrice[I] = 1.;
}
if (OUFitSpread() != 0)
{
Errors++;
}
for (I = 0; I < 160; I++)
{
OUPrice[I] = exp(0.004 * sin(0.28 * I) + 0.001 * sin(1.71 * I));
}
A = OUFitSpread();
if (A < -1 || A > 1)
{
Errors++;
}
for (I = 0; I < 160; I++)
{
OUPrice[I] = 1. / OUPrice[I];
}
B = OUFitSpread();
if (B != -A)
{
Errors++;
}
OUPrice[3] = -1.;
if (OUFitSpread() != 0)
{
Errors++;
}
file_write("Log/TS02_OUHorizonSpread_test.txt", strf("4 checks; %d failures\n", Errors), 0);
}
var OUForecastAt(int Steps)
{
int T;
var Average;
if (Steps < 1 || OUPersistence <= 0. || OUPersistence >= 1.)
{
return 0.;
}
Average = 0.;
for (T = 1; T <= 120; T++)
{
Average += OUSample[T] / 120.;
}
return Average + pow(OUPersistence, Steps) * (OUSample[0] - Average);
}
var OUInnovationAutocorrelation()
{
int T;
var Previous, Current, Numerator, Denominator;
Numerator = 0.;
Denominator = 0.;
Previous = 0.;
for (T = 119; T >= 1; T--)
{
Current = OUSample[T] - OUPersistence * OUSample[T + 1];
Numerator += Current * Previous;
Denominator += Current * Current;
Previous = Current;
}
if (Denominator < 1.e-24)
{
return 0.;
}
return Numerator / Denominator;
}
var OULevelDispersion()
{
int T;
var Average, SpreadVariance;
Average = 0.;
SpreadVariance = 0.;
for (T = 1; T <= 120; T++)
{
Average += OUSample[T] / 120.;
}
for (T = 1; T <= 120; T++)
{
SpreadVariance += pow(OUSample[T] - Average, 2.) / 120.;
}
return sqrt(SpreadVariance);
}
void OUResearchDiagnostics()
{
printf("\nOU half-life %.6g; forecast4 %.6g; innovation correlation %.6g; spread scale %.6g",
OUHalfLife, OUForecastAt(4), OUInnovationAutocorrelation(), OULevelDispersion());
}
// ============================================================================
// EXTENDED REGRESSION SUITE: deterministic properties over market regimes
// These functions run ONLY under STX_SELFTEST; they never place orders.
// ============================================================================
int OUExtendedCount = 0;
int OUExtendedFailures = 0;
void OUExpect(int Passed, string Description)
{
OUExtendedCount++;
if (!Passed)
{
OUExtendedFailures++;
file_append("Log/TS02_OUHorizonSpread_extended.txt", strf("FAIL: %s\n", Description));
}
}
void OUCase_quiet_cycle()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.0005 * sin(0.17 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "quiet_cycle: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "quiet_cycle: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "quiet_cycle: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "quiet_cycle: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "quiet_cycle: missing price rejected");
}
void OUCase_mixed_cycles()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.003 * sin(0.23 * K) + 0.0007 * cos(0.81 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "mixed_cycles: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "mixed_cycles: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "mixed_cycles: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "mixed_cycles: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "mixed_cycles: missing price rejected");
}
void OUCase_persistent_upward()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(-0.001 * K + 0.0001 * sin(0.53 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "persistent_upward: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "persistent_upward: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "persistent_upward: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "persistent_upward: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "persistent_upward: missing price rejected");
}
void OUCase_persistent_downward()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.001 * K + 0.0001 * cos(0.47 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "persistent_downward: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "persistent_downward: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "persistent_downward: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "persistent_downward: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "persistent_downward: missing price rejected");
}
void OUCase_slow_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.004 * cos(0.08 * K) + 0.0002 * sin(0.7 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "slow_curve: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "slow_curve: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "slow_curve: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "slow_curve: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "slow_curve: missing price rejected");
}
void OUCase_rough_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.001 * sin(1.7 * K) + 0.0005 * cos(2.3 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "rough_curve: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "rough_curve: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "rough_curve: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "rough_curve: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "rough_curve: missing price rejected");
}
void OUCase_accelerating_path()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(-0.0001 * K - 0.000002 * K * K + 0.0002 * sin(0.41 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "accelerating_path: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "accelerating_path: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "accelerating_path: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "accelerating_path: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "accelerating_path: missing price rejected");
}
void OUCase_decelerating_path()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(-0.0008 * K + 0.000002 * K * K + 0.0002 * cos(0.31 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "decelerating_path: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "decelerating_path: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "decelerating_path: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "decelerating_path: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "decelerating_path: missing price rejected");
}
void OUCase_volatility_ramp()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp((0.0001 + K * 0.00001) * sin(0.37 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "volatility_ramp: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "volatility_ramp: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "volatility_ramp: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "volatility_ramp: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "volatility_ramp: missing price rejected");
}
void OUCase_volatility_decay()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.006 * exp(-0.02 * K) * cos(0.7 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "volatility_decay: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "volatility_decay: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "volatility_decay: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "volatility_decay: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "volatility_decay: missing price rejected");
}
void OUCase_cycle_with_drift()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(-0.0002 * K + 0.003 * sin(0.19 * K) + 0.0005 * cos(0.61 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "cycle_with_drift: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "cycle_with_drift: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "cycle_with_drift: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "cycle_with_drift: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "cycle_with_drift: missing price rejected");
}
void OUCase_near_zero_prices()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 0.0001 * exp(0.002 * sin(0.29 * K) + 0.001 * cos(0.13 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "near_zero_prices: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "near_zero_prices: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "near_zero_prices: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "near_zero_prices: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "near_zero_prices: missing price rejected");
}
void OUCase_large_price_units()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 10000.0 * exp(0.003 * sin(0.11 * K) + 0.0008 * cos(0.43 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "large_price_units: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "large_price_units: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "large_price_units: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "large_price_units: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "large_price_units: missing price rejected");
}
void OUCase_high_frequency_chop()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.0008 * cos(2.8 * K) + 0.001 * sin(0.23 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "high_frequency_chop: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "high_frequency_chop: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "high_frequency_chop: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "high_frequency_chop: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "high_frequency_chop: missing price rejected");
}
void OUCase_long_wave_trend()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(-0.0005 * K + 0.002 * cos(0.07 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "long_wave_trend: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "long_wave_trend: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "long_wave_trend: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "long_wave_trend: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "long_wave_trend: missing price rejected");
}
void OUCase_opposing_scales()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.005 * sin(0.09 * K) - 0.002 * sin(0.59 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "opposing_scales: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "opposing_scales: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "opposing_scales: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "opposing_scales: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "opposing_scales: missing price rejected");
}
void OUCase_nonlinear_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.002 * sin(0.021 * K * K) + 0.001 * cos(0.33 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "nonlinear_curve: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "nonlinear_curve: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "nonlinear_curve: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "nonlinear_curve: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "nonlinear_curve: missing price rejected");
}
void OUCase_amplitude_modulation()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.002 * sin(0.3 * K) * cos(0.04 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "amplitude_modulation: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "amplitude_modulation: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "amplitude_modulation: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "amplitude_modulation: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "amplitude_modulation: missing price rejected");
}
void OUCase_decaying_event()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.01 * exp(-0.2 * K) + 0.0003 * sin(0.71 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "decaying_event: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "decaying_event: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "decaying_event: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "decaying_event: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "decaying_event: missing price rejected");
}
void OUCase_time_varying_frequency()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.002 * cos(0.004 * K * K + 0.11 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "time_varying_frequency: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "time_varying_frequency: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "time_varying_frequency: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "time_varying_frequency: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "time_varying_frequency: missing price rejected");
}
void OUCase_slow_fast_cancellation()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.004 * sin(0.07 * K) + 0.0013 * cos(0.97 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "slow_fast_cancellation: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "slow_fast_cancellation: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "slow_fast_cancellation: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "slow_fast_cancellation: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "slow_fast_cancellation: missing price rejected");
}
void OUExtendedTests()
{
OUExtendedCount = 0;
OUExtendedFailures = 0;
file_write("Log/TS02_OUHorizonSpread_extended.txt",
"TS02_OUHorizonSpread: extended properties\n", 0);
OUCase_quiet_cycle();
OUCase_mixed_cycles();
OUCase_persistent_upward();
OUCase_persistent_downward();
OUCase_slow_curve();
OUCase_rough_curve();
OUCase_accelerating_path();
OUCase_decelerating_path();
OUCase_volatility_ramp();
OUCase_volatility_decay();
OUCase_cycle_with_drift();
OUCase_near_zero_prices();
OUCase_large_price_units();
OUCase_high_frequency_chop();
OUCase_long_wave_trend();
OUCase_opposing_scales();
OUCase_nonlinear_curve();
OUCase_amplitude_modulation();
OUCase_decaying_event();
OUCase_time_varying_frequency();
OUCase_slow_fast_cancellation();
file_append("Log/TS02_OUHorizonSpread_extended.txt",
strf("RESULT: %d checks; %d failures\n", OUExtendedCount, OUExtendedFailures));
}
function run()
{
#ifdef STX_SELFTEST
if (is(INITRUN))
{
OUChecks();
OUExtendedTests();
quit("OU tests complete");
}
return;
#endif
int I, OrderSide;
var Exposure, LossUnit;
BarPeriod = 60;
StartDate = 20240101;
EndDate = 20241231;
LookBack = 161;
Capital = 10000.;
MaxLong = 1;
MaxShort = 1;
Hedge = 0;
asset("EUR/USD");
algo("OU02");
if (is(EXITRUN))
{
OUResearchDiagnostics();
return;
}
if (is(LOOKBACK))
{
return;
}
for (I = 0; I < 160; I++)
{
OUPrice[I] = priceClose(I + 1);
}
OrderSide = OUFitSpread();
// Equilibrium arrival or loss of mean-reverting dynamics closes the trade.
if (abs(OUZ) < 0.25 || OUPersistence >= 0.98 || OUPersistence <= 0.)
{
exitLong();
exitShort();
}
if (OrderSide == 0 || NumOpenLong + NumOpenShort > 0)
{
return;
}
if (Equity <= Balance * 0.85 || Balance <= 0.)
{
return;
}
if (LotAmount <= 0. || PIPCost <= 0. || PIP <= 0. || MarginCost <= 0.)
{
return;
}
Stop = PIP * 35.;
TakeProfit = PIP * 50.;
LifeTime = max(2., min(48., OUHalfLife * 3.));
LossUnit = PIPCost * (Stop + Spread) / PIP + Commission * LotAmount / 10000.;
if (LossUnit <= 0.)
{
return;
}
Exposure = min(2000. / LotAmount, Balance * 0.005 / LossUnit);
Exposure = min(Exposure, max(0., Equity - MarginVal) * 0.8 / MarginCost);
if (Exposure < 1.)
{
return;
}
Lots = floor(Exposure);
Risk = 0.;
Margin = 0.;
if (OrderSide == 1)
{
enterLong();
}
else
{
enterShort();
}
}