|
2 registered members (TipmyPip, JeyKey II),
13,184
guests, and 2
spiders. |
|
Key:
Admin,
Global Mod,
Mod
|
|
|
Hidden Markov Curve
[Re: TipmyPip]
#489731
1 hour ago
1 hour ago
|
Joined: Sep 2017
Posts: 351
TipmyPip
OP
Senior Member
|
OP
Senior Member
Joined: Sep 2017
Posts: 351
|
Hidden Markov Curve — The Three Weather Systems Picture the market as moving among three weather systems: an upward drifting environment, a downward drifting environment, and a quieter neutral environment. The weather cannot be observed directly. Instead, the program sees short and longer horizon returns, much as a forecaster sees temperature and wind readings while trying to infer the larger atmospheric state. Each weather system has an assumed pattern of observations and an assumed tendency to persist. The program stores transition preferences describing how likely a state is to remain in place or move to another state. It also stores observation profiles describing the return behavior expected under each environment. These settings are fixed priors and emission assumptions, not a fully fitted hidden Markov model. An older calibration period supplies a return scale. The program then replays a more recent sequence from old observations toward the newest completed bar. At each step, it first predicts the distribution of possible weather systems using the transition preferences. It next compares the observed return pair with the observation profile of every state. The resulting state weights are normalized into a new posterior distribution. A surprising observation may weaken the previously favored state, but the persistence assumptions prevent every small fluctuation from instantly replacing the entire weather forecast. The process combines continuity with new evidence rather than treating either as sufficient alone. A buying decision requires a strong posterior preference for the upward environment. A strong preference for the downward environment supports selling. If the neutral environment dominates, or if the evidence remains divided, the program avoids opening a directional position. Existing positions can be closed when support for their corresponding directional state falls below a lower retention threshold. Model diagnostics report posterior uncertainty, a projected future probability of the upward state, and uncertainty associated with possible transitions. These measures describe the internal state distribution. They can help distinguish a confident directional classification from a market that is moving ambiguously among competing interpretations. Price stops, profit targets, limited position size, and a maximum lifetime remain active because a confident classification is not equivalent to a reliable profit forecast. The short and longer return observations overlap, so the simplified emission treatment does not represent every statistical dependency. The weather analogy must therefore remain conditional. The market may develop environments that the three predefined states do not represent. Reported posterior weights are probabilities inside the assumed model, not independently established real world probabilities. Calibration, alternative state definitions, transaction costs, and genuinely unseen historical periods must be examined before these classifications can support strong practical conclusions. // Independent model 08: three-state Bayesian forward filter on return horizons.
#include <default.c>
var HMPrice[110];
var HMProbability[3];
var HMTransition[9] = {0.94, 0.03, 0.03, 0.03, 0.94, 0.03, 0.03, 0.03, 0.94};
var HMMean[3] = {1., -1., 0.};
var HMVariance[3] = {0.75, 0.75, 0.2};
int HMForwardInference()
{
int T, State, From;
var Sigma, R, LongObservation, ShortObservation, Predicted, Error;
var Evidence, Total;
var Updated[3];
for (State = 0; State < 3; State++)
{
HMProbability[State] = 1. / 3.;
}
for (T = 0; T < 110; T++)
{
if (!(HMPrice[T] > 0.))
{
return 0;
}
}
Sigma = 0.;
for (T = 81; T <= 100; T++)
{
R = log(HMPrice[T] / HMPrice[T + 1]);
Sigma += R * R / 20.;
}
if (Sigma < 1.e-20)
{
return 0;
}
Sigma = sqrt(Sigma);
// Fixed emissions and transition priors; no fitted HMM or hindsight labels.
for (T = 80; T >= 0; T--)
{
ShortObservation = log(HMPrice[T] / HMPrice[T + 1]) / Sigma;
LongObservation = log(HMPrice[T] / HMPrice[T + 4]) / (4. * Sigma);
Total = 0.;
for (State = 0; State < 3; State++)
{
Predicted = 0.;
for (From = 0; From < 3; From++)
{
Predicted += HMProbability[From] * HMTransition[From * 3 + State];
}
Error = (ShortObservation - HMMean[State]) * (ShortObservation - HMMean[State]);
Error += (LongObservation - HMMean[State]) * (LongObservation - HMMean[State]);
Evidence = exp(max(-80., -Error / (2. * HMVariance[State]))) / HMVariance[State];
Updated[State] = Predicted * Evidence;
Total += Updated[State];
}
if (Total <= 1.e-100)
{
return 0;
}
for (State = 0; State < 3; State++)
{
HMProbability[State] = Updated[State] / Total;
}
}
if (HMProbability[0] > 0.85)
{
return 1;
}
if (HMProbability[1] > 0.85)
{
return -1;
}
return 0;
}
void HMCheckPosterior()
{
int T, Problems;
var Sum;
Problems = 0;
for (T = 0; T < 110; T++)
{
HMPrice[T] = 1.;
}
if (HMForwardInference() != 0)
{
Problems++;
}
for (T = 0; T < 110; T++)
{
HMPrice[T] = exp(-0.001 * T);
}
if (HMForwardInference() != 1)
{
Problems++;
}
Sum = HMProbability[0] + HMProbability[1] + HMProbability[2];
if (abs(Sum - 1.) > 1.e-6)
{
Problems++;
}
for (T = 0; T < 110; T++)
{
HMPrice[T] = 1. / HMPrice[T];
}
if (HMForwardInference() != -1)
{
Problems++;
}
file_write("Log/TS08_HiddenMarkovCurve_test.txt", strf("4 checks; %d failures\n", Problems), 0);
}
var HMPosteriorEntropy()
{
int State;
var H;
H = 0.;
for (State = 0; State < 3; State++)
{
if (HMProbability[State] > 1.e-20)
{
H -= HMProbability[State] * log(HMProbability[State]) / log(3.);
}
}
return H;
}
var HMFutureBullProbability(int Steps)
{
int Step, From, To;
var P[3];
var Next[3];
if (Steps < 0 || Steps > 64)
{
return 0.;
}
for (From = 0; From < 3; From++)
{
P[From] = HMProbability[From];
}
for (Step = 0; Step < Steps; Step++)
{
for (To = 0; To < 3; To++)
{
Next[To] = 0.;
for (From = 0; From < 3; From++)
{
Next[To] += P[From] * HMTransition[From * 3 + To];
}
}
for (To = 0; To < 3; To++)
{
P[To] = Next[To];
}
}
return P[0];
}
var HMTransitionUncertainty()
{
int S, T;
var Result, P;
Result = 0.;
for (S = 0; S < 3; S++)
{
for (T = 0; T < 3; T++)
{
P = HMTransition[S * 3 + T];
if (P > 0.)
{
Result -= HMProbability[S] * P * log(P);
}
}
}
return Result;
}
void HMResearchDiagnostics()
{
printf("\nHMM bull %.6g; bear %.6g; neutral %.6g", HMProbability[0], HMProbability[1],
HMProbability[2]);
printf("\nHMM entropy %.6g; bull4 %.6g", HMPosteriorEntropy(), HMFutureBullProbability(4));
printf("\nHMM transition uncertainty %.6g", HMTransitionUncertainty());
}
// ============================================================================
// EXTENDED REGRESSION SUITE: deterministic properties over market regimes
// These functions run ONLY under STX_SELFTEST; they never place orders.
// ============================================================================
int HMExtendedCount = 0;
int HMExtendedFailures = 0;
void HMExpect(int Passed, string Description)
{
HMExtendedCount++;
if (!Passed)
{
HMExtendedFailures++;
file_append("Log/TS08_HiddenMarkovCurve_extended.txt", strf("FAIL: %s\n", Description));
}
}
void HMCase_quiet_cycle()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp(0.0005 * sin(0.17 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "quiet_cycle: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "quiet_cycle: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "quiet_cycle: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "quiet_cycle: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "quiet_cycle: missing price rejected");
}
void HMCase_mixed_cycles()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp(0.003 * sin(0.23 * K) + 0.0007 * cos(0.81 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "mixed_cycles: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "mixed_cycles: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "mixed_cycles: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "mixed_cycles: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "mixed_cycles: missing price rejected");
}
void HMCase_persistent_upward()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp(-0.001 * K + 0.0001 * sin(0.53 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "persistent_upward: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "persistent_upward: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "persistent_upward: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "persistent_upward: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "persistent_upward: missing price rejected");
}
void HMCase_persistent_downward()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp(0.001 * K + 0.0001 * cos(0.47 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "persistent_downward: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "persistent_downward: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "persistent_downward: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "persistent_downward: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "persistent_downward: missing price rejected");
}
void HMCase_slow_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp(0.004 * cos(0.08 * K) + 0.0002 * sin(0.7 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "slow_curve: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "slow_curve: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "slow_curve: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "slow_curve: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "slow_curve: missing price rejected");
}
void HMCase_rough_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp(0.001 * sin(1.7 * K) + 0.0005 * cos(2.3 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "rough_curve: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "rough_curve: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "rough_curve: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "rough_curve: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "rough_curve: missing price rejected");
}
void HMCase_accelerating_path()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp(-0.0001 * K - 0.000002 * K * K + 0.0002 * sin(0.41 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "accelerating_path: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "accelerating_path: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "accelerating_path: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "accelerating_path: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "accelerating_path: missing price rejected");
}
void HMCase_decelerating_path()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp(-0.0008 * K + 0.000002 * K * K + 0.0002 * cos(0.31 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "decelerating_path: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "decelerating_path: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "decelerating_path: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "decelerating_path: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "decelerating_path: missing price rejected");
}
void HMCase_volatility_ramp()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp((0.0001 + K * 0.00001) * sin(0.37 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "volatility_ramp: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "volatility_ramp: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "volatility_ramp: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "volatility_ramp: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "volatility_ramp: missing price rejected");
}
void HMCase_volatility_decay()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp(0.006 * exp(-0.02 * K) * cos(0.7 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "volatility_decay: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "volatility_decay: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "volatility_decay: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "volatility_decay: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "volatility_decay: missing price rejected");
}
void HMCase_cycle_with_drift()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp(-0.0002 * K + 0.003 * sin(0.19 * K) + 0.0005 * cos(0.61 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "cycle_with_drift: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "cycle_with_drift: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "cycle_with_drift: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "cycle_with_drift: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "cycle_with_drift: missing price rejected");
}
void HMCase_near_zero_prices()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 0.0001 * exp(0.002 * sin(0.29 * K) + 0.001 * cos(0.13 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "near_zero_prices: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "near_zero_prices: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "near_zero_prices: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "near_zero_prices: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "near_zero_prices: missing price rejected");
}
void HMCase_large_price_units()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 10000.0 * exp(0.003 * sin(0.11 * K) + 0.0008 * cos(0.43 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "large_price_units: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "large_price_units: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "large_price_units: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "large_price_units: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "large_price_units: missing price rejected");
}
void HMCase_high_frequency_chop()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp(0.0008 * cos(2.8 * K) + 0.001 * sin(0.23 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "high_frequency_chop: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "high_frequency_chop: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "high_frequency_chop: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "high_frequency_chop: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "high_frequency_chop: missing price rejected");
}
void HMCase_long_wave_trend()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp(-0.0005 * K + 0.002 * cos(0.07 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "long_wave_trend: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "long_wave_trend: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "long_wave_trend: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "long_wave_trend: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "long_wave_trend: missing price rejected");
}
void HMCase_opposing_scales()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp(0.005 * sin(0.09 * K) - 0.002 * sin(0.59 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "opposing_scales: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "opposing_scales: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "opposing_scales: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "opposing_scales: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "opposing_scales: missing price rejected");
}
void HMCase_nonlinear_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp(0.002 * sin(0.021 * K * K) + 0.001 * cos(0.33 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "nonlinear_curve: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "nonlinear_curve: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "nonlinear_curve: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "nonlinear_curve: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "nonlinear_curve: missing price rejected");
}
void HMCase_amplitude_modulation()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp(0.002 * sin(0.3 * K) * cos(0.04 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "amplitude_modulation: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "amplitude_modulation: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "amplitude_modulation: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "amplitude_modulation: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "amplitude_modulation: missing price rejected");
}
void HMCase_decaying_event()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp(0.01 * exp(-0.2 * K) + 0.0003 * sin(0.71 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "decaying_event: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "decaying_event: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "decaying_event: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "decaying_event: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "decaying_event: missing price rejected");
}
void HMCase_time_varying_frequency()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp(0.002 * cos(0.004 * K * K + 0.11 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "time_varying_frequency: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "time_varying_frequency: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "time_varying_frequency: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "time_varying_frequency: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "time_varying_frequency: missing price rejected");
}
void HMCase_slow_fast_cancellation()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1.0 * exp(0.004 * sin(0.07 * K) + 0.0013 * cos(0.97 * K));
}
Original = HMForwardInference();
HMExpect(Original >= -1 && Original <= 1, "slow_fast_cancellation: bounded direction");
Again = HMForwardInference();
HMExpect(Again == Original, "slow_fast_cancellation: repeatable estimate");
for (K = 0; K < 110; K++)
{
HMPrice[K] *= 7.;
}
Scaled = HMForwardInference();
HMExpect(Scaled == Original, "slow_fast_cancellation: price-unit invariance");
for (K = 0; K < 110; K++)
{
HMPrice[K] = 1. / HMPrice[K];
}
Reflected = HMForwardInference();
HMExpect(Reflected == -Original, "slow_fast_cancellation: directional reflection");
HMPrice[55] = 0.;
HMExpect(HMForwardInference() == 0, "slow_fast_cancellation: missing price rejected");
}
void HMExtendedTests()
{
HMExtendedCount = 0;
HMExtendedFailures = 0;
file_write("Log/TS08_HiddenMarkovCurve_extended.txt",
"TS08_HiddenMarkovCurve: extended properties\n", 0);
HMCase_quiet_cycle();
HMCase_mixed_cycles();
HMCase_persistent_upward();
HMCase_persistent_downward();
HMCase_slow_curve();
HMCase_rough_curve();
HMCase_accelerating_path();
HMCase_decelerating_path();
HMCase_volatility_ramp();
HMCase_volatility_decay();
HMCase_cycle_with_drift();
HMCase_near_zero_prices();
HMCase_large_price_units();
HMCase_high_frequency_chop();
HMCase_long_wave_trend();
HMCase_opposing_scales();
HMCase_nonlinear_curve();
HMCase_amplitude_modulation();
HMCase_decaying_event();
HMCase_time_varying_frequency();
HMCase_slow_fast_cancellation();
file_append("Log/TS08_HiddenMarkovCurve_extended.txt",
strf("RESULT: %d checks; %d failures\n", HMExtendedCount, HMExtendedFailures));
}
function run()
{
#ifdef STX_SELFTEST
if (is(INITRUN))
{
HMCheckPosterior();
HMExtendedTests();
quit("HMM tests complete");
}
return;
#endif
int T, Regime;
var Units, AtRisk;
BarPeriod = 60;
LookBack = 111;
Capital = 10000.;
StartDate = 20240101;
EndDate = 20241231;
MaxLong = 1;
MaxShort = 1;
Hedge = 0;
if (!asset("EUR/USD"))
{
return;
}
algo("HM08");
if (is(EXITRUN))
{
HMResearchDiagnostics();
return;
}
if (is(LOOKBACK))
{
return;
}
for (T = 0; T < 110; T++)
{
HMPrice[T] = priceClose(T + 1);
}
Regime = HMForwardInference();
if (NumOpenLong > 0 && HMProbability[0] < 0.45)
{
exitLong();
}
if (NumOpenShort > 0 && HMProbability[1] < 0.45)
{
exitShort();
}
if (Regime == 0 || NumOpenLong + NumOpenShort > 0)
{
return;
}
if (Balance <= 0. || Equity <= Balance * 0.85)
{
return;
}
if (MarginCost <= 0. || PIP <= 0. || PIPCost <= 0. || LotAmount <= 0.)
{
return;
}
Stop = 50. * PIP;
TakeProfit = 125. * PIP;
LifeTime = 32;
AtRisk = PIPCost * (Stop + Spread) / PIP + Commission * LotAmount / 10000.;
if (AtRisk <= 0.)
{
return;
}
Units = min(2000. / LotAmount, Balance * 0.005 / AtRisk);
Units = min(Units, max(0., Equity - MarginVal) * 0.8 / MarginCost);
if (Units < 1.)
{
return;
}
Lots = floor(Units);
Margin = 0.;
Risk = 0.;
if (Regime > 0)
{
enterLong();
}
else
{
enterShort();
}
}
|
|
|
Entropy Analogues
[Re: TipmyPip]
#489732
1 hour ago
1 hour ago
|
Joined: Sep 2017
Posts: 351
TipmyPip
OP
Senior Member
|
OP
Senior Member
Joined: Sep 2017
Posts: 351
|
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. // 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();
}
}
|
|
|
Robust Quantile Curve
[Re: TipmyPip]
#489733
1 hour ago
1 hour ago
|
Joined: Sep 2017
Posts: 351
TipmyPip
OP
Senior Member
|
OP
Senior Member
Joined: Sep 2017
Posts: 351
|
Robust Quantile Curve — The Flexible Ruler Imagine measuring a flexible ruler that bends differently at short and long observation horizons. The ruler represents average price returns across several horizons. Instead of allowing one unusual point to determine its shape, the program uses a robust construction designed to describe the broad relationship among those observations. For each observation time, the program compares pairs of horizon readings and obtains a collection of possible slopes along the horizon axis. It then takes the median of those slopes. This Theil–Sen construction is less dependent on a single extreme reading than an ordinary line fitted only by minimizing squared errors. The program repeats that construction across a historical window, creating a time series of robust curve slopes. It sorts the historical slopes to obtain their median and empirical quantiles. It also measures typical displacement from the historical center using the median absolute deviation. These references describe the usual shape of the ruler without assuming that the entire distribution follows a perfect bell curve. The newest curve is examined in two complementary ways. Its empirical rank indicates whether it lies near an edge of the historical distribution. Its robust displacement score indicates whether it is meaningfully far from the historical center. Both must support an extreme interpretation before the program considers a trade. There is also a turning requirement. The curve must begin moving back toward the historical center while still remaining unusually displaced. A high long horizon versus short horizon slope suggests that near horizon returns have lagged; when this distortion starts easing, the strategy considers buying. The corresponding low extreme turning upward supports selling. This is a curve normalization hypothesis rather than a simple momentum rule. Existing positions can be closed when the robust displacement becomes small or when the curve rank returns toward the middle of its historical range. The strategy does not need to wait for an opposite extreme to recognize that the original distortion has largely disappeared. Price based protection, constrained exposure, and a maximum holding period provide separate safeguards. Research diagnostics describe quantile asymmetry, the width between outer historical quantiles, and the typical change between successive robust curve slopes. These help distinguish a stable distribution with a temporary distortion from a curve whose entire historical distribution is shifting. Robustness does not establish economic causality. Horizon returns overlap, historical quantiles can change, and a turn toward an old center can be temporary. The analytical curve is not a separately traded instrument. Converting its normalization into a directional Forex trade remains an experimental interpretation requiring cost aware, out of sample validation. // 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();
}
}
|
|
|
|