// Independent model 10: Theil-Sen curve geometry, median/QValueMAD and quantile ranks.
#include <default.c>
var QPrice[120];
var QCurveHistory[65];
var QSorted[64];
var QDeviation[64];
var QRobustScore = 0.;
var QPercentile = 0.5;
int QTenors[6] = {1, 2, 4, 8, 16, 32};
void QInsertionSort(vars Values, int Length)
{
int At, Back;
var Saved;
for (At = 1; At < Length; At++)
{
Saved = Values[At];
Back = At - 1;
while (Back >= 0)
{
if (Values[Back] <= Saved)
{
break;
}
Values[Back + 1] = Values[Back];
Back--;
}
Values[Back + 1] = Saved;
}
}
var QTheilSenAt(int QValueOffset)
{
var Rates[6];
var PairSlope[15];
int Short, Long, Pair;
for (Short = 0; Short < 6; Short++)
{
Rates[Short] =
log(QPrice[QValueOffset] / QPrice[QValueOffset + QTenors[Short]]) / QTenors[Short];
}
Pair = 0;
for (Short = 0; Short < 5; Short++)
{
for (Long = Short + 1; Long < 6; Long++)
{
PairSlope[Pair] =
(Rates[Long] - Rates[Short]) / (log(QTenors[Long]) - log(QTenors[Short]));
Pair++;
}
}
QInsertionSort(PairSlope, 15);
return PairSlope[7];
}
int QEstimateShape()
{
int QValueOffset, QValueLess, QValueEqual;
var QValueCenter, QValueMAD, QValueInterquartile, QValueNow;
QRobustScore = 0.;
QPercentile = 0.5;
for (QValueOffset = 0; QValueOffset < 120; QValueOffset++)
{
if (!(QPrice[QValueOffset] > 0.))
{
return 0;
}
}
for (QValueOffset = 0; QValueOffset <= 64; QValueOffset++)
{
QCurveHistory[QValueOffset] = QTheilSenAt(QValueOffset);
}
for (QValueOffset = 0; QValueOffset < 64; QValueOffset++)
{
QSorted[QValueOffset] = QCurveHistory[QValueOffset + 1];
}
QInsertionSort(QSorted, 64);
QValueCenter = (QSorted[31] + QSorted[32]) / 2.;
QValueInterquartile = QSorted[48] - QSorted[16];
for (QValueOffset = 0; QValueOffset < 64; QValueOffset++)
{
QDeviation[QValueOffset] = abs(QSorted[QValueOffset] - QValueCenter);
}
QInsertionSort(QDeviation, 64);
QValueMAD = (QDeviation[31] + QDeviation[32]) / 2.;
if (QValueMAD < 1.e-15 || QValueInterquartile < 1.e-15)
{
return 0;
}
QValueNow = QCurveHistory[0];
QValueLess = 0;
QValueEqual = 0;
for (QValueOffset = 0; QValueOffset < 64; QValueOffset++)
{
if (QSorted[QValueOffset] < QValueNow)
{
QValueLess++;
}
if (QSorted[QValueOffset] == QValueNow)
{
QValueEqual++;
}
}
QPercentile = (QValueLess + QValueEqual * 0.5) / 64.;
QRobustScore = (QValueNow - QValueCenter) / (1.4826 * QValueMAD);
// High long-minus-short curve slope means near returns lag: fade the distortion.
// Require a turn back toward the historical center, not just an extreme level.
if (QPercentile > 0.9 && QRobustScore > 1.5 && QValueNow < QCurveHistory[1])
{
return 1;
}
if (QPercentile < 0.1 && QRobustScore < -1.5 && QValueNow > QCurveHistory[1])
{
return -1;
}
return 0;
}
void QCheckRobustness()
{
int I, Original, Reversed, Fail;
var Medians[5];
Fail = 0;
Medians[0] = 4.;
Medians[1] = 2.;
Medians[2] = 5.;
Medians[3] = 1.;
Medians[4] = 3.;
QInsertionSort(Medians, 5);
if (Medians[2] != 3.)
{
Fail++;
}
for (I = 0; I < 120; I++)
{
QPrice[I] = 1.;
}
if (QEstimateShape() != 0)
{
Fail++;
}
for (I = 0; I < 120; I++)
{
QPrice[I] = exp(0.003 * cos(0.23 * I) + 0.0007 * sin(0.87 * I));
}
Original = QEstimateShape();
for (I = 0; I < 120; I++)
{
QPrice[I] = 1. / QPrice[I];
}
Reversed = QEstimateShape();
if (Original != -Reversed)
{
Fail++;
}
QPrice[6] = 0.;
if (QEstimateShape() != 0)
{
Fail++;
}
file_write("Log/TS10_RobustQuantileCurve_test.txt", strf("4 checks; %d failures\n", Fail), 0);
}
var QHistoricalQuantile(var Probability)
{
var Index, Fraction;
int Lower;
if (Probability < 0. || Probability > 1.)
{
return 0.;
}
Index = 63. * Probability;
Lower = (int)floor(Index);
if (Lower >= 63)
{
return QSorted[63];
}
Fraction = Index - Lower;
return QSorted[Lower] * (1. - Fraction) + QSorted[Lower + 1] * Fraction;
}
var QBowleyAsymmetry()
{
var Low, Mid, High;
Low = QHistoricalQuantile(0.25);
Mid = QHistoricalQuantile(0.5);
High = QHistoricalQuantile(0.75);
if (High - Low < 1.e-20)
{
return 0.;
}
return (High + Low - 2. * Mid) / (High - Low);
}
var QInterdecileWidth()
{
return QHistoricalQuantile(0.9) - QHistoricalQuantile(0.1);
}
var QMedianSuccessiveDifference()
{
var Changes[64];
int At;
for (At = 0; At < 64; At++)
{
Changes[At] = abs(QCurveHistory[At] - QCurveHistory[At + 1]);
}
QInsertionSort(Changes, 64);
return (Changes[31] + Changes[32]) / 2.;
}
void QResearchDiagnostics()
{
printf("\nQuantile rank %.6g; Bowley asymmetry %.6g", QPercentile, QBowleyAsymmetry());
printf("\nQuantile interdecile width %.6g; median successive change %.6g", QInterdecileWidth(),
QMedianSuccessiveDifference());
}
// ============================================================================
// EXTENDED REGRESSION SUITE: deterministic properties over market regimes
// These functions run ONLY under STX_SELFTEST; they never place orders.
// ============================================================================
int QExtendedCount = 0;
int QExtendedFailures = 0;
void QExpect(int Passed, string Description)
{
QExtendedCount++;
if (!Passed)
{
QExtendedFailures++;
file_append("Log/TS10_RobustQuantileCurve_extended.txt", strf("FAIL: %s\n", Description));
}
}
void QCase_quiet_cycle()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp(0.0005 * sin(0.17 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "quiet_cycle: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "quiet_cycle: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "quiet_cycle: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "quiet_cycle: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "quiet_cycle: missing price rejected");
}
void QCase_mixed_cycles()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp(0.003 * sin(0.23 * K) + 0.0007 * cos(0.81 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "mixed_cycles: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "mixed_cycles: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "mixed_cycles: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "mixed_cycles: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "mixed_cycles: missing price rejected");
}
void QCase_persistent_upward()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp(-0.001 * K + 0.0001 * sin(0.53 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "persistent_upward: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "persistent_upward: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "persistent_upward: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "persistent_upward: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "persistent_upward: missing price rejected");
}
void QCase_persistent_downward()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp(0.001 * K + 0.0001 * cos(0.47 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "persistent_downward: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "persistent_downward: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "persistent_downward: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "persistent_downward: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "persistent_downward: missing price rejected");
}
void QCase_slow_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp(0.004 * cos(0.08 * K) + 0.0002 * sin(0.7 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "slow_curve: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "slow_curve: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "slow_curve: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "slow_curve: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "slow_curve: missing price rejected");
}
void QCase_rough_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp(0.001 * sin(1.7 * K) + 0.0005 * cos(2.3 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "rough_curve: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "rough_curve: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "rough_curve: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "rough_curve: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "rough_curve: missing price rejected");
}
void QCase_accelerating_path()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp(-0.0001 * K - 0.000002 * K * K + 0.0002 * sin(0.41 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "accelerating_path: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "accelerating_path: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "accelerating_path: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "accelerating_path: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "accelerating_path: missing price rejected");
}
void QCase_decelerating_path()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp(-0.0008 * K + 0.000002 * K * K + 0.0002 * cos(0.31 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "decelerating_path: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "decelerating_path: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "decelerating_path: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "decelerating_path: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "decelerating_path: missing price rejected");
}
void QCase_volatility_ramp()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp((0.0001 + K * 0.00001) * sin(0.37 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "volatility_ramp: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "volatility_ramp: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "volatility_ramp: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "volatility_ramp: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "volatility_ramp: missing price rejected");
}
void QCase_volatility_decay()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp(0.006 * exp(-0.02 * K) * cos(0.7 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "volatility_decay: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "volatility_decay: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "volatility_decay: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "volatility_decay: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "volatility_decay: missing price rejected");
}
void QCase_cycle_with_drift()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp(-0.0002 * K + 0.003 * sin(0.19 * K) + 0.0005 * cos(0.61 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "cycle_with_drift: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "cycle_with_drift: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "cycle_with_drift: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "cycle_with_drift: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "cycle_with_drift: missing price rejected");
}
void QCase_near_zero_prices()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 0.0001 * exp(0.002 * sin(0.29 * K) + 0.001 * cos(0.13 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "near_zero_prices: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "near_zero_prices: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "near_zero_prices: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "near_zero_prices: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "near_zero_prices: missing price rejected");
}
void QCase_large_price_units()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 10000.0 * exp(0.003 * sin(0.11 * K) + 0.0008 * cos(0.43 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "large_price_units: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "large_price_units: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "large_price_units: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "large_price_units: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "large_price_units: missing price rejected");
}
void QCase_high_frequency_chop()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp(0.0008 * cos(2.8 * K) + 0.001 * sin(0.23 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "high_frequency_chop: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "high_frequency_chop: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "high_frequency_chop: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "high_frequency_chop: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "high_frequency_chop: missing price rejected");
}
void QCase_long_wave_trend()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp(-0.0005 * K + 0.002 * cos(0.07 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "long_wave_trend: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "long_wave_trend: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "long_wave_trend: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "long_wave_trend: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "long_wave_trend: missing price rejected");
}
void QCase_opposing_scales()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp(0.005 * sin(0.09 * K) - 0.002 * sin(0.59 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "opposing_scales: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "opposing_scales: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "opposing_scales: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "opposing_scales: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "opposing_scales: missing price rejected");
}
void QCase_nonlinear_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp(0.002 * sin(0.021 * K * K) + 0.001 * cos(0.33 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "nonlinear_curve: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "nonlinear_curve: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "nonlinear_curve: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "nonlinear_curve: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "nonlinear_curve: missing price rejected");
}
void QCase_amplitude_modulation()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp(0.002 * sin(0.3 * K) * cos(0.04 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "amplitude_modulation: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "amplitude_modulation: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "amplitude_modulation: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "amplitude_modulation: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "amplitude_modulation: missing price rejected");
}
void QCase_decaying_event()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp(0.01 * exp(-0.2 * K) + 0.0003 * sin(0.71 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "decaying_event: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "decaying_event: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "decaying_event: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "decaying_event: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "decaying_event: missing price rejected");
}
void QCase_time_varying_frequency()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp(0.002 * cos(0.004 * K * K + 0.11 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "time_varying_frequency: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "time_varying_frequency: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "time_varying_frequency: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "time_varying_frequency: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "time_varying_frequency: missing price rejected");
}
void QCase_slow_fast_cancellation()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 120; K++)
{
QPrice[K] = 1.0 * exp(0.004 * sin(0.07 * K) + 0.0013 * cos(0.97 * K));
}
Original = QEstimateShape();
QExpect(Original >= -1 && Original <= 1, "slow_fast_cancellation: bounded direction");
Again = QEstimateShape();
QExpect(Again == Original, "slow_fast_cancellation: repeatable estimate");
for (K = 0; K < 120; K++)
{
QPrice[K] *= 7.;
}
Scaled = QEstimateShape();
QExpect(Scaled == Original, "slow_fast_cancellation: price-unit invariance");
for (K = 0; K < 120; K++)
{
QPrice[K] = 1. / QPrice[K];
}
Reflected = QEstimateShape();
QExpect(Reflected == -Original, "slow_fast_cancellation: directional reflection");
QPrice[60] = 0.;
QExpect(QEstimateShape() == 0, "slow_fast_cancellation: missing price rejected");
}
void QExtendedTests()
{
QExtendedCount = 0;
QExtendedFailures = 0;
file_write("Log/TS10_RobustQuantileCurve_extended.txt",
"TS10_RobustQuantileCurve: extended properties\n", 0);
QCase_quiet_cycle();
QCase_mixed_cycles();
QCase_persistent_upward();
QCase_persistent_downward();
QCase_slow_curve();
QCase_rough_curve();
QCase_accelerating_path();
QCase_decelerating_path();
QCase_volatility_ramp();
QCase_volatility_decay();
QCase_cycle_with_drift();
QCase_near_zero_prices();
QCase_large_price_units();
QCase_high_frequency_chop();
QCase_long_wave_trend();
QCase_opposing_scales();
QCase_nonlinear_curve();
QCase_amplitude_modulation();
QCase_decaying_event();
QCase_time_varying_frequency();
QCase_slow_fast_cancellation();
file_append("Log/TS10_RobustQuantileCurve_extended.txt",
strf("RESULT: %d checks; %d failures\n", QExtendedCount, QExtendedFailures));
}
function run()
{
#ifdef STX_SELFTEST
if (is(INITRUN))
{
QCheckRobustness();
QExtendedTests();
quit("Quantile tests complete");
}
return;
#endif
int I, Distortion;
var AmountAllowed, CashLoss;
BarPeriod = 60;
LookBack = 121;
Capital = 10000.;
StartDate = 20240101;
EndDate = 20241231;
Hedge = 0;
MaxLong = 1;
MaxShort = 1;
if (!asset("EUR/USD"))
{
return;
}
algo("Q10");
if (is(EXITRUN))
{
QResearchDiagnostics();
return;
}
if (is(LOOKBACK))
{
return;
}
for (I = 0; I < 120; I++)
{
QPrice[I] = priceClose(I + 1);
}
Distortion = QEstimateShape();
if (abs(QRobustScore) < 0.3 || (QPercentile > 0.4 && QPercentile < 0.6))
{
exitLong();
exitShort();
}
if (Distortion == 0 || NumOpenLong + NumOpenShort > 0)
{
return;
}
if (Balance <= 0. || Equity < Balance * 0.85)
{
return;
}
if (LotAmount <= 0. || PIPCost <= 0. || MarginCost <= 0. || PIP <= 0.)
{
return;
}
Stop = 35. * PIP;
TakeProfit = 60. * PIP;
LifeTime = 12;
CashLoss = (Stop + Spread) * PIPCost / PIP + Commission * LotAmount / 10000.;
if (CashLoss <= 0.)
{
return;
}
AmountAllowed = min(Balance * 0.005 / CashLoss, 2000. / LotAmount);
AmountAllowed = min(AmountAllowed, max(0., Equity - MarginVal) * 0.8 / MarginCost);
if (AmountAllowed < 1.)
{
return;
}
Lots = floor(AmountAllowed);
Margin = 0.;
Risk = 0.;
if (Distortion > 0)
{
enterLong();
}
else
{
enterShort();
}
}