|
2 registered members (TipmyPip, Martin_HH),
9,681
guests, and 1
spider. |
|
Key:
Admin,
Global Mod,
Mod
|
|
|
Order Book Fuzzy Strangle
[Re: TipmyPip]
#489701
10/13/26 17:08
10/13/26 17:08
|
Joined: Sep 2017
Posts: 346
TipmyPip
OP
Senior Member
|
OP
Senior Member
Joined: Sep 2017
Posts: 346
|
We can extend your original strategy into a market-depth-aware options trading system, combining Zorro's native Level II order-book analysis, fuzzy logic, volatility estimation, and dynamic options selection. The important improvement is that the strategy will no longer rely exclusively on historical closing prices. It will also examine the current distribution of buy and sell liquidity before deciding whether to open an options position. 1. Zorro's built-in order-book functions Zorro provides three native functions for market-depth analysis, documented in its official manual. They require Zorro S. Function Purpose orderUpdate() Retrieves market-depth quotes from the broker or historical data orderCVD() Constructs a cumulative distribution of order-book liquidity cpd() Measures cumulative relative bid or ask volume at a price These can be combined into three analytical signals: - Bid pressure: Relative liquidity on the buying side. - Ask pressure: Relative liquidity on the selling side. - Order-book imbalance: Difference between buying and selling pressure, normalized by their combined liquidity. A useful mathematical representation is: \[ I_t=\frac{B_t-A_t}{B_t+A_t} \] Where \(B_t\) and \(A_t\) are comparable bid-side and ask-side cumulative liquidity measurements. A positive imbalance indicates more visible bid liquidity; a negative imbalance indicates more visible ask liquidity. Neither guarantees the next price movement, because displayed orders can be canceled or moved. (Latex formula) // OrderBookFuzzyStrangle_v1.c
// Zorro S / lite-C -- SPY long strangle with underlying order-book filter.
// Requires synchronized historical .t2 book snapshots and .t8 options chains
// for Test/Train; GET_BOOK and options support from broker for Trade.
// Research prototype: validate fills, execution, and broker behavior in demo.
#include <contract.c>
#define BOOK_HANDLE 1
#define VOL_PERIOD 20
#define MIN_BOOK_QUOTES 6
#define MIN_DTE 30
#define EXIT_DTE 10
#define MIN_IMBALANCE 0.15
#define EXIT_IMBALANCE 0.07
#define MIN_FUZZY 0.65
#define MOVE_MULTIPLIER 1.10
#define MAX_DEBIT_USD 1500.0
#define PROFIT_FRACTION 0.50
#define LOSS_FRACTION 0.35
// Average absolute close-to-close change, in SPY dollars per hourly bar.
var meanAbsoluteMove(vars Prices, int Period)
{
var Sum = 0;
int i;
for(i = 1; i <= Period; i++)
Sum += abs(Prices[i-1] - Prices[i]);
return Sum / Period;
}
// Returns 1 if a usable book snapshot exists and fills the imbalance output.
int readBookImbalance(var Spot, var* Imbalance)
{
int N = orderUpdate(0,BOOK_HANDLE); // History\SPY.t2 or broker GET_BOOK
if(N < MIN_BOOK_QUOTES) return 0;
T2* Quotes = dataStr(BOOK_HANDLE,OrderRow,0);
if(!Quotes) return 0;
var Distance = 0.01*Spot; // visible liquidity within +/-1% of SPY
if(orderCVD(Quotes,N,Distance) < MIN_BOOK_QUOTES) return 0;
// cpd returns values in 0..100 normalized to the larger book side.
var BidDepth = cpd(-(Spot-Distance));
var AskDepth = cpd(Spot+Distance);
var Total = BidDepth+AskDepth;
if(Total <= 0) return 0;
*Imbalance = (BidDepth-AskDepth)/Total;
return 1;
}
// Returns 1 if open options should be liquidated on risk/expiration rules.
int exitByRisk()
{
var Cost = 0;
var OpenPnL = 0;
int ExpireSoon = 0;
int Legs = 0;
for(current_trades) {
if(!TradeIsOpen || !TradeIsLong || !TradeIsContract) continue;
Cost += TradePriceOpen*TradeUnits;
OpenPnL += TradeProfit;
if(contractDays(ThisTrade) <= EXIT_DTE) ExpireSoon = 1;
Legs++;
}
if(!Legs) return 0;
if(ExpireSoon) return 1;
if(Cost <= 0) return 0;
if(OpenPnL >= PROFIT_FRACTION*Cost) return 1;
if(OpenPnL <= -LOSS_FRACTION*Cost) return 1;
return 0;
}
void buyStrangle(var Spot, var AvgMove)
{
// Dynamic OTM strike targets in dollars. Select nearest listed strikes.
var Gap = max(0.01*Spot,8*AvgMove);
var CallTarget = Spot+Gap;
var PutTarget = Spot-Gap;
if(PutTarget <= 0) return;
// 6 means select by strike (the 4th contractFind parameter is mandatory).
CONTRACT* Call = contractFind(CALL,MIN_DTE,CallTarget,6);
CONTRACT* Put = contractFind(PUT,MIN_DTE,PutTarget,6);
if(!Call || !Put) return;
if(Call->fStrike <= Spot || Put->fStrike >= Spot) return;
if(Call->Expiry != Put->Expiry) return;
if(contractDays(Call) <= EXIT_DTE || contractDays(Put) <= EXIT_DTE)
return;
if(contractPrice(Call) <= 0) return;
if(contractPrice(Put) <= 0) return;
if(Call->fAsk <= 0 || Put->fAsk <= 0) return;
if(Call->fBid <= 0 || Put->fBid <= 0) return;
var Debit = (Call->fAsk+Put->fAsk)*Multiplier;
if(Debit <= 0 || Debit > MAX_DEBIT_USD) return;
if(combo(Call,1,Put,1,0,0,0,0) != 2) return;
MarginCost = comboMargin(1,3); // Stock/ETF options; long positions.
// The broker receives the combo when the second leg is submitted, if
// SET_COMBO_LEG is supported. Otherwise separate orders can be submitted.
enterLong(comboLeg(1));
enterLong(comboLeg(2));
printf("\nStrangle order attempted: SPY %.2f, call %.2f, put %.2f, debit %.2f",
Spot,Call->fStrike,Put->fStrike,Debit);
}
void run()
{
set(LOGFILE);
BarPeriod = 60;
LookBack = 100;
StartDate = 2020;
EndDate = 2024;
History = ".t8"; // Workshop 8: historical options/unadjusted underlying.
assetList("AssetsIB");
asset("SPY");
algo("OBStrangle");
Multiplier = 100; // Standard SPY option contract; verify with broker.
Capital = 100000; // Research backtest account, not a live risk control.
if(is(EXITRUN)) return;
static int LastChainDate = 0;
if(is(INITRUN)) {
LastChainDate = 0;
return;
}
vars Prices = series(priceClose());
var AvgMove = meanAbsoluteMove(Prices,VOL_PERIOD);
if(AvgMove <= 0) return;
FuzzyRange = max(0.01,0.25*AvgMove);
var Bullish = max(risingF(Prices),valleyF(Prices));
var Bearish = max(fallingF(Prices),peakF(Prices));
// In historical tests, options must be refreshed every bar.
// For live mode, cache the costly IB chain download for the session day.
int Today = ymd(wdate(0));
if(!Live || Today != LastChainDate || NumContracts <= 0) {
if(!contractUpdate(Asset,0,CALL|PUT)) return;
LastChainDate = Today;
}
// These exits must work even when the current order book is unavailable.
if(NumOpenLong > 0 && exitByRisk()) {
exitLong("cp"); // Only call and put contracts of this asset/algo.
return;
}
var Imbalance = 0;
if(!readBookImbalance(priceClose(),&Imbalance)) return;
plot("Order book imbalance",Imbalance,NEW,BLUE);
if(NumOpenLong > 0 || NumPendingLong > 0) {
// Fuzzy bearish price signal plus disappearing book pressure.
if(Bearish > MIN_FUZZY && abs(Imbalance) < EXIT_IMBALANCE)
exitLong("cp");
return;
}
// Require a meaningful move, visible bid/ask pressure, and corresponding
// fuzzy price direction. Long strangle still buys BOTH call and put.
if(abs(Prices[0]-Prices[1]) < MOVE_MULTIPLIER*AvgMove) return;
if(abs(Imbalance) < MIN_IMBALANCE) return;
if(Imbalance > 0 && Bullish >= MIN_FUZZY)
buyStrangle(priceClose(),AvgMove);
else if(Imbalance < 0 && Bearish >= MIN_FUZZY)
buyStrangle(priceClose(),AvgMove);
}
|
|
|
Markov Rulebook v.01
[Re: TipmyPip]
#489704
10/15/26 08:28
10/15/26 08:28
|
Joined: Sep 2017
Posts: 346
TipmyPip
OP
Senior Member
|
OP
Senior Member
Joined: Sep 2017
Posts: 346
|
VWAP Markov State Trading Strategy for Zorro The Strategy consist of an algorithmic trading strategy that combines VWAP volatility bands, candlestick price action, and Markov state-transition probabilities to identify trading opportunities across 28 currency pairs. How the strategy works 1. VWAP Analysis: Calculates VWAP and six volatility bands at ±1, ±2, and ±2.5 standard deviations to identify important market price zones. 2. Market Classification: Classifies completed candles according to their bullish, bearish, or doji structure and their positions relative to the VWAP bands. 3. Markov Probability Model: Learns historical transitions between market states and estimates the probability that the next candle will be bullish, bearish, or neutral. 4. Trading Decisions: Supports 36 predefined research strategies, 300 VWAP event/action rules, custom filters, and Markov-directed trading. 5. Risk Management: Includes configurable stop-loss levels, pending entries, VWAP-based exits, and portfolio position limits. Key technical characteristics Feature Configuration Currency pairs 28 Default timeframe 5 minutes (configurable to H4) Markov states 780 Transition matrix 780 × 780 per pair Historical learning window 1,024 observations VWAP period 20 Default execution Market orders Default stop-loss buffer 10% of candle range Important observations Although the filename says Markov720, the actual code defines 780 states: 330 bullish, 330 bearish, and 120 doji. The current default configuration runs research strategy M00 while the Markov model monitors transitions. Markov-directed trading must be explicitly enabled. I also noticed that the code's sweep-direction logic does not match your earlier bullish-candle SELL / bearish-candle BUY reversal specification. That should be corrected if the original reversal behavior is intended. // VWAP_Rulebook_28Pair_Markov780.c
#include <default.c>
// ---- EDIT THESE CONFIGURATION PARAMETERS (native lite-C, no MQL input syntax) ----
#define ENGINE_MODE 0 // 0 M00-M35, 1 Catalog 0..299, 2 S/B custom, 3 Markov
#define RESEARCH_ID 0 // M00..M35
#define CATALOG_ID 0 // 0..299 (100 observable events x BUY/SELL/EXIT)
#define SELL_STATIC_ID 0 // 0..30
#define BUY_STATIC_ID 0 // 0..30
#define SELL_EVENT_ID 1 // 1..12
#define BUY_EVENT_ID 1 // 1..12
#define CANDLE_RULE_ID 0 // 0 disabled, 1..25
#define SIGNATURE_FILTER -1 // -1 disabled; 0..779
#define TIMEFRAME_MINUTES 5 // 5 M5 default, 240 for H4
#define SINGLE_ASSET 0 // 0 = 28 assets, 1 = named symbol only
#define SINGLE_ASSET_NAME "EUR/USD"
#define ASSET_LIST "AssetsForex" // Broker-compatible list MUST contain requested pairs
#define ENTRY_STYLE 0 // 0 market; 1 Zorro-managed Entry stop
#define PENDING_PERCENT 10.0
#define STOP_PERCENT 10.0
#define ENTRY_WAIT_BARS 2
#define ENABLE_BROKER_STOP 1 // Zorro StopFactor=1, when broker plugin supports it
#define ENABLE_SOFTWARE_EXIT 1
#define EXIT_RULE_ID 14 // X01..X20
#define MAX_SPREAD_PIPS 0.0 // 0 disabled
#define ORDER_LOTS 1 // Zorro INTEGER micro/mini/standard lots, NOT MQL "0.01"
#define MAX_PORTFOLIO_POSITIONS 28
#define VWAP_PERIOD 20
#define VWAP_MATCH_MQL 1 // N+1 VWAP bars, N STD bars
#define VWAP_VOLUME_MODE 1 // 1 use marketVol (Zorro S required), 0 unweighted approximation
#define VWAP_PRICE_MODE 0 // 0 close; 1 open; 2 high; 3 low; 4 median; 5 typical; 6 weighted
#define DEVIATION_1 1.0
#define DEVIATION_2 2.0
#define DEVIATION_3 2.5
#define MARKOV_MODE 0 // 0 monitor only, 1 gate existing engines
#define MARKOV_MIN_COUNT 8
#define MARKOV_MIN_DIRECTION 0.50
#define MARKOV_PRIOR 0.01
#define MARKOV_BACKOFF 1
#define MARKOV_BLOCK_SPARSE 0
#define MARKOV_NEED_SWEEP 1
#define MK_WINDOW 1024
#define MAX_PAIRS 28
#define MK_STATES 780
#define Z_THRESHOLD 1.0
#define WICK_THRESHOLD 0.35
#define EPSILON_PIPS 1.0
#define MIN_REWARD_PIPS 1.0
#define MIN_RISK_REWARD 1.0
#define SMALL_CANDLE_SPREAD_MULTIPLE 3.0
#define SWEEP_TIMEOUT_SECONDS 60
#define MIN_RETURN_TICKS 3
#define EXIT_SIGMA_MULTIPLE 1.0
#define EXIT_RANGE_MULTIPLE 1.0
#define EXIT_RANGE_PERCENT 10.0
#define EXIT_WAIT_BARS 3
#define EXIT_DEADLINE_MINUTES 120
#define PARTIAL_PERCENT 50.0
#define TRAIL_SIGMA_MULTIPLE 1.0
#define PH_SWEEP 0
#define PH_RETURN 1
#define PH_READY 2
#define PH_RETEST 3
// Exact 28-currency universe, Zorro's usual slash format.
string ForexNames[28];
void initForexNames()
{
// lite-C requires explicit initialization for arrays of string pointers.
ForexNames[0]="EUR/USD";ForexNames[1]="GBP/USD";ForexNames[2]="USD/JPY";
ForexNames[3]="GBP/JPY";ForexNames[4]="USD/CAD";ForexNames[5]="EUR/AUD";
ForexNames[6]="EUR/JPY";ForexNames[7]="AUD/CAD";ForexNames[8]="AUD/JPY";
ForexNames[9]="AUD/NZD";ForexNames[10]="AUD/USD";ForexNames[11]="CAD/JPY";
ForexNames[12]="EUR/CAD";ForexNames[13]="EUR/CHF";ForexNames[14]="EUR/GBP";
ForexNames[15]="EUR/NZD";ForexNames[16]="GBP/CAD";ForexNames[17]="GBP/CHF";
ForexNames[18]="NZD/CAD";ForexNames[19]="NZD/JPY";ForexNames[20]="NZD/USD";
ForexNames[21]="USD/CHF";ForexNames[22]="CHF/JPY";ForexNames[23]="AUD/CHF";
ForexNames[24]="GBP/NZD";ForexNames[25]="NZD/CHF";ForexNames[26]="CAD/CHF";
ForexNames[27]="GBP/AUD";
}
typedef struct
{
int usable;
string symbol;
int valid,signature,dir;
int phase,crossMask,retouch,quoteKnown,activeBefore;
int observedBar,lastEntryBar,lastPersistBar;
int ringHead,ringSize;
int predictionSource,nEffective,topNext,topCount;
int seenPosition;
int partialDone;
var o,h,l,c,range,body,uw,lw;
var v,sigma,band[7]; // D3 D2 D1 VWAP U1 U2 U3
var previousAsk,sweepStamp;
var probabilityBull,probabilityBear,probabilityDoji;
var frozenCenter,highWater,lowWater;
var lastEntryLevel,entryStamp;
} FXSTATE;
FXSTATE FX[MAX_PAIRS];
int* MarkovEdges = 0;
int* MarkovOutgoing = 0;
int* MarkovBull = 0;
int* MarkovBear = 0;
int* MarkovDoji = 0;
int* MarkovRing = 0;
int MarkovDirection[MK_STATES],MarkovCloseZone[MK_STATES];
int ActivePairs = 0;
// ---- Signature ranking: 0..329 bullish, 330..659 bearish, 660..779 doji ----
// a=zone(L), b=zone(min(O,C)), c=zone(max(O,C)), d=zone(H).
int signatureRank(int a,int b,int c,int d,int doji)
{
int n=0,i,j,k,h;
if(doji) {
for(i=0;i<8;i++)for(j=i;j<8;j++)for(k=j;k<8;k++) {
if(i==a && j==b && k==d)return n;
n++;
}
return -1;
}
for(i=0;i<8;i++)for(j=i;j<8;j++)
for(k=j;k<8;k++)for(h=k;h<8;h++) {
if(i==a && j==b && k==c && h==d)return n;
n++;
}
return -1;
}
void makeMetadata()
{
int n=0,i,j,k,h;
for(i=0;i<8;i++)for(j=i;j<8;j++)
for(k=j;k<8;k++)for(h=k;h<8;h++) {
MarkovDirection[n]=1; MarkovCloseZone[n]=k;
MarkovDirection[330+n]=-1; MarkovCloseZone[330+n]=j;
n++;
}
n=0;
for(i=0;i<8;i++)for(j=i;j<8;j++)for(k=j;k<8;k++) {
MarkovDirection[660+n]=0; MarkovCloseZone[660+n]=j;
n++;
}
}
int initMarkov()
{
int edgeCount = ActivePairs*MK_STATES*MK_STATES;
int rowCount=ActivePairs*MK_STATES;
MarkovEdges = malloc(edgeCount*sizeof(int));
MarkovOutgoing = malloc(rowCount*sizeof(int));
MarkovBull = malloc(rowCount*sizeof(int));
MarkovBear = malloc(rowCount*sizeof(int));
MarkovDoji = malloc(rowCount*sizeof(int));
MarkovRing = malloc(ActivePairs*MK_WINDOW*sizeof(int));
if(!MarkovEdges || !MarkovOutgoing || !MarkovBull || !MarkovBear ||
!MarkovDoji || !MarkovRing) return 0;
memset(MarkovEdges,0,edgeCount*sizeof(int));
memset(MarkovOutgoing,0,rowCount*sizeof(int));
memset(MarkovBull,0,rowCount*sizeof(int));
memset(MarkovBear,0,rowCount*sizeof(int));
memset(MarkovDoji,0,rowCount*sizeof(int));
int i;
for(i=0;i<ActivePairs*MK_WINDOW;i++)MarkovRing[i]=-1;
makeMetadata();
return 1;
}
void releaseMarkov()
{
if(MarkovEdges)free(MarkovEdges);
if(MarkovOutgoing)free(MarkovOutgoing);
if(MarkovBull)free(MarkovBull);
if(MarkovBear)free(MarkovBear);
if(MarkovDoji)free(MarkovDoji);
if(MarkovRing)free(MarkovRing);
MarkovEdges=0;MarkovOutgoing=0;MarkovBull=0;MarkovBear=0;
MarkovDoji=0;MarkovRing=0;
}
int mkRow(int id,int from) {return id*MK_STATES+from;}
int mkEdge(int id,int from,int to) {return (id*MK_STATES+from)*MK_STATES+to;}
int mkRingIndex(int id,int offset) {return id*MK_WINDOW+offset;}
void adjustTransition(int id,int from,int to,int delta)
{
if(from<0 || to<0 || from>=MK_STATES || to>=MK_STATES)return;
int e=mkEdge(id,from,to),r=mkRow(id,from);
if(delta<0 && MarkovEdges[e]<=0)return;
MarkovEdges[e]+=delta;MarkovOutgoing[r]+=delta;
if(to<330)MarkovBull[r]+=delta;
else if(to<660)MarkovBear[r]+=delta;
else MarkovDoji[r]+=delta;
}
void addObservation(int id,int sig)
{
if(sig<-1 || sig>=MK_STATES)return;
int head=FX[id].ringHead,n=FX[id].ringSize;
if(n>=MK_WINDOW) {
int old=MarkovRing[mkRingIndex(id,head)];
int next=MarkovRing[mkRingIndex(id,(head+1)%MK_WINDOW)];
adjustTransition(id,old,next,-1);
head=(head+1)%MK_WINDOW;n--;
}
if(n>0) {
int last=MarkovRing[mkRingIndex(id,(head+n-1)%MK_WINDOW)];
adjustTransition(id,last,sig,1);
}
MarkovRing[mkRingIndex(id,(head+n)%MK_WINDOW)]=sig;
FX[id].ringHead=head;FX[id].ringSize=n+1;
}
int zoneAt(int id,var p)
{
int j;for(j=0;j<7;j++)if(p<FX[id].band[j])return j;
return 7;
}
int signatureOf(int id)
{
int zl=zoneAt(id,FX[id].l);
int zb=zoneAt(id,min(FX[id].o,FX[id].c));
int zt=zoneAt(id,max(FX[id].o,FX[id].c));
int zh=zoneAt(id,FX[id].h);
if(FX[id].dir==0)return 660+signatureRank(zl,zb,zt,zh,1);
if(FX[id].dir>0)return signatureRank(zl,zb,zt,zh,0);
return 330+signatureRank(zl,zb,zt,zh,0);
}
void predict(int id)
{
FXSTATE* F=&FX[id];
int st=F->signature;
F->nEffective=0;F->predictionSource=0;
F->probabilityBull=0;F->probabilityBear=0;F->probabilityDoji=0;
F->topNext=-1;F->topCount=0;
if(st<0 || st>=MK_STATES)return;
int row=mkRow(id,st),n=MarkovOutgoing[row],bull=MarkovBull[row];
int bear=MarkovBear[row],doji=MarkovDoji[row],source=1,k,r;
if(MARKOV_BACKOFF && n<MARKOV_MIN_COUNT) {
n=0;bull=0;bear=0;doji=0;source=2;
for(k=0;k<MK_STATES;k++)
if(MarkovDirection[k]==MarkovDirection[st] &&
MarkovCloseZone[k]==MarkovCloseZone[st]) {
r=mkRow(id,k);
n+=MarkovOutgoing[r];bull+=MarkovBull[r];
bear+=MarkovBear[r];doji+=MarkovDoji[r];
}
if(n<MARKOV_MIN_COUNT) {
n=0;bull=0;bear=0;doji=0;source=3;
for(k=0;k<MK_STATES;k++)if(MarkovDirection[k]==MarkovDirection[st]) {
r=mkRow(id,k);
n+=MarkovOutgoing[r];bull+=MarkovBull[r];
bear+=MarkovBear[r];doji+=MarkovDoji[r];
}
}
if(n<MARKOV_MIN_COUNT) {
n=0;bull=0;bear=0;doji=0;source=4;
for(k=0;k<MK_STATES;k++) {
r=mkRow(id,k);
n+=MarkovOutgoing[r];bull+=MarkovBull[r];
bear+=MarkovBear[r];doji+=MarkovDoji[r];
}
}
}
F->predictionSource=source;F->nEffective=n;
var denom=n+MK_STATES*MARKOV_PRIOR;
if(denom<=0)return;
F->probabilityBull=(bull+330.0*MARKOV_PRIOR)/denom;
F->probabilityBear=(bear+330.0*MARKOV_PRIOR)/denom;
F->probabilityDoji=(doji+120.0*MARKOV_PRIOR)/denom;
for(k=0;k<MK_STATES;k++) {
int count=MarkovEdges[mkEdge(id,st,k)];
if(count>F->topCount) { F->topCount=count;F->topNext=k; }
}
}
int markovDecision(int id)
{
FXSTATE* F=&FX[id];
if(F->nEffective<MARKOV_MIN_COUNT)return 0;
if(F->probabilityBear>=MARKOV_MIN_DIRECTION &&
F->probabilityBear>F->probabilityBull)return -1;
if(F->probabilityBull>=MARKOV_MIN_DIRECTION &&
F->probabilityBull>F->probabilityBear)return 1;
return 0;
}
int passMarkov(int id,int side)
{
if(ENGINE_MODE!=3 && MARKOV_MODE==0)return 1;
if(FX[id].nEffective<MARKOV_MIN_COUNT)
return (ENGINE_MODE!=3 && !MARKOV_BLOCK_SPARSE);
return markovDecision(id)==side;
}
// VWAP original: N+1 prices in numerator but N deviations around same center.
// In Zorro, marketVol may be unavailable without Zorro S or compatible .t6 feed.
// VWAP_VOLUME_MODE=0 intentionally uses equal weights, NOT exact MQL tick-volume VWAP.
// marketVol() in Zorro S can be trade volume, quote rate or historical fVol.
// Matching MQL's iVolume() requires verified matching tick-volume history.
var selectedPrice(int offset)
{
var o=priceO(offset),h=priceH(offset),l=priceL(offset),c=priceC(offset);
if(VWAP_PRICE_MODE==1)return o;
if(VWAP_PRICE_MODE==2)return h;
if(VWAP_PRICE_MODE==3)return l;
if(VWAP_PRICE_MODE==4)return (h+l)*0.5;
if(VWAP_PRICE_MODE==5)return (h+l+c)/3.;
if(VWAP_PRICE_MODE==6)return (h+l+2*c)/4.;
return c;
}
int calculateBands(int id)
{
FXSTATE* F=&FX[id];
int window=VWAP_PERIOD, j;
if(VWAP_MATCH_MQL)window++;
var sv=0,pv=0,p,w;
for(j=0;j<window;j++) {
p=selectedPrice(j);
if(p<=0)return 0;
w=1;
if(VWAP_VOLUME_MODE==1)w=marketVol(j);
if(w<=0)return 0;
pv+=p*w;sv+=w;
}
if(sv<=0)return 0;
F->v=pv/sv;
var sq=0;
for(j=0;j<VWAP_PERIOD;j++) {
p=selectedPrice(j);
sq+=(p-F->v)*(p-F->v);
}
F->sigma=sqrt(sq/VWAP_PERIOD);
if(F->sigma<=0)return 0;
F->band[0]=F->v-DEVIATION_3*F->sigma;
F->band[1]=F->v-DEVIATION_2*F->sigma;
F->band[2]=F->v-DEVIATION_1*F->sigma;
F->band[3]=F->v;
F->band[4]=F->v+DEVIATION_1*F->sigma;
F->band[5]=F->v+DEVIATION_2*F->sigma;
F->band[6]=F->v+DEVIATION_3*F->sigma;
for(j=1;j<7;j++)if(F->band[j]<=F->band[j-1])return 0;
return 1;
}
void resetPhase(int id)
{
FX[id].phase=PH_SWEEP;FX[id].crossMask=0;
FX[id].sweepStamp=0;FX[id].retouch=0;
}
int findFX(string name)
{
int id;for(id=0;id<ActivePairs;id++)
if(FX[id].usable && !strcmp(FX[id].symbol,name))return id;
return -1;
}
var clockSeconds() {return wdate(0)*86400.;} // simulated/bar clock, not PC wall time
void completeCandle(int id)
{
FXSTATE* F=&FX[id];
F->o=priceO(0);F->h=priceH(0);F->l=priceL(0);F->c=priceC(0);
F->range=F->h-F->l;
F->valid=0;F->signature=-1;
F->dir=0;F->body=abs(F->c-F->o);
F->uw=F->h-max(F->o,F->c);F->lw=min(F->o,F->c)-F->l;
if(F->c>F->o)F->dir=1; else if(F->c<F->o)F->dir=-1;
if(F->range>0)F->valid=calculateBands(id);
if(F->valid)F->signature=signatureOf(id);
addObservation(id,F->signature);
predict(id);
if(!is(LOOKBACK) && F->valid)
printf("\n%s VWAP %.5f sig %i N %i Pbull %.3f Pbear %.3f",
F->symbol,F->v,F->signature,F->nEffective,
F->probabilityBull,F->probabilityBear);
resetPhase(id);
F->quoteKnown=0;
F->observedBar=Bar;
}
int crossedMask(int id,var before,var after)
{
int k,m=0;
for(k=0;k<7;k++) {
var b=FX[id].band[k];
if(before<b && after>=b)m |= (1<<k);
if(before>b && after<=b)m |= (1<<(7+k));
}
return m;
}
int crossedBit(int level,int up)
{
if(up)return (1<<level);
return (1<<(level+7));
}
void sweepEvent(int id,var before,var after,int newQuote)
{
if(!newQuote || FX[id].dir==0)return;
FXSTATE* F=&FX[id];
var t=PIP;
int m=crossedMask(id,before,after);
if(F->phase!=PH_SWEEP)F->crossMask |= m;
if(F->phase==PH_SWEEP) {
if(F->dir<0 && before<F->h && after>=F->h) {
F->phase=PH_RETURN;F->sweepStamp=clockSeconds();F->crossMask=m;
} else if(F->dir>0 && before>=F->l && after<F->l) {
F->phase=PH_RETURN;F->sweepStamp=clockSeconds();F->crossMask=m;
}
return;
}
if(ENGINE_MODE==0 && RESEARCH_ID==30 && SWEEP_TIMEOUT_SECONDS>0 &&
clockSeconds()-F->sweepStamp>SWEEP_TIMEOUT_SECONDS) {
resetPhase(id);return;
}
if(F->phase==PH_RETURN) {
if(F->dir<0 && after<F->h) {
F->phase=PH_READY;
if(ENGINE_MODE==0 && RESEARCH_ID==24)F->phase=PH_RETEST;
} else if(F->dir>0 && after>F->l) {
F->phase=PH_READY;
if(ENGINE_MODE==0 && RESEARCH_ID==24)F->phase=PH_RETEST;
}
return;
}
if(F->phase==PH_RETEST) {
if(F->dir<0) {
if(after>F->h+t) {resetPhase(id);return;}
if(after>=F->h-t)F->retouch=1;
if(F->retouch && before>after && after<F->h-t)F->phase=PH_READY;
} else {
if(after<F->l-t) {resetPhase(id);return;}
if(after<=F->l+t)F->retouch=1;
if(F->retouch && before<after && after>F->l+t)F->phase=PH_READY;
}
return;
}
if(F->phase==PH_READY) {
if(F->dir<0 && after>=F->h-t && after<F->h)F->retouch=1;
if(F->dir>0 && after<=F->l+t && after>F->l)F->retouch=1;
if(F->dir<0 && after>=F->h)resetPhase(id);
if(F->dir>0 && after<=F->l)resetPhase(id);
}
}
// All filters use side=+1 BUY and -1 SELL; candle sign is separate.
int payoffOK(int id,int side,var ask,var minPips)
{
if(side<0)return (ask-FX[id].v)>=minPips*PIP;
return (FX[id].v-ask)>=minPips*PIP;
}
int anatomy(int id,int rule,var ask)
{
FXSTATE* F=&FX[id];var R=F->range,B=F->body;
var u=F->uw,l=F->lw,o=F->o,c=F->c,h=F->h,lo=F->l;
if(rule==0)return 1;
if(R<=0)return 0;
if(rule==1)return c>o;
if(rule==2)return c<o;
if(rule==3)return c==o;
if(rule==4)return B/R>=0.70;
if(rule==5)return B/R<=0.20;
if(rule==6)return u/R>=0.50;
if(rule==7)return l/R>=0.50;
if(rule==8)return u>=2*l;
if(rule==9)return l>=2*u;
if(rule==10)return (c-lo)/R>=0.90;
if(rule==11)return (c-lo)/R<=0.10;
if(rule==12)return (o-lo)/R>=0.90;
if(rule==13)return (o-lo)/R<=0.10;
if(rule==14)return min(o,c)>lo+0.5*R;
if(rule==15)return max(o,c)<lo+0.5*R;
if(rule==16)return min(o,c)<=lo+0.5*R && max(o,c)>=lo+0.5*R;
if(rule==17)return abs(u-l)/R<=0.10;
if(rule==18)return B/R>=0.80;
if(rule==19) {if(F->dir<0)return u>=B;return l>=B;}
if(rule==20)return R<=SMALL_CANDLE_SPREAD_MULTIPLE*Spread;
if(rule==21)return abs(h-o)/R<=0.10 || abs(o-lo)/R<=0.10;
if(rule==22)return abs(h-c)/R<=0.10 || abs(c-lo)/R<=0.10;
if(rule==23)return c>o && u>B;
if(rule==24)return c<o && l>B;
if(rule==25)return F->sigma>0 && R/F->sigma>=Z_THRESHOLD;
return 0;
}
int bandRule(int id,int side,int n,var q,var ask)
{
FXSTATE* F=&FX[id];
var O=F->o,H=F->h,L=F->l,C=F->c,R=F->range;
var B=F->body,W=F->lw;if(side<0)W=F->uw;
var v=F->v,sd=F->sigma,d1=F->band[2],d2=F->band[1],d3=F->band[0];
var u1=F->band[4],u2=F->band[5],u3=F->band[6];
var eps=EPSILON_PIPS*PIP;
if(side<0) {
if(n==0)return q<=u3;
if(n==1)return H>=u1;if(n==2)return H>=u2;if(n==3)return H>=u3;
if(n==4)return C>=v;if(n==5)return C>=u1;
if(n==6)return O<v && C>=v;
if(n==7)return O<u1 && C>=u1;
if(n==8)return O<u2 && C>=u2;
if(n==9)return H>=u3 && C<u3;
if(n==10)return H>=u3 && C<u2;
if(n==11)return H>=u2 && C<u1;
if(n==12)return H>=u1 && C<v;
if(n==13)return q>=v && q<=u1;
if(n==14)return q>=u1 && q<=u2;
if(n==15)return q>=u2 && q<=u3;
if(n==16)return q<=v;
if(n==17)return q>v && q<=u3;
if(n==18)return H>=u3 && q>v && q<=u2;
if(n==19)return H>=u3 && q>v && q<=u1;
if(n==20)return H>=u2 && W>B;
if(n==21)return H>=u3 && W/R>=0.35;
if(n==22)return sd>0 && (H-v)/sd>=Z_THRESHOLD;
if(n==23)return sd>0 && (q-v)/sd>=0.5 && (q-v)/sd<=2.5;
if(n==24)return abs(q-u2)<=eps;
if(n==25)return abs(H-u3)<=eps;
if(n==26)return H>u3 && q<u3;
if(n==27)return q<u3 && H>=u3 && C>O;
if(n==28)return sd>0 && R/sd>=Z_THRESHOLD;
if(n==29)return W/R>=WICK_THRESHOLD && q>v;
if(n==30)return q>v && payoffOK(id,side,ask,MIN_REWARD_PIPS);
} else {
if(n==0)return q>=d3;
if(n==1)return L<=d1;if(n==2)return L<=d2;if(n==3)return L<=d3;
if(n==4)return C<=v;if(n==5)return C<=d1;
if(n==6)return O>v && C<=v;
if(n==7)return O>d1 && C<=d1;
if(n==8)return O>d2 && C<=d2;
if(n==9)return L<=d3 && C>d3;
if(n==10)return L<=d3 && C>d2;
if(n==11)return L<=d2 && C>d1;
if(n==12)return L<=d1 && C>v;
if(n==13)return q>=d1 && q<=v;
if(n==14)return q>=d2 && q<=d1;
if(n==15)return q>=d3 && q<=d2;
if(n==16)return q>=v;
if(n==17)return q>=d3 && q<v;
if(n==18)return L<=d3 && q>=d2 && q<v;
if(n==19)return L<=d3 && q>=d1 && q<v;
if(n==20)return L<=d2 && W>B;
if(n==21)return L<=d3 && W/R>=0.35;
if(n==22)return sd>0 && (v-L)/sd>=Z_THRESHOLD;
if(n==23)return sd>0 && (q-v)/sd>=-2.5 && (q-v)/sd<=-0.5;
if(n==24)return abs(q-d2)<=eps;
if(n==25)return abs(L-d3)<=eps;
if(n==26)return L<d3 && q>d3;
if(n==27)return q>d3 && L<=d3 && C<O;
if(n==28)return sd>0 && R/sd>=Z_THRESHOLD;
if(n==29)return W/R>=WICK_THRESHOLD && q<v;
if(n==30)return q<v && payoffOK(id,side,ask,MIN_REWARD_PIPS);
}
return 0;
}
int confirmation(int id,int side,int n,var q)
{
FXSTATE* F=&FX[id];
if(n==1)return 1;
if(n>=2 && n<=5) {
int b=3;
if(side<0) {
if(n==2)b=6;if(n==3)b=5;if(n==4)b=4;
return (F->crossMask & crossedBit(b,0))!=0;
}
if(n==2)b=0;if(n==3)b=1;if(n==4)b=2;
return (F->crossMask & crossedBit(b,1))!=0;
}
if(n>=6 && n<=8) {
var ratio=0.10;
if(n==7)ratio=0.25;
if(n==8)ratio=0.50;
if(side<0)return q<=F->h-ratio*F->range;
return q>=F->l+ratio*F->range;
}
if(n==9)return F->sweepStamp>0 && !F->retouch &&
clockSeconds()-F->sweepStamp>=SWEEP_TIMEOUT_SECONDS;
if(n==10)return F->retouch;
if(n==11) {
if(side<0)return (F->crossMask & crossedBit(6,0))!=0 &&
q>=F->band[5] && q<=F->band[6];
return (F->crossMask & crossedBit(0,1))!=0 &&
q>=F->band[0] && q<=F->band[1];
}
if(n==12) {
if(side<0)return (F->crossMask & crossedBit(5,0))!=0 &&
q>=F->band[4] && q<=F->band[5];
return (F->crossMask & crossedBit(1,1))!=0 &&
q>=F->band[1] && q<=F->band[2];
}
return 0;
}
var protectiveStop(int id,int side)
{
if(side<0)return FX[id].h+STOP_PERCENT*0.01*FX[id].range;
return FX[id].l-STOP_PERCENT*0.01*FX[id].range;
}
int currencyConflict(int id)
{
// All 28 are known 6-letter FX roots. Split by currency strings.
int k;
string a=ForexNames[id];
string base=strmid(a,0,3),quote=strmid(a,4,3);
for(k=0;k<ActivePairs;k++) {
if(k==id || !FX[k].usable)continue;
if(!asset(FX[k].symbol))continue;
algo("MK780");
if(NumOpenLong+NumOpenShort<=0)continue;
string b=strmid(ForexNames[k],0,3),q=strmid(ForexNames[k],4,3);
if(!strcmp(base,b) || !strcmp(base,q) ||
!strcmp(quote,b) || !strcmp(quote,q)) {
asset(FX[id].symbol);algo("MK780");return 1;
}
}
asset(FX[id].symbol);algo("MK780");
return 0;
}
int researchGate(int id,int n,int side,var q,var ask)
{
FXSTATE* F=&FX[id];int s=(side<0);
var o=F->o,h=F->h,l=F->l,c=F->c,r=F->range;
var u1=F->band[4],u2=F->band[5],u3=F->band[6];
var d1=F->band[2],d2=F->band[1],d3=F->band[0],v=F->v;
var wick=F->lw;if(s)wick=F->uw;
if(n==0 || n==26)return bandRule(id,side,0,q,ask);
if(n>=1 && n<=3)return bandRule(id,side,n,q,ask);
if(n==4) {if(s)return h>=u3 && q<u3;return l<=d3 && q>d3;}
if(n==5 || n==19) {if(s)return h>=u3 && q<=u2;return l<=d3 && q>=d2;}
if(n==6 || n==20) {if(s)return h>=u3 && q<=u1;return l<=d3 && q>=d1;}
if(n==7) {if(s)return h>=u2 && wick>F->body;return l<=d2 && wick>F->body;}
if(n==8) {if(s)return h>=u3 && wick/r>=0.35;return l<=d3 && wick/r>=0.35;}
if(n==9) {if(s)return h>=u2 && c<u1;return l<=d2 && c>d1;}
if(n==10) {if(s)return o<u1 && c>=u1;return o>d1 && c<=d1;}
if(n==11) {if(s)return o<u2 && c>=u2;return o>d2 && c<=d2;}
if(n==12) {if(s)return q>v && q<=u1;return q>=d1 && q<v;}
if(n==13) {if(s)return q>=u1 && q<=u2;return q>=d2 && q<=d1;}
if(n==14) {if(s)return q>=u2 && q<=u3;return q>=d3 && q<=d2;}
if(n==15)return payoffOK(id,side,ask,MIN_REWARD_PIPS);
if(n==16)return r/F->sigma>=1.;
if(n==17)return r/F->sigma<=1.;
if(n==18) {if(s)return h>=u3 && wick/r>=0.4;return l<=d3 && wick/r>=0.4;}
if(n>=21 && n<=23) {
if(s && h<u3)return 0;
if(!s && l>d3)return 0;
return confirmation(id,side,n-19,q);
}
if(n==24) {
if(s)return h>=u3 && F->retouch && F->phase==PH_READY;
return l<=d3 && F->retouch && F->phase==PH_READY;
}
if(n==25) {if(s)return h>=u3 && q<=h-0.1*r;return l<=d3 && q>=l+0.1*r;}
if(n==27) {
var stop=protectiveStop(id,side);
var risk=ask-stop,reward=v-ask;
if(s){risk=stop-ask;reward=ask-v;}
return risk>0 && reward>0 && reward/risk>=MIN_RISK_REWARD;
}
if(n==28) {
if(s)return h>=u3 && c>=u2 && zoneAt(id,q)==6;
return l<=d3 && c<=d2 && zoneAt(id,q)==1;
}
if(n==29) {
if(s)return h>=u3 && c<u2 && zoneAt(id,q)==5;
return l<=d3 && c>d2 && zoneAt(id,q)==2;
}
if(n==30) {
if(s && h<u3)return 0;if(!s && l>d3)return 0;
return clockSeconds()-F->sweepStamp<=60.;
}
if(n==31) {
if(s)return h>=u3 && q<=h-MIN_RETURN_TICKS*PIP;
return l<=d3 && q>=l+MIN_RETURN_TICKS*PIP;
}
if(n==32) {
if(s)return h>=u3 && Spread<=PIP;
return l<=d3 && Spread<=PIP;
}
if(n==33) {
if(s && h<u3)return 0;if(!s && l>d3)return 0;
return !currencyConflict(id);
}
if(n==34 || n==35) {if(s)return h>=u3;return l<=d3;}
return 0;
}
// Universal 300-event event catalog from uploaded MQL4:
// event=CATALOG_ID/3 (0..99), action=CATALOG_ID%3 (BUY/SELL/EXIT).
int catalogEvent(int id,var prior,var current,int observed,int newBar)
{
FXSTATE* F=&FX[id];
int evt=CATALOG_ID/3;
int beforeZone=7-zoneAt(id,prior),nowZone=7-zoneAt(id,current);
int bandIndex, up, source,dest,counter;
var line;
if(evt<8)return observed && beforeZone!=nowZone && nowZone==evt;
if(evt<22) {
if(!observed)return 0;
bandIndex=(evt-8)/2;up=((evt-8)%2)==0;
line=F->band[6-bandIndex];
if(up)return prior<=line && current>line;
return prior>=line && current<line;
}
if(evt<36) {
if(!newBar)return 0;
bandIndex=(evt-22)/2;up=((evt-22)%2)==0;
line=F->band[6-bandIndex];
if(up)return F->h>=line && F->c<line;
return F->l<=line && F->c>line;
}
if(evt<92) {
if(!observed || beforeZone==nowZone)return 0;
counter=evt-36;
for(source=0;source<8;source++)for(dest=0;dest<8;dest++) {
if(source==dest)continue;
if(counter==0)return beforeZone==source && nowZone==dest;
counter--;
}
return 0;
}
// One persistence signal per completed bar after two observations in same zone.
if(!observed || nowZone!=evt-92 || beforeZone!=nowZone ||
F->lastPersistBar==Bar)return 0;
F->lastPersistBar=Bar;return 1;
}
int effectiveExit()
{
if(ENGINE_MODE==0 && (RESEARCH_ID==18 || RESEARCH_ID==19))return 2;
if(ENGINE_MODE==0 && RESEARCH_ID==34)return 1;
return EXIT_RULE_ID;
}
var exitTarget(int id,int side,var entry,int rule)
{
FXSTATE* F=&FX[id];
var v=F->v,sd=F->sigma,r=F->range;
if(rule==2) {if(side<0)return F->band[4];return F->band[2];}
if(rule==3) {if(side<0)return F->band[5];return F->band[1];}
if(rule==4) {if(side<0)return F->band[2];return F->band[4];}
if(rule==5) {if(side<0)return F->band[1];return F->band[5];}
if(rule==6) {if(side<0)return F->band[0];return F->band[6];}
if(rule==7)return (entry+v)*0.5;
if(rule==8)return entry+side*EXIT_SIGMA_MULTIPLE*sd;
if(rule==9)return entry+side*EXIT_RANGE_MULTIPLE*r;
if(rule==10)return v+side*EXIT_RANGE_PERCENT*0.01*r;
return v;
}
int crossedExit(int side,var entry,var price,var target,int allowAdverse)
{
if(side<0) {
if(target<entry)return price<=target;
return allowAdverse && price>=target;
}
if(target>entry)return price>=target;
return allowAdverse && price<=target;
}
int managePosition(int id,int newBar)
{
FXSTATE* F=&FX[id];
int rule=effectiveExit();
int wanted=0;
// for(current_trades) loops only current Asset AND Algo.
for(current_trades) {
if(!TradeIsOpen || TradeIsPhantom)continue;
int side=1;
if(TradeIsShort)side=-1;
var entry=TradePriceOpen; // Zorro's ask-based entry reference
var ask=AskPrice,bid=BidPrice;
var target=exitTarget(id,side,entry,rule);
if(ENGINE_MODE==0 && RESEARCH_ID==34 && F->frozenCenter>0)
target=F->frozenCenter;
wanted=0;
if(rule>=1 && rule<=15) {
if(rule!=15 || TradeBars>=EXIT_WAIT_BARS)
if(side<0)wanted=crossedExit(side,entry,ask,target,rule==14);
else wanted=crossedExit(side,entry,bid,target,rule==14);
}
if(rule==16)wanted=(TradeBars*TIMEFRAME_MINUTES>=EXIT_DEADLINE_MINUTES);
if(rule==17 && newBar)wanted=(F->dir==-side);
if(rule==18)wanted=(F->dir==-side && F->phase==PH_READY);
if(rule==19) {
if(!F->partialDone) {
var first=exitTarget(id,side,entry,2);
int hitFirst=0;
if(side<0)hitFirst=crossedExit(side,entry,ask,first,0);
else hitFirst=crossedExit(side,entry,bid,first,0);
if(hitFirst) {
int partial=(int)(TradeLots*PARTIAL_PERCENT/100.);
if(partial>=1 && TradeLots-partial>=1) {
if(exitTrade(ThisTrade,0,partial))F->partialDone=1;
} else F->partialDone=1;
}
}
if(F->partialDone)
{ if(side<0)wanted=crossedExit(side,entry,ask,F->v,0);
else wanted=crossedExit(side,entry,bid,F->v,0); }
}
if(rule==20) {
var gap=TRAIL_SIGMA_MULTIPLE*F->sigma;
if(F->highWater==0 || bid>F->highWater)F->highWater=bid;
if(F->lowWater==0 || ask<F->lowWater)F->lowWater=ask;
if(side>0 && F->highWater>entry+gap)
wanted=(bid<=max(F->v-gap,F->highWater-gap));
if(side<0 && F->lowWater<entry-gap)
wanted=(ask>=min(F->v+gap,F->lowWater+gap));
}
if(wanted && ENABLE_SOFTWARE_EXIT)
exitTrade(ThisTrade); // auto-retry on subsequent quote if still open
}
return wanted;
}
int countPortfolio()
{
int n=0;
for(open_trades)
if(TradeIsOpen && !TradeIsPhantom && !strcmp(TradeAlgo,"MK780"))n++;
return n;
}
int entryEligible(int id,int side,var ask)
{
if(!FX[id].valid || FX[id].range<=0)return 0;
if(NumOpenLong+NumOpenShort+NumPendingLong+NumPendingShort>0)return 0;
if(countPortfolio()>=MAX_PORTFOLIO_POSITIONS)return 0;
var spreadLimit=MAX_SPREAD_PIPS;
if(spreadLimit>0 && Spread>spreadLimit*PIP)return 0;
if(side<0 && protectiveStop(id,side)<=ask+PIP)return 0;
if(side>0 && protectiveStop(id,side)>=ask-Spread-PIP)return 0;
return 1;
}
int placeSignal(int id,int side,var ask)
{
FXSTATE* F=&FX[id];
if(!entryEligible(id,side,ask))return 0;
var stop=protectiveStop(id,side);
var trigger=0;
if(ENTRY_STYLE==1) {
if(side<0)trigger=F->h-PENDING_PERCENT*0.01*F->range;
else trigger=F->l+PENDING_PERCENT*0.01*F->range;
// Zorro Entry is always an ASK price; SELL stop needs ask conversion.
// Current Spread is only an estimate of future spread at execution.
if(side<0)trigger+=Spread;
if(side<0 && !(trigger<ask-PIP))return 0;
if(side>0 && !(trigger>ask+PIP))return 0;
}
Entry=trigger;
Stop=stop; // absolute ASK-level; original MQL broker stops may differ
TakeProfit=0; // software VWAP exits, do not set a wrong-side TP
StopFactor=0;
if(ENABLE_BROKER_STOP)StopFactor=1;
EntryTime=ENTRY_WAIT_BARS;
MaxLong=1;MaxShort=1;
Lots=ORDER_LOTS;
TRADE* tr=0;
if(side<0)tr=enterShort();
else tr=enterLong();
Entry=0;Stop=0;TakeProfit=0;
if(!tr)return 0;
F->activeBefore=1;
F->frozenCenter=F->v;
F->highWater=0;F->lowWater=0;
F->partialDone=0;
F->lastEntryBar=Bar;
F->lastEntryLevel=trigger;
F->entryStamp=clockSeconds();
resetPhase(id);
return 1;
}
void cancelOldPending()
{
// Called after a new completed candle (reference levels changed).
for(current_trades) {
if(TradeIsPending)exitTrade(ThisTrade);
}
}
void onQuote(int id,int newBar)
{
FXSTATE* F=&FX[id];
if(!F->usable || !F->valid || F->signature<0)return;
// Set the current asset/algo context before invoking trade statistics/functions.
if(!asset(F->symbol))return;
algo("MK780");
var ask=AskPrice;
if(ask<=0)return;
var spreadLimit=MAX_SPREAD_PIPS;
if(spreadLimit>0 && Spread>spreadLimit*PIP)return;
int present=NumOpenLong+NumOpenShort+NumPendingLong+NumPendingShort;
if(F->activeBefore && present==0) {
resetPhase(id);
F->quoteKnown=0;F->activeBefore=0;
F->partialDone=0;F->frozenCenter=0;
}
if(present>0)F->activeBefore=1;
int observed=F->quoteKnown && abs(ask-F->previousAsk)>PIP*0.001;
var prev=F->previousAsk;
F->previousAsk=ask;
F->quoteKnown=1;
if(observed && (present==0 || effectiveExit()==18))
sweepEvent(id,prev,ask,observed);
int cat=0;
if(ENGINE_MODE==1)cat=catalogEvent(id,prev,ask,observed,newBar);
if(NumOpenLong+NumOpenShort>0) {
if(ENGINE_MODE==1 && cat && CATALOG_ID%3==2) {
exitLong();exitShort();
return;
}
managePosition(id,newBar);
return;
}
if(NumPendingLong+NumPendingShort>0)return;
if(is(LOOKBACK))return;
if(SIGNATURE_FILTER>=0 && F->signature!=SIGNATURE_FILTER)return;
if(!anatomy(id,CANDLE_RULE_ID,ask))return;
if(ENGINE_MODE==3 && !MARKOV_NEED_SWEEP) {
if(F->lastEntryBar==Bar)return;
int decision=markovDecision(id);
if(decision && passMarkov(id,decision))placeSignal(id,decision,ask);
return;
}
if(ENGINE_MODE==1) {
if(!cat || CATALOG_ID%3==2)return;
int side=-1;
if(CATALOG_ID%3==0)side=1;
if(passMarkov(id,side))placeSignal(id,side,ask);
return;
}
if(F->phase!=PH_READY || F->dir==0)return;
int side=F->dir;
if(!passMarkov(id,side))return;
if(ENGINE_MODE==0 && !researchGate(id,RESEARCH_ID,side,ask,ask))return;
if(ENGINE_MODE==2) {
if(side<0 && (!bandRule(id,side,SELL_STATIC_ID,ask,ask) ||
!confirmation(id,side,SELL_EVENT_ID,ask)))return;
if(side>0 && (!bandRule(id,side,BUY_STATIC_ID,ask,ask) ||
!confirmation(id,side,BUY_EVENT_ID,ask)))return;
}
placeSignal(id,side,ask);
}
// Zorro run() is called after each completed bar. In run, [0] is the
// finished bar; in tick, [0] is still changing, and must NOT update VWAP.
void run()
{
if(is(INITRUN)) {
initForexNames();
BarPeriod=TIMEFRAME_MINUTES;
LookBack=MK_WINDOW+VWAP_PERIOD+40;
set(TICKS);
TickTime=0;
MaxLong=1;MaxShort=1;
ActivePairs=MAX_PAIRS;
if(SINGLE_ASSET)ActivePairs=1;
if(!initMarkov()) {quit("!Cannot allocate 780-state Markov tables");return;}
int loaded=assetList(ASSET_LIST);
if(!loaded) {quit("!Asset list unavailable");return;}
int i;
for(i=0;i<ActivePairs;i++) {
FX[i].symbol=ForexNames[i];
if(SINGLE_ASSET)FX[i].symbol=SINGLE_ASSET_NAME;
FX[i].usable=0;FX[i].signature=-1;FX[i].lastEntryBar=-1;
FX[i].lastPersistBar=-1;FX[i].ringHead=0;FX[i].ringSize=0;
resetPhase(i);
// Assets must be selected in INITRUN to load/subscribed histories.
if(asset(FX[i].symbol)) {
FX[i].usable=1;
algo("MK780");
} else
printf("\nUnavailable asset: %s (check %s)",FX[i].symbol,ASSET_LIST);
}
return;
}
if(is(EXITRUN)) {releaseMarkov();return;}
int i;
for(i=0;i<ActivePairs;i++) {
if(!FX[i].usable)continue;
if(!asset(FX[i].symbol))continue;
algo("MK780");
// reference bar data + new Markov state, no future data or files.
completeCandle(i);
if(!is(LOOKBACK)) {
cancelOldPending(); // new candle invalidates prior candle's STOP level
onQuote(i,1); // completed-candle rejection and X17 exit
}
}
}
void tick()
{
if(is(INITRUN) || is(EXITRUN))return;
// tick() is called for the subscribed asset that just got a new quote.
int id=findFX(Asset);
if(id<0)return;
onQuote(id,0);
}
|
|
|
Term Structure Covariation Strategy
[Re: TipmyPip]
#489722
Yesterday at 16:52
Yesterday at 16:52
|
Joined: Sep 2017
Posts: 346
TipmyPip
OP
Senior Member
|
OP
Senior Member
Joined: Sep 2017
Posts: 346
|
Term Structure Covariation Strategy The strategy can be understood as a sequence of lenses, filters, and gates. Each stage transforms ordinary Forex prices into a structured picture of market movement, then decides whether that picture permits a trade. The observation window A moving window collects closing prices from completed candles. The prices are arranged from oldest to newest, preventing unfinished bars from influencing the analysis. Logarithmic price changes describe movement proportionally rather than through raw price differences. The maturity lenses The strategy views each price change through several synthetic maturity lenses. Shorter and longer maturity intervals receive different amplification weights. These lenses do not observe separate financial instruments. They reshape the same Forex return into several related channels, creating an artificial maturity structure rather than measuring an actual bond yield curve. The noise filter The analyzer measures the combined strength of each observation across those channels. It sorts these measurements and uses the quieter portion of the sample to estimate a preliminary background scale. An adjustable truncation setting converts that scale into a boundary. Ordinary observations remain inside the boundary, while unusually large movements fall outside it and become potential jumps. The two market portraits The strategy constructs two covariation portraits. The first includes every observation and represents total measured movement. The second excludes observations beyond the truncation boundary and represents movement after large shocks have been filtered out. Both portraits retain the original sample length when averaging. Filtering therefore removes contributions without increasing the weight of the remaining observations. The factor prism A Jacobi decomposition separates the filtered portrait into principal directions and their relative strengths. The strongest direction becomes the first factor. Additional factors are counted until their combined contribution reaches the configured explanation target. The analyzer also calculates a matrix magnitude called the Hilbert Schmidt norm. Despite its volatility label in the original implementation, this quantity measures covariance magnitude, not conventional annualized volatility. The trading compass The first factor passes through a maturity weighted scoring rule. A sufficiently positive score requests a purchase. A sufficiently negative score requests a sale. Scores inside the decision boundary remain neutral. An optional setting adjusts that boundary according to the covariance magnitude. The protection gates Before entry, the strategy checks drawdown, factor count, underlying movement, existing positions, and signal spacing. An opposing position must close before reversal. Order size follows either a fixed Forex volume or a stop based risk budget, subject to available margin. Stop loss, take profit, and stepped trailing protection manage exits. The essential warning Every synthetic channel originates from the same return stream. Consequently, the unregularized structure contains only one independent direction. Its orientation does not establish economic buying or selling meaning. The preserved trading compass is therefore experimental rather than a validated forecast. // Standalone single-file edition. Requires only standard Zorro includes.
// MQL5 TermStructureCovariationEA -> Zorro 32-bit lite-C.
#include <default.c>
// ============================================================================
// ANALYSIS LIBRARY
// ============================================================================
// ===== Embedded TermStructureLib.h =====
#ifndef TERM_STRUCTURE_LIB_H
#define TERM_STRUCTURE_LIB_H
// Zorro 32-bit lite-C. Singleton analyzer; all matrices are row-major.
// Forex spot proxy, NOT observations of a bond term structure.
#define TS_MAX_MATURITIES 16
#define TS_MAX_PAIRS 15
#define TS_MATRIX_SIZE 225
#define TS_MAX_PRICES 2048
#define TS_RETURN_SIZE 30705
int TS_Maturities[TS_MAX_MATURITIES];
int TS_MaturityCount = 0;
int TS_Lookback = 250;
int TS_TimeSteps = 0;
int TS_Pairs = 0;
int TS_Initialized = 0;
int TS_NumFactors = 0;
int TS_JumpCount = 0;
int TS_KeptCount = 0;
int TS_Debug = 0;
int TS_DataDriven = 1;
int TS_Regularize = 1;
var TS_Alpha = 3.;
var TS_VarianceTarget = 0.85;
var TS_PreliminaryVariance = 0.;
var TS_Threshold = 0.;
var TS_IntegratedVolatility = 0.;
var TS_RealizedTrace = 0.;
var TS_Returns[TS_RETURN_SIZE];
var TS_Norms[TS_MAX_PRICES];
var TS_SortedNorms[TS_MAX_PRICES];
var TS_LogPrices[TS_MAX_PRICES];
var TS_Realized[TS_MATRIX_SIZE];
var TS_Truncated[TS_MATRIX_SIZE];
var TS_Eigenvalues[TS_MAX_PAIRS];
var TS_ExplainedVariance[TS_MAX_PAIRS];
var TS_Eigenvectors[TS_MATRIX_SIZE];
var TS_WorkMatrix[TS_MATRIX_SIZE];
var TS_RawEigenvalues[TS_MAX_PAIRS];
void TS_ResetResults()
{
TS_Initialized = 0;
TS_NumFactors = 0;
TS_TimeSteps = 0;
TS_Pairs = 0;
TS_JumpCount = 0;
TS_KeptCount = 0;
TS_PreliminaryVariance = 0.;
TS_Threshold = 0.;
TS_IntegratedVolatility = 0.;
TS_RealizedTrace = 0.;
}
int TS_SetMaturities(int *Tenors, int TenorCount)
{
int K;
TS_ResetResults();
if (TenorCount < 2 || TenorCount > TS_MAX_MATURITIES)
{
return 0;
}
for (K = 0; K < TenorCount; K++)
{
if (Tenors[K] <= 0)
{
return 0;
}
if (K > 0 && Tenors[K] <= Tenors[K - 1])
{
return 0;
}
}
for (K = 0; K < TenorCount; K++)
{
TS_Maturities[K] = Tenors[K];
}
TS_MaturityCount = TenorCount;
return 1;
}
var TS_MatrixTrace(vars MatrixData, int Dimension)
{
int K;
var Total = 0.;
for (K = 0; K < Dimension; K++)
{
Total += MatrixData[K * Dimension + K];
}
return Total;
}
var TS_FrobeniusNorm(vars MatrixData, int Dimension)
{
int K;
var Total = 0.;
for (K = 0; K < Dimension * Dimension; K++)
{
Total += MatrixData[K] * MatrixData[K];
}
return sqrt(Total);
}
// Symmetric Jacobi solver: columns of Vectors are orthonormal eigenvectors.
// Unlike the source, non-convergence is a failure, not a successful result.
int TS_EigenDecomposition(vars MatrixData, int Dimension, vars Values, vars Vectors)
{
int I, J, K, Iteration, P, Q, MaxIndex, Converged;
var Scale, Tolerance, Largest, App, Aqq, Apq;
var Theta, Rotation, Cosine, Sine, Aip, Aiq, Vip, Viq, Temp;
if (Dimension < 1 || Dimension > TS_MAX_PAIRS)
{
return 0;
}
Scale = 0.;
for (I = 0; I < Dimension * Dimension; I++)
{
TS_WorkMatrix[I] = MatrixData[I];
Vectors[I] = 0.;
Scale = max(Scale, abs(MatrixData[I]));
}
for (I = 0; I < Dimension; I++)
{
Vectors[I * Dimension + I] = 1.;
}
Tolerance = max(1.e-30, Scale * 1.e-12);
Converged = 0;
for (Iteration = 0; Iteration < 150 * Dimension * Dimension; Iteration++)
{
Largest = 0.;
P = 0;
Q = 0;
for (I = 0; I < Dimension; I++)
{
for (J = I + 1; J < Dimension; J++)
{
Temp = abs(TS_WorkMatrix[I * Dimension + J]);
if (Temp > Largest)
{
Largest = Temp;
P = I;
Q = J;
}
}
}
if (Largest <= Tolerance)
{
Converged = 1;
break;
}
App = TS_WorkMatrix[P * Dimension + P];
Aqq = TS_WorkMatrix[Q * Dimension + Q];
Apq = TS_WorkMatrix[P * Dimension + Q];
Theta = (Aqq - App) / (2. * Apq);
// Stable equivalent to sign(theta)/(abs(theta)+sqrt(1+theta^2)).
if (abs(Theta) > 1.e100)
{
Rotation = 0.5 / Theta;
}
else
{
Rotation = 1. / (abs(Theta) + sqrt(1. + Theta * Theta));
if (Theta < 0.)
{
Rotation = -Rotation;
}
}
Cosine = 1. / sqrt(1. + Rotation * Rotation);
Sine = Rotation * Cosine;
TS_WorkMatrix[P * Dimension + P] = App - Rotation * Apq;
TS_WorkMatrix[Q * Dimension + Q] = Aqq + Rotation * Apq;
TS_WorkMatrix[P * Dimension + Q] = 0.;
TS_WorkMatrix[Q * Dimension + P] = 0.;
for (I = 0; I < Dimension; I++)
{
if (I != P && I != Q)
{
Aip = TS_WorkMatrix[I * Dimension + P];
Aiq = TS_WorkMatrix[I * Dimension + Q];
TS_WorkMatrix[I * Dimension + P] = Cosine * Aip - Sine * Aiq;
TS_WorkMatrix[P * Dimension + I] = TS_WorkMatrix[I * Dimension + P];
TS_WorkMatrix[I * Dimension + Q] = Sine * Aip + Cosine * Aiq;
TS_WorkMatrix[Q * Dimension + I] = TS_WorkMatrix[I * Dimension + Q];
}
Vip = Vectors[I * Dimension + P];
Viq = Vectors[I * Dimension + Q];
Vectors[I * Dimension + P] = Cosine * Vip - Sine * Viq;
Vectors[I * Dimension + Q] = Sine * Vip + Cosine * Viq;
}
}
if (!Converged)
{
return 0;
}
for (I = 0; I < Dimension; I++)
{
Values[I] = TS_WorkMatrix[I * Dimension + I];
}
for (I = 0; I < Dimension - 1; I++)
{
MaxIndex = I;
for (J = I + 1; J < Dimension; J++)
{
if (abs(Values[J]) > abs(Values[MaxIndex]))
{
MaxIndex = J;
}
}
if (MaxIndex != I)
{
Temp = Values[I];
Values[I] = Values[MaxIndex];
Values[MaxIndex] = Temp;
for (K = 0; K < Dimension; K++)
{
Temp = Vectors[K * Dimension + I];
Vectors[K * Dimension + I] = Vectors[K * Dimension + MaxIndex];
Vectors[K * Dimension + MaxIndex] = Temp;
}
}
}
return 1;
}
int TS_ComputePreliminaryEstimate()
{
int N, J, K, ValidSamples, CutoffIndex;
var Temporary, Quantile, TraceSum;
for (N = 0; N < TS_TimeSteps; N++)
{
TS_SortedNorms[N] = TS_Norms[N];
}
// Insertion sort: no dependency on MQL ArraySort or C qsort callbacks.
for (N = 1; N < TS_TimeSteps; N++)
{
Temporary = TS_SortedNorms[N];
K = N - 1;
while (K >= 0)
{
if (TS_SortedNorms[K] <= Temporary)
{
break;
}
TS_SortedNorms[K + 1] = TS_SortedNorms[K];
K--;
}
TS_SortedNorms[K + 1] = Temporary;
}
CutoffIndex = (int)(TS_TimeSteps * 0.75);
Quantile = TS_SortedNorms[CutoffIndex];
ValidSamples = 0;
TraceSum = 0.;
// Only the trace is needed; equivalent to constructing prelimCov.
for (N = 0; N < TS_TimeSteps; N++)
{
if (TS_Norms[N] <= Quantile)
{
ValidSamples++;
for (J = 0; J < TS_Pairs; J++)
{
Temporary = TS_Returns[N * TS_Pairs + J];
TraceSum += Temporary * Temporary;
}
}
}
TS_PreliminaryVariance = 0.;
if (ValidSamples > 0)
{
TS_PreliminaryVariance = TraceSum / (ValidSamples * TS_Pairs);
}
return TS_PreliminaryVariance > 1.e-15;
}
// Prices must be oldest -> newest. Uses only the last TS_Lookback prices.
int TS_ComputeCovariations(vars Prices, int PriceCount)
{
int I, J, N, Offset, PreliminaryOK;
var LogReturn, Amplification, SumSquares, Product, Total, Cumulative;
TS_ResetResults();
if (TS_Lookback < 2 || TS_Lookback > TS_MAX_PRICES)
{
return 0;
}
if (PriceCount < TS_Lookback || TS_MaturityCount < 2)
{
return 0;
}
if (TS_Alpha <= 0. || TS_VarianceTarget <= 0. || TS_VarianceTarget > 1.)
{
return 0;
}
Offset = PriceCount - TS_Lookback;
for (I = 0; I < TS_Lookback; I++)
{
// Comparisons also reject NaN; upper bound prevents overflow inputs.
if (!(Prices[Offset + I] > 0.) || Prices[Offset + I] > 1.e100)
{
return 0;
}
TS_LogPrices[I] = log(Prices[Offset + I]);
}
TS_TimeSteps = TS_Lookback - 1;
TS_Pairs = TS_MaturityCount - 1;
for (N = 0; N < TS_TimeSteps; N++)
{
LogReturn = TS_LogPrices[N + 1] - TS_LogPrices[N];
SumSquares = 0.;
for (J = 0; J < TS_Pairs; J++)
{
Amplification = sqrt((TS_Maturities[J + 1] - TS_Maturities[J]) / 30.) * (J + 1) * 2.;
Product = LogReturn * Amplification;
TS_Returns[N * TS_Pairs + J] = Product;
SumSquares += Product * Product;
}
TS_Norms[N] = sqrt(SumSquares);
}
PreliminaryOK = TS_ComputePreliminaryEstimate();
TS_Threshold = TS_Alpha * sqrt(TS_PreliminaryVariance);
if (TS_DataDriven && PreliminaryOK)
{
TS_Threshold *= sqrt(TS_Pairs);
}
// When DataDriven=0 use alpha*sqrt(preliminaryVariance), in norm units.
for (N = 0; N < TS_TimeSteps; N++)
{
if (TS_Norms[N] < TS_Threshold)
{
TS_KeptCount++;
}
if (TS_Norms[N] > TS_Threshold)
{
TS_JumpCount++;
}
}
for (I = 0; I < TS_Pairs; I++)
{
for (J = I; J < TS_Pairs; J++)
{
Total = 0.;
Cumulative = 0.;
for (N = 0; N < TS_TimeSteps; N++)
{
Product = TS_Returns[N * TS_Pairs + I] * TS_Returns[N * TS_Pairs + J];
Total += Product;
if (TS_Norms[N] < TS_Threshold)
{
Cumulative += Product;
}
}
TS_Realized[I * TS_Pairs + J] = Total / TS_TimeSteps;
TS_Realized[J * TS_Pairs + I] = Total / TS_TimeSteps;
TS_Truncated[I * TS_Pairs + J] = Cumulative / TS_TimeSteps;
TS_Truncated[J * TS_Pairs + I] = Cumulative / TS_TimeSteps;
}
}
TS_RealizedTrace = TS_MatrixTrace(TS_Realized, TS_Pairs);
if (TS_Regularize && TS_MatrixTrace(TS_Truncated, TS_Pairs) < 1.e-12)
{
for (I = 0; I < TS_Pairs; I++)
{
TS_Truncated[I * TS_Pairs + I] += 1.e-9;
}
}
if (!TS_EigenDecomposition(TS_Truncated, TS_Pairs, TS_RawEigenvalues, TS_Eigenvectors))
{
return 0;
}
TS_IntegratedVolatility = TS_FrobeniusNorm(TS_Truncated, TS_Pairs);
Total = 0.;
for (I = 0; I < TS_Pairs; I++)
{
Total += abs(TS_RawEigenvalues[I]);
}
if (Total < 1.e-15)
{
return 0;
}
Cumulative = 0.;
for (I = 0; I < TS_Pairs; I++)
{
TS_Eigenvalues[I] = abs(TS_RawEigenvalues[I]) / Total;
Cumulative += TS_Eigenvalues[I];
TS_ExplainedVariance[I] = Cumulative;
if (TS_NumFactors == 0 && Cumulative + 1.e-14 >= TS_VarianceTarget)
{
TS_NumFactors = I + 1;
}
}
if (TS_NumFactors == 0)
{
TS_NumFactors = TS_Pairs;
}
TS_Initialized = 1;
if (TS_Debug)
{
printf("\nTS: HS=%.10g factors=%d kept=%d/%d jumps=%d threshold=%.8g",
TS_IntegratedVolatility, TS_NumFactors, TS_KeptCount, TS_TimeSteps, TS_JumpCount,
TS_Threshold);
}
return 1;
}
var TS_GetIntegratedVolatility()
{
if (!TS_Initialized)
{
return 0.;
}
return TS_IntegratedVolatility;
}
int TS_GetNumberOfFactors()
{
if (!TS_Initialized)
{
return 0;
}
return TS_NumFactors;
}
int TS_GetEigenvalues(vars Destination, int Capacity)
{
int K;
if (!TS_Initialized || Capacity < TS_Pairs)
{
return 0;
}
for (K = 0; K < TS_Pairs; K++)
{
Destination[K] = TS_Eigenvalues[K];
}
return TS_Pairs;
}
int TS_GetFactorLoadings(int FactorIndex, vars Destination, int Capacity)
{
int K;
if (!TS_Initialized || FactorIndex < 0 || FactorIndex >= TS_Pairs)
{
return 0;
}
if (Capacity < TS_Pairs)
{
return 0;
}
for (K = 0; K < TS_Pairs; K++)
{
Destination[K] = TS_Eigenvectors[K * TS_Pairs + FactorIndex];
}
return TS_Pairs;
}
var TS_GetExplainedVariance(int FactorCount)
{
if (!TS_Initialized || FactorCount <= 0 || FactorCount > TS_Pairs)
{
return 0.;
}
return TS_ExplainedVariance[FactorCount - 1];
}
// Returns count; -1 = invalid/uninitialized/insufficient output capacity.
// Indices are zero-based return indices in the oldest -> newest window.
int TS_DetectJumps(vars Indices, vars Sizes, int Capacity)
{
int N, K;
if (!TS_Initialized || Capacity < TS_JumpCount)
{
return -1;
}
K = 0;
for (N = 0; N < TS_TimeSteps; N++)
{
if (TS_Norms[N] > TS_Threshold)
{
Indices[K] = N;
Sizes[K] = TS_Norms[N];
K++;
}
}
return K;
}
void TS_PrintDiagnostics()
{
int K;
if (!TS_Initialized)
{
printf("\nTS: analyzer not initialized");
return;
}
printf("\nTERM STRUCTURE SPOT-PROXY DIAGNOSTICS");
printf("\nSteps=%d pairs=%d HS=%.10g factors=%d target=%.1f%%", TS_TimeSteps, TS_Pairs,
TS_IntegratedVolatility, TS_NumFactors, TS_VarianceTarget * 100.);
printf("\nPreliminary=%.10g threshold=%.10g jumps=%d excluded=%d", TS_PreliminaryVariance,
TS_Threshold, TS_JumpCount, TS_TimeSteps - TS_KeptCount);
for (K = 0; K < TS_Pairs; K++)
{
printf("\n lambda[%d]=%.5f%% cumulative=%.5f%%", K, TS_Eigenvalues[K] * 100.,
TS_ExplainedVariance[K] * 100.);
}
}
#endif
// ===== End TermStructureLib.h =====
// ============================================================================
// STRATEGY CONFIGURATION
// ============================================================================
// Configuration. Forex maturity proxy is preserved from the supplied EA.
string InpAsset = "EUR/USD";
int InpBarPeriod = 60;
int InpStartDate = 20240101;
int InpEndDate = 20241231;
int InpMaturity1 = 30;
int InpMaturity2 = 90;
int InpMaturity3 = 180;
int InpMaturity4 = 360;
int InpLookbackBars = 100;
var InpTruncLevel = 3.;
var InpVarianceThreshold = 0.85;
int InpUseDataDrivenTruncation = 1;
int InpRegularizeSmallTrace = 1;
var InpSignalThreshold = 0.000001;
int InpUseVolatilityScaling = 0;
var InpVolatilityScaleFactor = 3.;
int InpMinBarsBetweenSignals = 1;
int InpMinFactorsRequired = 1;
var InpLotSize = 0.1; // MT5 standard Forex lots, 100000 units each.
int InpUseAdaptiveLots = 0;
var InpRiskPercent = 2.;
var InpInitialCapital = 10000.;
var InpMaxDrawdownPercent = 15.;
var InpStopLossPips = 50.;
var InpTakeProfitPips = 125.;
var InpRiskRewardRatio = 2.5;
int InpUseTrailingStop = 1;
var InpTrailingStopPips = 40.;
var InpTrailingStepPips = 10.;
// Zorro plots replace MT5 objects; native trade markers show fills/exits.
int InpShowPanel = 1;
int InpShowSignals = 1;
int InpShowDebugLogs = 0;
int InpShowDiagnostics = 0;
int InpBuyColor = GREEN;
int InpSellColor = RED;
var TS_InputPrices[TS_MAX_PRICES];
var TS_FirstFactor[TS_MAX_PAIRS];
int TS_ConfigMaturities[4];
int TS_LastTradeBar = -1000000;
int TS_TotalSignals = 0;
int TS_WinningTrades = 0;
int TS_LosingTrades = 0;
var TS_CurrentDrawdown = 0.;
var TS_SlopeFactor = 0.;
// ============================================================================
// CONFIGURATION VALIDATION AND INITIALIZATION
// ============================================================================
int TS_Configure()
{
if (InpLookbackBars < 2 || InpLookbackBars > TS_MAX_PRICES)
{
return 0;
}
if (InpTruncLevel <= 0. || InpVarianceThreshold <= 0. || InpVarianceThreshold > 1.)
{
return 0;
}
if (InpLotSize <= 0. || InpInitialCapital <= 0. || InpBarPeriod <= 0)
{
return 0;
}
if (InpRiskPercent <= 0. || InpRiskPercent > 100. || InpRiskRewardRatio <= 0.)
{
return 0;
}
if (InpMinFactorsRequired < 1 || InpMinFactorsRequired > 3)
{
return 0;
}
if (InpMinBarsBetweenSignals < 0 || InpSignalThreshold <= 0.)
{
return 0;
}
if (InpMaxDrawdownPercent < 0. || InpVolatilityScaleFactor < 0.)
{
return 0;
}
if (InpUseTrailingStop && (InpTrailingStopPips <= 0. || InpTrailingStepPips < 0.))
{
return 0;
}
TS_ConfigMaturities[0] = InpMaturity1;
TS_ConfigMaturities[1] = InpMaturity2;
TS_ConfigMaturities[2] = InpMaturity3;
TS_ConfigMaturities[3] = InpMaturity4;
if (!TS_SetMaturities(TS_ConfigMaturities, 4))
{
return 0;
}
TS_Lookback = InpLookbackBars;
TS_Alpha = InpTruncLevel;
TS_VarianceTarget = InpVarianceThreshold;
TS_DataDriven = InpUseDataDrivenTruncation;
TS_Regularize = InpRegularizeSmallTrace;
TS_Debug = InpShowDebugLogs;
TS_LastTradeBar = -1000000;
TS_TotalSignals = 0;
TS_WinningTrades = 0;
TS_LosingTrades = 0;
return 1;
}
// ============================================================================
// TRADE MANAGEMENT
// ============================================================================
// Called by Zorro for price updates and closures, including SL/TP exits.
int TS_ManageTrade()
{
var ExitPrice, Candidate, Distance, StepDistance;
if (TradeIsClosed)
{
if (TradeProfit > 0.)
{
TS_WinningTrades++;
}
if (TradeProfit < 0.)
{
TS_LosingTrades++;
}
return 0;
}
if (!TradeIsOpen || !InpUseTrailingStop || InpStopLossPips <= 0.)
{
return 0;
}
if (TradeStopLimit == 0.)
{
return 0;
}
Distance = InpTrailingStopPips * PIP;
StepDistance = InpTrailingStepPips * PIP;
ExitPrice = TradePriceClose;
if (TradeIsLong)
{
// Zorro prices are ask; long exits use bid, short exits use ask.
ExitPrice -= Spread;
Candidate = roundto(ExitPrice - Distance, PIP / 10.);
if (ExitPrice > TradePriceOpen + Distance && Candidate > TradeStopLimit + StepDistance)
{
TradeStopLimit = Candidate;
}
}
else
{
Candidate = roundto(ExitPrice + Distance, PIP / 10.);
if (ExitPrice < TradePriceOpen - Distance && Candidate < TradeStopLimit - StepDistance)
{
TradeStopLimit = Candidate;
}
}
return 0;
}
// ============================================================================
// ANALYSIS UPDATE AND SIGNAL GENERATION
// ============================================================================
int TS_UpdateAnalysis()
{
int K;
// Only completed candles, newest at index N-1. No MQL series-flag reversal.
for (K = 0; K < InpLookbackBars; K++)
{
TS_InputPrices[K] = priceClose(InpLookbackBars - K);
}
return TS_ComputeCovariations(TS_InputPrices, InpLookbackBars);
}
var TS_CalculateDrawdown()
{
if (Balance <= 0.)
{
return 0.;
}
return max(0., 100. * (Balance - Equity) / Balance);
}
int TS_CheckTradingConditions()
{
TS_CurrentDrawdown = TS_CalculateDrawdown();
if (TS_CurrentDrawdown > InpMaxDrawdownPercent)
{
return 0;
}
if (TS_GetNumberOfFactors() < InpMinFactorsRequired)
{
return 0;
}
// Prevent regularization from manufacturing a tradable flat-price signal.
if (TS_RealizedTrace <= 1.e-20)
{
return 0;
}
if (TS_GetIntegratedVolatility() < 1.e-10)
{
return 0;
}
return 1;
}
int TS_GenerateSignal()
{
int K, Count;
var WeightSum, Threshold;
Count = TS_GetFactorLoadings(0, TS_FirstFactor, TS_MAX_PAIRS);
if (Count < 2)
{
return 0;
}
TS_SlopeFactor = 0.;
WeightSum = 0.;
for (K = 0; K < Count; K++)
{
TS_SlopeFactor += TS_FirstFactor[K] * (K + 1.);
WeightSum += K + 1.;
}
TS_SlopeFactor /= WeightSum;
Threshold = InpSignalThreshold;
if (InpUseVolatilityScaling)
{
Threshold = max(Threshold, TS_GetIntegratedVolatility() * InpVolatilityScaleFactor);
}
if (InpShowDebugLogs)
{
printf("\nTS slope=%.8g threshold=%.8g", TS_SlopeFactor, Threshold);
}
// Compatibility only: eigenvector signs are not economic directions.
if (TS_SlopeFactor > Threshold)
{
return 1;
}
if (TS_SlopeFactor < -Threshold)
{
return -1;
}
return 0;
}
int TS_ShouldTakeSignal(int Direction)
{
if (Direction > 0 && NumOpenLong > 0)
{
return 0;
}
if (Direction < 0 && NumOpenShort > 0)
{
return 0;
}
// The source permits reversal immediately, bypassing the cooldown.
if (Direction > 0 && NumOpenShort > 0)
{
return 1;
}
if (Direction < 0 && NumOpenLong > 0)
{
return 1;
}
return Bar - TS_LastTradeBar >= InpMinBarsBetweenSignals;
}
// ============================================================================
// ORDER EXECUTION AND RISK MANAGEMENT
// ============================================================================
int TS_ExecuteTrade(int Direction)
{
int OrderLots;
var StopDistance, ProfitDistance, Available, RiskPerLot, LotCount;
TRADE *Entered;
if (Direction > 0)
{
if (NumOpenShort > 0)
{
exitShort();
}
if (NumOpenShort > 0)
{
return 0; // Do not hedge a pending/failed reversal.
}
if (NumOpenLong > 0)
{
return 0;
}
}
else
{
if (NumOpenLong > 0)
{
exitLong();
}
if (NumOpenLong > 0)
{
return 0;
}
if (NumOpenShort > 0)
{
return 0;
}
}
if (PIP <= 0. || LotAmount <= 0. || MarginCost <= 0.)
{
return 0;
}
StopDistance = InpStopLossPips * PIP;
if (InpStopLossPips <= 0.)
{
StopDistance = max(TS_GetIntegratedVolatility() * 3000. * PIP / 10., 30. * PIP);
}
ProfitDistance = InpTakeProfitPips * PIP;
if (InpTakeProfitPips <= 0.)
{
ProfitDistance = StopDistance * InpRiskRewardRatio;
}
LotCount = floor(InpLotSize * 100000. / LotAmount + 1.e-9);
if (InpUseAdaptiveLots)
{
if (PIPCost <= 0.)
{
return 0;
}
// Use the ACTUAL stop (plus spread/commission), not a different proxy SL.
RiskPerLot = (StopDistance + Spread) / PIP * PIPCost + Commission * LotAmount / 10000.;
if (RiskPerLot <= 0.)
{
return 0;
}
LotCount = floor(max(0., Balance) * InpRiskPercent / 100. / RiskPerLot);
}
Available = max(0., Equity - MarginVal) * 0.8;
LotCount = min(LotCount, floor(Available / MarginCost));
// Never round UP to the minimum lot if it breaches risk or margin caps.
if (LotCount < 1. || LotCount > 2000000000.)
{
return 0;
}
OrderLots = (int)LotCount;
Stop = StopDistance;
TakeProfit = ProfitDistance;
Trail = 0.; // Exact pip-step trailing implemented in the TMF, not TrailStep.
TrailStep = 0.;
Margin = 0.;
Risk = 0.;
Lots = OrderLots;
if (Direction > 0)
{
Entered = enterLong(TS_ManageTrade);
}
else
{
Entered = enterShort(TS_ManageTrade);
}
if (!Entered)
{
return 0;
}
// Market orders only. Do not treat an unfilled/rejected order as a fill.
if (!(Entered->flags & TR_OPEN))
{
return 0;
}
TS_LastTradeBar = Bar;
if (InpShowSignals)
{
if (Direction > 0)
{
plot("TS Buy", priceClose(0), MAIN | TRIANGLE, InpBuyColor);
}
else
{
plot("TS Sell", priceClose(0), MAIN | TRIANGLE, InpSellColor);
}
}
return 1;
}
// ============================================================================
// DIAGNOSTIC DISPLAY
// ============================================================================
void TS_UpdateDisplay()
{
if (!InpShowPanel)
{
return;
}
// HS is a covariance norm, not a volatility or basis-point quote.
plot("TS HS covariance norm", TS_GetIntegratedVolatility(), NEW, BLUE);
plot("TS factors", TS_GetNumberOfFactors(), NEW, BLACK);
plot("TS PC1 variance %", TS_Eigenvalues[0] * 100., NEW, BLUE);
plot("TS drawdown %", TS_CurrentDrawdown, NEW, RED);
}
// ============================================================================
// DETERMINISTIC SELF-TESTS
// ============================================================================
// Deterministic in-engine tests; invoked with -d TS_SELFTEST, never orders.
// ===== Embedded TermStructureSelfTest.h =====
#ifndef TERM_STRUCTURE_SELF_TEST_H
#define TERM_STRUCTURE_SELF_TEST_H
int TS_TestFailures = 0;
int TS_TestCount = 0;
var TS_TestMatrix[9];
var TS_TestVectors[9];
var TS_TestValues[3];
var TS_TestPrices[100];
var TS_TestJumpIndices[100];
var TS_TestJumpSizes[100];
void TS_TestAssert(int Passed, string Description)
{
TS_TestCount++;
if (!Passed)
{
TS_TestFailures++;
printf("\nFAIL: %s", Description);
file_append("Log/TermStructureSingleFileSelfTest.txt", strf("FAIL: %s\n", Description));
}
}
void TS_RunSelfTests()
{
int I, J, K, OK, Count;
var Residual, Left, Right, Dot, Expected, Cumulative;
TS_TestCount = 0;
TS_TestFailures = 0;
file_write("Log/TermStructureSingleFileSelfTest.txt", "32-bit Zorro lite-C self-tests\n", 0);
TS_TestAssert(sizeof(int) == 4, "32-bit integer");
TS_TestAssert(sizeof(void *) == 4, "32-bit host pointer");
for (I = 0; I < 9; I++)
{
TS_TestMatrix[I] = 0.;
}
TS_TestMatrix[0] = 2.;
TS_TestMatrix[1] = 1.;
TS_TestMatrix[3] = 1.;
TS_TestMatrix[4] = 2.;
TS_TestMatrix[8] = 0.5;
OK = TS_EigenDecomposition(TS_TestMatrix, 3, TS_TestValues, TS_TestVectors);
TS_TestAssert(OK, "Jacobi converges");
TS_TestAssert(abs(TS_TestValues[0] - 3.) < 1.e-10, "largest eigenvalue = 3");
TS_TestAssert(abs(TS_TestValues[1] - 1.) < 1.e-10, "second eigenvalue = 1");
Residual = 0.;
for (K = 0; K < 3; K++)
{
for (I = 0; I < 3; I++)
{
Left = 0.;
for (J = 0; J < 3; J++)
{
Left += TS_TestMatrix[I * 3 + J] * TS_TestVectors[J * 3 + K];
}
Right = TS_TestValues[K] * TS_TestVectors[I * 3 + K];
Residual = max(Residual, abs(Left - Right));
}
}
TS_TestAssert(Residual < 1.e-10, "A*v=lambda*v residual");
for (I = 0; I < 3; I++)
{
for (J = 0; J < 3; J++)
{
Dot = 0.;
for (K = 0; K < 3; K++)
{
Dot += TS_TestVectors[K * 3 + I] * TS_TestVectors[K * 3 + J];
}
Expected = 0.;
if (I == J)
{
Expected = 1.;
}
TS_TestAssert(abs(Dot - Expected) < 1.e-10, "eigenvector orthonormality");
}
}
// Small-scale covariance must not be accepted as already diagonal.
for (I = 0; I < 9; I++)
{
TS_TestMatrix[I] *= 1.e-14;
}
TS_TestAssert(TS_EigenDecomposition(TS_TestMatrix, 3, TS_TestValues, TS_TestVectors),
"Jacobi small-scale convergence");
TS_TestAssert(abs(TS_TestValues[0] / 3.e-14 - 1.) < 1.e-10, "relative Jacobi tolerance");
TS_TestAssert(TS_Configure(), "default configuration");
TS_Lookback = 100;
TS_Regularize = 0;
for (I = 0; I < 100; I++)
{
TS_TestPrices[I] = exp(0.001 * I);
}
TS_TestAssert(TS_ComputeCovariations(TS_TestPrices, 100), "spot proxy analysis");
file_append("Log/TermStructureSingleFileSelfTest.txt",
strf("MEASURED: trace=%.15g HS=%.15g C00=%.15g\n", TS_RealizedTrace,
TS_IntegratedVolatility, TS_Truncated[0]));
TS_TestAssert(TS_TimeSteps == 99 && TS_Pairs == 3, "sample dimensions");
// Amplification squared: 8,48,216. Trace = 272*0.001^2.
// lite-C numeric literals can enter arithmetic at float precision.
TS_TestAssert(abs(TS_RealizedTrace / 0.000272 - 1.) < 1.e-6,
"realized covariance normalization");
TS_TestAssert(abs(TS_Truncated[0] - 0.000008) < 1.e-12, "truncated covariance normalization");
TS_TestAssert(TS_NumFactors == 1 && TS_Eigenvalues[0] > 0.999999, "rank-one spot proxy");
TS_TestAssert(abs(TS_IntegratedVolatility / 0.000272 - 1.) < 1.e-6, "Hilbert-Schmidt norm");
Cumulative = 0.;
for (I = 0; I < 3; I++)
{
Cumulative += TS_Eigenvalues[I];
TS_TestAssert(abs(TS_GetExplainedVariance(I + 1) - Cumulative) < 1.e-12,
"all cumulative explained variances initialized");
}
TS_TestAssert(TS_DetectJumps(TS_TestJumpIndices, TS_TestJumpSizes, 100) == 0,
"no jumps in constant small returns");
for (I = 50; I < 100; I++)
{
TS_TestPrices[I] *= exp(0.1);
}
TS_TestAssert(TS_ComputeCovariations(TS_TestPrices, 100), "jump sample analysis");
Count = TS_DetectJumps(TS_TestJumpIndices, TS_TestJumpSizes, 100);
TS_TestAssert(Count == 1 && TS_TestJumpIndices[0] == 49., "jump index and count");
TS_TestAssert(TS_KeptCount == 98, "jump removed from truncated estimate");
TS_TestAssert(TS_DetectJumps(TS_TestJumpIndices, TS_TestJumpSizes, 0) == -1,
"jump output capacity guard");
TS_TestAssert(TS_GetEigenvalues(TS_TestValues, 2) == 0, "eigenvalue capacity guard");
TS_TestPrices[99] = 0.;
TS_TestAssert(!TS_ComputeCovariations(TS_TestPrices, 100) && !TS_Initialized,
"invalid prices clear stale results");
TS_TestAssert(!TS_ComputeCovariations(TS_TestPrices, 99), "insufficient samples");
for (I = 0; I < 100; I++)
{
TS_TestPrices[I] = 1.;
}
TS_Regularize = 1;
TS_TestAssert(TS_ComputeCovariations(TS_TestPrices, 100),
"flat-data regularization compatibility");
TS_TestAssert(TS_RealizedTrace == 0., "flat-data guard trace");
TS_TestAssert(!TS_CheckTradingConditions(), "flat data cannot trade after regularization");
TS_ConfigMaturities[1] = 30;
TS_TestAssert(!TS_SetMaturities(TS_ConfigMaturities, 4), "duplicate maturities rejected");
printf("\nTS SELFTEST: %d checks, %d failures", TS_TestCount, TS_TestFailures);
file_append("Log/TermStructureSingleFileSelfTest.txt",
strf("RESULT: %d checks, %d failures\n", TS_TestCount, TS_TestFailures));
}
#endif
// ===== End TermStructureSelfTest.h =====
// ============================================================================
// ZORRO STRATEGY ENTRY POINT
// ============================================================================
function run()
{
#ifdef TS_SELFTEST
if (is(INITRUN))
{
TS_RunSelfTests();
quit("TS self-test complete; see Log/TermStructureSingleFileSelfTest.txt");
}
return;
#endif
BarPeriod = InpBarPeriod;
StartDate = InpStartDate;
EndDate = InpEndDate;
LookBack = InpLookbackBars + 1;
Capital = InpInitialCapital;
Hedge = 0;
MaxLong = 1;
MaxShort = 1;
set(TICKS);
if (is(INITRUN))
{
if (!TS_Configure())
{
quit("Invalid TS configuration");
return;
}
printf("\nTermStructureCovariation: spot proxy; factor-sign signal is experimental.");
}
if (!asset(InpAsset))
{
return;
}
algo("TS");
if (is(EXITRUN))
{
printf("\nTS summary: signals=%d wins=%d losses=%d", TS_TotalSignals, TS_WinningTrades,
TS_LosingTrades);
return;
}
if (is(LOOKBACK))
{
return;
}
if (!TS_UpdateAnalysis())
{
return;
}
TS_CurrentDrawdown = TS_CalculateDrawdown();
TS_UpdateDisplay();
if (!TS_CheckTradingConditions())
{
return;
}
int Direction = TS_GenerateSignal();
if (Direction != 0 && TS_ShouldTakeSignal(Direction))
{
TS_TotalSignals++;
if (InpShowDiagnostics)
{
TS_PrintDiagnostics();
}
TS_ExecuteTrade(Direction);
}
}
Last edited by TipmyPip; 3 hours ago.
|
|
|
Covariance PCA
[Re: TipmyPip]
#489724
1 hour ago
1 hour ago
|
Joined: Sep 2017
Posts: 346
TipmyPip
OP
Senior Member
|
OP
Senior Member
Joined: Sep 2017
Posts: 346
|
Covariance PCA — The Market Orchestra Imagine the market as an orchestra whose instruments represent different ages of price movement. One instrument plays the latest change, another plays the preceding interval, and others represent progressively older stretches. These instruments come from the same Forex price history, but they do not simply repeat one identical return at different volumes. Each listens to a separate age bucket. The program first examines how these instruments have moved together during recent completed bars. It builds a portrait of their shared activity. Exceptionally energetic observations can be excluded from this portrait so that one loud interruption does not completely dominate the arrangement. The result is an uncentered covariation portrait, which retains both directional movement and joint fluctuation rather than removing every average drift. A numerical search then looks for the strongest common theme. Instead of decomposing the entire orchestra into every possible component, the program repeatedly refines one candidate direction. This process is called power iteration. Its purpose is to identify a principal direction explaining a substantial share of the observed activity. The program checks how dominant this theme is before considering a trade. The newest completed observation is then projected onto that common theme. Crucially, the program translates the projection back toward the shortest price interval. This prevents an arbitrary reversal of the principal vector from reversing the economic meaning of the signal. The direction comes from observed movement interpreted through the component, not from the component's orientation alone. When the common theme is sufficiently concentrated and its current projection is meaningfully positive, the strategy considers buying. A sufficiently negative projection supports selling. When the orchestra loses its common theme, existing positions can be closed. Positions also have protective stops, profit targets, and a maximum lifetime, so the component is not treated as permanent evidence. Additional diagnostics examine the effective number of active directions, the concentration of component loadings, and the numerical residual of the estimated principal direction. These diagnostics help distinguish a coherent common movement from a poorly represented collection of unrelated fluctuations. The orchestra remains an analogy, not a forecasting guarantee. Separate age buckets can still be statistically dependent, and a strong common component can describe movement that has already happened. Risk limits constrain individual exposure, but they do not establish that the common theme will continue after entry. // Independent model 01: robust cross-horizon covariance and power-iteration PCA.
// Forex horizon buckets are not bond maturities. Research prototype only.
#include <default.c>
string PCAAsset = "EUR/USD";
var PCAClose[150];
var PCAFeatures[320];
var PCAMatrix[25];
var PCAVector[5];
var PCAActivity = 0.;
var PCADominance = 0.;
int PCATenors[6] = {0, 1, 2, 4, 8, 16};
int PCAEvaluate()
{
int Row, A, B, Iteration, Width;
var Observation[5];
var NextVector[5];
var Trace, Energy, Limit, Projection, Eigenvalue, Norm;
for (Row = 0; Row < 150; Row++)
{
if (!(PCAClose[Row] > 0.))
{
return 0;
}
}
Energy = 0.;
for (Row = 0; Row < 64; Row++)
{
for (A = 0; A < 5; A++)
{
Width = PCATenors[A + 1] - PCATenors[A];
Projection =
log(PCAClose[Row + 1 + PCATenors[A]] / PCAClose[Row + 1 + PCATenors[A + 1]]) /
sqrt(Width);
PCAFeatures[Row * 5 + A] = Projection;
Energy += Projection * Projection / 64.;
}
}
if (Energy <= 1.e-20)
{
return 0;
}
Limit = 9. * Energy;
for (A = 0; A < 25; A++)
{
PCAMatrix[A] = 0.;
}
for (Row = 0; Row < 64; Row++)
{
Norm = 0.;
for (A = 0; A < 5; A++)
{
Norm += PCAFeatures[Row * 5 + A] * PCAFeatures[Row * 5 + A];
}
if (Norm > Limit)
{
continue;
}
for (A = 0; A < 5; A++)
{
for (B = 0; B < 5; B++)
{
PCAMatrix[A * 5 + B] += PCAFeatures[Row * 5 + A] * PCAFeatures[Row * 5 + B] / 64.;
}
}
}
// Uncentered covariation preserves the drift direction in the observations.
for (A = 0; A < 5; A++)
{
PCAVector[A] = 1. / sqrt(5.);
}
for (Iteration = 0; Iteration < 80; Iteration++)
{
Norm = 0.;
for (A = 0; A < 5; A++)
{
NextVector[A] = 0.;
for (B = 0; B < 5; B++)
{
NextVector[A] += PCAMatrix[A * 5 + B] * PCAVector[B];
}
Norm += NextVector[A] * NextVector[A];
}
if (Norm <= 1.e-40)
{
return 0;
}
for (A = 0; A < 5; A++)
{
PCAVector[A] = NextVector[A] / sqrt(Norm);
}
}
Trace = 0.;
Eigenvalue = 0.;
Projection = 0.;
for (A = 0; A < 5; A++)
{
Trace += PCAMatrix[A * 5 + A];
for (B = 0; B < 5; B++)
{
Eigenvalue += PCAVector[A] * PCAMatrix[A * 5 + B] * PCAVector[B];
}
Width = PCATenors[A + 1] - PCATenors[A];
Observation[A] = log(PCAClose[PCATenors[A]] / PCAClose[PCATenors[A + 1]]) / sqrt(Width);
Projection += PCAVector[A] * Observation[A];
}
if (Trace <= 1.e-20)
{
return 0;
}
PCADominance = Eigenvalue / Trace;
// Sign invariant: both vector factors change sign together.
PCAActivity = Projection * PCAVector[0] / sqrt(max(1.e-20, PCAMatrix[0]));
if (PCADominance < 0.55 || abs(PCAActivity) < 0.5)
{
return 0;
}
if (PCAActivity > 0.)
{
return 1;
}
return -1;
}
void PCATests()
{
int K, Failures;
Failures = 0;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.;
}
if (PCAEvaluate() != 0)
{
Failures++;
}
for (K = 0; K < 150; K++)
{
PCAClose[K] = exp(-0.001 * K);
}
if (PCAEvaluate() != 1)
{
Failures++;
}
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
if (PCAEvaluate() != -1)
{
Failures++;
}
PCAClose[0] = 0.;
if (PCAEvaluate() != 0)
{
Failures++;
}
file_write("Log/TS01_CovariancePCA_test.txt", strf("4 checks; %d failures\n", Failures), 0);
}
var PCAResidualNorm()
{
int A, B;
var Lambda, Product, Residual;
Lambda = 0.;
Residual = 0.;
for (A = 0; A < 5; A++)
{
for (B = 0; B < 5; B++)
{
Lambda += PCAVector[A] * PCAMatrix[A * 5 + B] * PCAVector[B];
}
}
for (A = 0; A < 5; A++)
{
Product = 0.;
for (B = 0; B < 5; B++)
{
Product += PCAMatrix[A * 5 + B] * PCAVector[B];
}
Product -= Lambda * PCAVector[A];
Residual += Product * Product;
}
return sqrt(Residual);
}
var PCAEffectiveDimension()
{
int A, B;
var Trace, SquaredTrace;
Trace = 0.;
SquaredTrace = 0.;
for (A = 0; A < 5; A++)
{
Trace += PCAMatrix[A * 5 + A];
for (B = 0; B < 5; B++)
{
SquaredTrace += PCAMatrix[A * 5 + B] * PCAMatrix[A * 5 + B];
}
}
if (SquaredTrace < 1.e-40)
{
return 0.;
}
return Trace * Trace / SquaredTrace;
}
var PCALoadingConcentration()
{
int A;
var FourthMoment, SecondMoment;
FourthMoment = 0.;
SecondMoment = 0.;
for (A = 0; A < 5; A++)
{
SecondMoment += PCAVector[A] * PCAVector[A];
FourthMoment += pow(PCAVector[A], 4.);
}
if (SecondMoment <= 0.)
{
return 0.;
}
return FourthMoment / (SecondMoment * SecondMoment);
}
void PCAResearchDiagnostics()
{
printf("\nPCA effective dimension %.6g; loading concentration %.6g; residual %.6g",
PCAEffectiveDimension(), PCALoadingConcentration(), PCAResidualNorm());
}
// ============================================================================
// EXTENDED REGRESSION SUITE: deterministic properties over market regimes
// These functions run ONLY under STX_SELFTEST; they never place orders.
// ============================================================================
int PCAExtendedCount = 0;
int PCAExtendedFailures = 0;
void PCAExpect(int Passed, string Description)
{
PCAExtendedCount++;
if (!Passed)
{
PCAExtendedFailures++;
file_append("Log/TS01_CovariancePCA_extended.txt", strf("FAIL: %s\n", Description));
}
}
void PCACase_quiet_cycle()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp(0.0005 * sin(0.17 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "quiet_cycle: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "quiet_cycle: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "quiet_cycle: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "quiet_cycle: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "quiet_cycle: missing price rejected");
}
void PCACase_mixed_cycles()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp(0.003 * sin(0.23 * K) + 0.0007 * cos(0.81 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "mixed_cycles: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "mixed_cycles: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "mixed_cycles: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "mixed_cycles: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "mixed_cycles: missing price rejected");
}
void PCACase_persistent_upward()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp(-0.001 * K + 0.0001 * sin(0.53 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "persistent_upward: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "persistent_upward: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "persistent_upward: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "persistent_upward: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "persistent_upward: missing price rejected");
}
void PCACase_persistent_downward()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp(0.001 * K + 0.0001 * cos(0.47 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "persistent_downward: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "persistent_downward: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "persistent_downward: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "persistent_downward: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "persistent_downward: missing price rejected");
}
void PCACase_slow_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp(0.004 * cos(0.08 * K) + 0.0002 * sin(0.7 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "slow_curve: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "slow_curve: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "slow_curve: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "slow_curve: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "slow_curve: missing price rejected");
}
void PCACase_rough_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp(0.001 * sin(1.7 * K) + 0.0005 * cos(2.3 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "rough_curve: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "rough_curve: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "rough_curve: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "rough_curve: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "rough_curve: missing price rejected");
}
void PCACase_accelerating_path()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp(-0.0001 * K - 0.000002 * K * K + 0.0002 * sin(0.41 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "accelerating_path: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "accelerating_path: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "accelerating_path: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "accelerating_path: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "accelerating_path: missing price rejected");
}
void PCACase_decelerating_path()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp(-0.0008 * K + 0.000002 * K * K + 0.0002 * cos(0.31 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "decelerating_path: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "decelerating_path: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "decelerating_path: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "decelerating_path: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "decelerating_path: missing price rejected");
}
void PCACase_volatility_ramp()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp((0.0001 + K * 0.00001) * sin(0.37 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "volatility_ramp: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "volatility_ramp: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "volatility_ramp: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "volatility_ramp: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "volatility_ramp: missing price rejected");
}
void PCACase_volatility_decay()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp(0.006 * exp(-0.02 * K) * cos(0.7 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "volatility_decay: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "volatility_decay: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "volatility_decay: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "volatility_decay: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "volatility_decay: missing price rejected");
}
void PCACase_cycle_with_drift()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp(-0.0002 * K + 0.003 * sin(0.19 * K) + 0.0005 * cos(0.61 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "cycle_with_drift: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "cycle_with_drift: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "cycle_with_drift: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "cycle_with_drift: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "cycle_with_drift: missing price rejected");
}
void PCACase_near_zero_prices()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 0.0001 * exp(0.002 * sin(0.29 * K) + 0.001 * cos(0.13 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "near_zero_prices: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "near_zero_prices: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "near_zero_prices: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "near_zero_prices: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "near_zero_prices: missing price rejected");
}
void PCACase_large_price_units()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 10000.0 * exp(0.003 * sin(0.11 * K) + 0.0008 * cos(0.43 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "large_price_units: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "large_price_units: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "large_price_units: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "large_price_units: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "large_price_units: missing price rejected");
}
void PCACase_high_frequency_chop()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp(0.0008 * cos(2.8 * K) + 0.001 * sin(0.23 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "high_frequency_chop: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "high_frequency_chop: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "high_frequency_chop: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "high_frequency_chop: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "high_frequency_chop: missing price rejected");
}
void PCACase_long_wave_trend()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp(-0.0005 * K + 0.002 * cos(0.07 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "long_wave_trend: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "long_wave_trend: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "long_wave_trend: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "long_wave_trend: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "long_wave_trend: missing price rejected");
}
void PCACase_opposing_scales()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp(0.005 * sin(0.09 * K) - 0.002 * sin(0.59 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "opposing_scales: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "opposing_scales: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "opposing_scales: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "opposing_scales: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "opposing_scales: missing price rejected");
}
void PCACase_nonlinear_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp(0.002 * sin(0.021 * K * K) + 0.001 * cos(0.33 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "nonlinear_curve: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "nonlinear_curve: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "nonlinear_curve: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "nonlinear_curve: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "nonlinear_curve: missing price rejected");
}
void PCACase_amplitude_modulation()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp(0.002 * sin(0.3 * K) * cos(0.04 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "amplitude_modulation: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "amplitude_modulation: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "amplitude_modulation: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "amplitude_modulation: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "amplitude_modulation: missing price rejected");
}
void PCACase_decaying_event()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp(0.01 * exp(-0.2 * K) + 0.0003 * sin(0.71 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "decaying_event: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "decaying_event: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "decaying_event: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "decaying_event: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "decaying_event: missing price rejected");
}
void PCACase_time_varying_frequency()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp(0.002 * cos(0.004 * K * K + 0.11 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "time_varying_frequency: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "time_varying_frequency: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "time_varying_frequency: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "time_varying_frequency: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "time_varying_frequency: missing price rejected");
}
void PCACase_slow_fast_cancellation()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1.0 * exp(0.004 * sin(0.07 * K) + 0.0013 * cos(0.97 * K));
}
Original = PCAEvaluate();
PCAExpect(Original >= -1 && Original <= 1, "slow_fast_cancellation: bounded direction");
Again = PCAEvaluate();
PCAExpect(Again == Original, "slow_fast_cancellation: repeatable estimate");
for (K = 0; K < 150; K++)
{
PCAClose[K] *= 7.;
}
Scaled = PCAEvaluate();
PCAExpect(Scaled == Original, "slow_fast_cancellation: price-unit invariance");
for (K = 0; K < 150; K++)
{
PCAClose[K] = 1. / PCAClose[K];
}
Reflected = PCAEvaluate();
PCAExpect(Reflected == -Original, "slow_fast_cancellation: directional reflection");
PCAClose[75] = 0.;
PCAExpect(PCAEvaluate() == 0, "slow_fast_cancellation: missing price rejected");
}
void PCAExtendedTests()
{
PCAExtendedCount = 0;
PCAExtendedFailures = 0;
file_write("Log/TS01_CovariancePCA_extended.txt", "TS01_CovariancePCA: extended properties\n",
0);
PCACase_quiet_cycle();
PCACase_mixed_cycles();
PCACase_persistent_upward();
PCACase_persistent_downward();
PCACase_slow_curve();
PCACase_rough_curve();
PCACase_accelerating_path();
PCACase_decelerating_path();
PCACase_volatility_ramp();
PCACase_volatility_decay();
PCACase_cycle_with_drift();
PCACase_near_zero_prices();
PCACase_large_price_units();
PCACase_high_frequency_chop();
PCACase_long_wave_trend();
PCACase_opposing_scales();
PCACase_nonlinear_curve();
PCACase_amplitude_modulation();
PCACase_decaying_event();
PCACase_time_varying_frequency();
PCACase_slow_fast_cancellation();
file_append("Log/TS01_CovariancePCA_extended.txt",
strf("RESULT: %d checks; %d failures\n", PCAExtendedCount, PCAExtendedFailures));
}
function run()
{
#ifdef STX_SELFTEST
if (is(INITRUN))
{
PCATests();
PCAExtendedTests();
quit("PCA tests complete");
}
return;
#endif
int K, Side;
var Budget, PerLot, Volume;
BarPeriod = 60;
LookBack = 151;
StartDate = 20240101;
EndDate = 20241231;
Capital = 10000.;
Hedge = 0;
MaxLong = 1;
MaxShort = 1;
if (!asset(PCAAsset))
{
return;
}
algo("PCA01");
if (is(EXITRUN))
{
PCAResearchDiagnostics();
return;
}
if (is(LOOKBACK))
{
return;
}
for (K = 0; K < 150; K++)
{
PCAClose[K] = priceClose(K + 1);
}
Side = PCAEvaluate();
// Exit when the dominant common factor loses concentration.
if (PCADominance < 0.35)
{
exitLong();
exitShort();
return;
}
if (Side == 0)
{
return;
}
if (Side > 0)
{
exitShort();
}
else
{
exitLong();
}
if (NumOpenLong + NumOpenShort > 0)
{
return;
}
if (PIP <= 0. || PIPCost <= 0. || LotAmount <= 0. || MarginCost <= 0.)
{
return;
}
if (Balance <= 0. || Equity < Balance * 0.85)
{
return;
}
Stop = 40. * PIP;
TakeProfit = 100. * PIP;
LifeTime = 16;
Budget = Balance * 0.005;
PerLot = (Stop + Spread) / PIP * PIPCost + Commission * LotAmount / 10000.;
if (PerLot <= 0.)
{
return;
}
Volume = min(Budget / PerLot, 2000. / LotAmount);
Volume = min(Volume, max(0., Equity - MarginVal) * 0.8 / MarginCost);
if (Volume < 1.)
{
return;
}
Lots = floor(Volume);
Margin = 0.;
Risk = 0.;
if (Side > 0)
{
enterLong();
}
else
{
enterShort();
}
}
|
|
|
OU Horizon Spread
[Re: TipmyPip]
#489725
1 hour ago
1 hour ago
|
Joined: Sep 2017
Posts: 346
TipmyPip
OP
Senior Member
|
OP
Senior Member
Joined: Sep 2017
Posts: 346
|
OU Horizon Spread — The Elastic Bridge Picture an elastic bridge connecting the market's nearby movement to its more distant movement. The near end represents a short average return. The far end represents an older interval with a longer averaging span. The program watches the difference between these ends, treating that difference as a spread in horizon behavior rather than as the price spread between two independently traded assets. The bridge is useful only if it appears to pull back toward an equilibrium. The program therefore studies how earlier spread observations relate to the observations that followed them. It fits a simple persistence relationship and estimates whether deviations tend to diminish, remain almost permanent, or alternate in a way that does not match the intended model. If the fitted persistence is unsuitable, the bridge is considered unreliable and the program avoids opening a new position. When persistence falls inside the accepted region, the program estimates an equilibrium level, the typical size of deviations around that level, and a half life describing how quickly a displacement might diminish under the fitted assumptions. The current spread is then compared with that estimated equilibrium. A sufficiently stretched positive displacement is interpreted as a candidate for downward normalization. A sufficiently stretched negative displacement is interpreted as a candidate for upward normalization. The strategy therefore seeks reversion in the relationship between horizons, rather than simply following a large recent price change. The half life also influences the allowed holding period. A bridge expected to relax quickly should not justify an indefinitely patient trade. Existing positions can be closed when the spread returns near equilibrium or when the persistence estimate no longer supports the intended mean reversion interpretation. Protective price stops and profit targets remain active alongside these analytical exits. Research diagnostics describe the expected spread at a future horizon, the dispersion of historical spread levels, and the remaining serial relationship in model innovations. These observations help reveal whether the apparent elastic behavior is being captured reasonably or whether important patterns remain unexplained. The central limitation is that the spread itself is not a directly traded two asset portfolio. The strategy converts a horizon relationship into a directional Forex position. Even genuine normalization of the analytical spread does not guarantee a favorable movement in that position. Historical equilibrium can also shift abruptly, so the elastic bridge should be viewed as a conditional hypothesis rather than a physical law. // Independent model 02: equilibrium spread, AR(1) dynamics and OU half-life.
#include <default.c>
var OUSample[121];
var OUPrice[160];
var OUZ = 0.;
var OUHalfLife = 0.;
var OUPersistence = 0.;
int OUFitSpread()
{
int T;
var MeanX, MeanY, SumXX, SumXY, Intercept, Innovation, Noise, Current;
OUZ = 0.;
OUHalfLife = 0.;
OUPersistence = 1.;
for (T = 0; T < 160; T++)
{
if (!(OUPrice[T] > 0.))
{
return 0;
}
}
// Contrast a near-horizon average return with a distant age-bucket rate.
for (T = 0; T <= 120; T++)
{
OUSample[T] =
log(OUPrice[T] / OUPrice[T + 2]) / 2. - log(OUPrice[T + 8] / OUPrice[T + 16]) / 8.;
}
MeanX = 0.;
MeanY = 0.;
for (T = 2; T <= 120; T++)
{
MeanX += OUSample[T] / 119.;
MeanY += OUSample[T - 1] / 119.;
}
SumXX = 0.;
SumXY = 0.;
for (T = 2; T <= 120; T++)
{
SumXX += (OUSample[T] - MeanX) * (OUSample[T] - MeanX);
SumXY += (OUSample[T] - MeanX) * (OUSample[T - 1] - MeanY);
}
if (SumXX < 1.e-20)
{
return 0;
}
OUPersistence = SumXY / SumXX;
if (OUPersistence <= 0.05 || OUPersistence >= 0.98)
{
return 0;
}
Intercept = MeanY - OUPersistence * MeanX;
Noise = 0.;
for (T = 2; T <= 120; T++)
{
Innovation = OUSample[T - 1] - Intercept - OUPersistence * OUSample[T];
Noise += Innovation * Innovation / 119.;
}
if (Noise < 1.e-22)
{
return 0;
}
OUHalfLife = -log(2.) / log(OUPersistence);
Current = OUSample[0] - Intercept / (1. - OUPersistence);
OUZ = Current / sqrt(Noise / (1. - OUPersistence * OUPersistence));
if (OUHalfLife < 1. || OUHalfLife > 24. || abs(OUZ) < 1.75)
{
return 0;
}
if (OUZ < 0.)
{
return 1;
}
return -1;
}
void OUChecks()
{
int I, A, B, Errors;
Errors = 0;
for (I = 0; I < 160; I++)
{
OUPrice[I] = 1.;
}
if (OUFitSpread() != 0)
{
Errors++;
}
for (I = 0; I < 160; I++)
{
OUPrice[I] = exp(0.004 * sin(0.28 * I) + 0.001 * sin(1.71 * I));
}
A = OUFitSpread();
if (A < -1 || A > 1)
{
Errors++;
}
for (I = 0; I < 160; I++)
{
OUPrice[I] = 1. / OUPrice[I];
}
B = OUFitSpread();
if (B != -A)
{
Errors++;
}
OUPrice[3] = -1.;
if (OUFitSpread() != 0)
{
Errors++;
}
file_write("Log/TS02_OUHorizonSpread_test.txt", strf("4 checks; %d failures\n", Errors), 0);
}
var OUForecastAt(int Steps)
{
int T;
var Average;
if (Steps < 1 || OUPersistence <= 0. || OUPersistence >= 1.)
{
return 0.;
}
Average = 0.;
for (T = 1; T <= 120; T++)
{
Average += OUSample[T] / 120.;
}
return Average + pow(OUPersistence, Steps) * (OUSample[0] - Average);
}
var OUInnovationAutocorrelation()
{
int T;
var Previous, Current, Numerator, Denominator;
Numerator = 0.;
Denominator = 0.;
Previous = 0.;
for (T = 119; T >= 1; T--)
{
Current = OUSample[T] - OUPersistence * OUSample[T + 1];
Numerator += Current * Previous;
Denominator += Current * Current;
Previous = Current;
}
if (Denominator < 1.e-24)
{
return 0.;
}
return Numerator / Denominator;
}
var OULevelDispersion()
{
int T;
var Average, SpreadVariance;
Average = 0.;
SpreadVariance = 0.;
for (T = 1; T <= 120; T++)
{
Average += OUSample[T] / 120.;
}
for (T = 1; T <= 120; T++)
{
SpreadVariance += pow(OUSample[T] - Average, 2.) / 120.;
}
return sqrt(SpreadVariance);
}
void OUResearchDiagnostics()
{
printf("\nOU half-life %.6g; forecast4 %.6g; innovation correlation %.6g; spread scale %.6g",
OUHalfLife, OUForecastAt(4), OUInnovationAutocorrelation(), OULevelDispersion());
}
// ============================================================================
// EXTENDED REGRESSION SUITE: deterministic properties over market regimes
// These functions run ONLY under STX_SELFTEST; they never place orders.
// ============================================================================
int OUExtendedCount = 0;
int OUExtendedFailures = 0;
void OUExpect(int Passed, string Description)
{
OUExtendedCount++;
if (!Passed)
{
OUExtendedFailures++;
file_append("Log/TS02_OUHorizonSpread_extended.txt", strf("FAIL: %s\n", Description));
}
}
void OUCase_quiet_cycle()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.0005 * sin(0.17 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "quiet_cycle: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "quiet_cycle: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "quiet_cycle: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "quiet_cycle: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "quiet_cycle: missing price rejected");
}
void OUCase_mixed_cycles()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.003 * sin(0.23 * K) + 0.0007 * cos(0.81 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "mixed_cycles: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "mixed_cycles: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "mixed_cycles: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "mixed_cycles: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "mixed_cycles: missing price rejected");
}
void OUCase_persistent_upward()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(-0.001 * K + 0.0001 * sin(0.53 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "persistent_upward: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "persistent_upward: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "persistent_upward: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "persistent_upward: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "persistent_upward: missing price rejected");
}
void OUCase_persistent_downward()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.001 * K + 0.0001 * cos(0.47 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "persistent_downward: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "persistent_downward: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "persistent_downward: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "persistent_downward: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "persistent_downward: missing price rejected");
}
void OUCase_slow_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.004 * cos(0.08 * K) + 0.0002 * sin(0.7 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "slow_curve: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "slow_curve: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "slow_curve: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "slow_curve: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "slow_curve: missing price rejected");
}
void OUCase_rough_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.001 * sin(1.7 * K) + 0.0005 * cos(2.3 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "rough_curve: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "rough_curve: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "rough_curve: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "rough_curve: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "rough_curve: missing price rejected");
}
void OUCase_accelerating_path()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(-0.0001 * K - 0.000002 * K * K + 0.0002 * sin(0.41 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "accelerating_path: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "accelerating_path: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "accelerating_path: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "accelerating_path: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "accelerating_path: missing price rejected");
}
void OUCase_decelerating_path()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(-0.0008 * K + 0.000002 * K * K + 0.0002 * cos(0.31 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "decelerating_path: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "decelerating_path: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "decelerating_path: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "decelerating_path: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "decelerating_path: missing price rejected");
}
void OUCase_volatility_ramp()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp((0.0001 + K * 0.00001) * sin(0.37 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "volatility_ramp: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "volatility_ramp: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "volatility_ramp: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "volatility_ramp: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "volatility_ramp: missing price rejected");
}
void OUCase_volatility_decay()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.006 * exp(-0.02 * K) * cos(0.7 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "volatility_decay: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "volatility_decay: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "volatility_decay: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "volatility_decay: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "volatility_decay: missing price rejected");
}
void OUCase_cycle_with_drift()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(-0.0002 * K + 0.003 * sin(0.19 * K) + 0.0005 * cos(0.61 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "cycle_with_drift: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "cycle_with_drift: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "cycle_with_drift: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "cycle_with_drift: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "cycle_with_drift: missing price rejected");
}
void OUCase_near_zero_prices()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 0.0001 * exp(0.002 * sin(0.29 * K) + 0.001 * cos(0.13 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "near_zero_prices: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "near_zero_prices: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "near_zero_prices: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "near_zero_prices: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "near_zero_prices: missing price rejected");
}
void OUCase_large_price_units()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 10000.0 * exp(0.003 * sin(0.11 * K) + 0.0008 * cos(0.43 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "large_price_units: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "large_price_units: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "large_price_units: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "large_price_units: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "large_price_units: missing price rejected");
}
void OUCase_high_frequency_chop()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.0008 * cos(2.8 * K) + 0.001 * sin(0.23 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "high_frequency_chop: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "high_frequency_chop: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "high_frequency_chop: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "high_frequency_chop: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "high_frequency_chop: missing price rejected");
}
void OUCase_long_wave_trend()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(-0.0005 * K + 0.002 * cos(0.07 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "long_wave_trend: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "long_wave_trend: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "long_wave_trend: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "long_wave_trend: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "long_wave_trend: missing price rejected");
}
void OUCase_opposing_scales()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.005 * sin(0.09 * K) - 0.002 * sin(0.59 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "opposing_scales: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "opposing_scales: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "opposing_scales: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "opposing_scales: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "opposing_scales: missing price rejected");
}
void OUCase_nonlinear_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.002 * sin(0.021 * K * K) + 0.001 * cos(0.33 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "nonlinear_curve: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "nonlinear_curve: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "nonlinear_curve: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "nonlinear_curve: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "nonlinear_curve: missing price rejected");
}
void OUCase_amplitude_modulation()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.002 * sin(0.3 * K) * cos(0.04 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "amplitude_modulation: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "amplitude_modulation: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "amplitude_modulation: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "amplitude_modulation: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "amplitude_modulation: missing price rejected");
}
void OUCase_decaying_event()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.01 * exp(-0.2 * K) + 0.0003 * sin(0.71 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "decaying_event: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "decaying_event: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "decaying_event: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "decaying_event: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "decaying_event: missing price rejected");
}
void OUCase_time_varying_frequency()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.002 * cos(0.004 * K * K + 0.11 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "time_varying_frequency: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "time_varying_frequency: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "time_varying_frequency: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "time_varying_frequency: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "time_varying_frequency: missing price rejected");
}
void OUCase_slow_fast_cancellation()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1.0 * exp(0.004 * sin(0.07 * K) + 0.0013 * cos(0.97 * K));
}
Original = OUFitSpread();
OUExpect(Original >= -1 && Original <= 1, "slow_fast_cancellation: bounded direction");
Again = OUFitSpread();
OUExpect(Again == Original, "slow_fast_cancellation: repeatable estimate");
for (K = 0; K < 160; K++)
{
OUPrice[K] *= 7.;
}
Scaled = OUFitSpread();
OUExpect(Scaled == Original, "slow_fast_cancellation: price-unit invariance");
for (K = 0; K < 160; K++)
{
OUPrice[K] = 1. / OUPrice[K];
}
Reflected = OUFitSpread();
OUExpect(Reflected == -Original, "slow_fast_cancellation: directional reflection");
OUPrice[80] = 0.;
OUExpect(OUFitSpread() == 0, "slow_fast_cancellation: missing price rejected");
}
void OUExtendedTests()
{
OUExtendedCount = 0;
OUExtendedFailures = 0;
file_write("Log/TS02_OUHorizonSpread_extended.txt",
"TS02_OUHorizonSpread: extended properties\n", 0);
OUCase_quiet_cycle();
OUCase_mixed_cycles();
OUCase_persistent_upward();
OUCase_persistent_downward();
OUCase_slow_curve();
OUCase_rough_curve();
OUCase_accelerating_path();
OUCase_decelerating_path();
OUCase_volatility_ramp();
OUCase_volatility_decay();
OUCase_cycle_with_drift();
OUCase_near_zero_prices();
OUCase_large_price_units();
OUCase_high_frequency_chop();
OUCase_long_wave_trend();
OUCase_opposing_scales();
OUCase_nonlinear_curve();
OUCase_amplitude_modulation();
OUCase_decaying_event();
OUCase_time_varying_frequency();
OUCase_slow_fast_cancellation();
file_append("Log/TS02_OUHorizonSpread_extended.txt",
strf("RESULT: %d checks; %d failures\n", OUExtendedCount, OUExtendedFailures));
}
function run()
{
#ifdef STX_SELFTEST
if (is(INITRUN))
{
OUChecks();
OUExtendedTests();
quit("OU tests complete");
}
return;
#endif
int I, OrderSide;
var Exposure, LossUnit;
BarPeriod = 60;
StartDate = 20240101;
EndDate = 20241231;
LookBack = 161;
Capital = 10000.;
MaxLong = 1;
MaxShort = 1;
Hedge = 0;
asset("EUR/USD");
algo("OU02");
if (is(EXITRUN))
{
OUResearchDiagnostics();
return;
}
if (is(LOOKBACK))
{
return;
}
for (I = 0; I < 160; I++)
{
OUPrice[I] = priceClose(I + 1);
}
OrderSide = OUFitSpread();
// Equilibrium arrival or loss of mean-reverting dynamics closes the trade.
if (abs(OUZ) < 0.25 || OUPersistence >= 0.98 || OUPersistence <= 0.)
{
exitLong();
exitShort();
}
if (OrderSide == 0 || NumOpenLong + NumOpenShort > 0)
{
return;
}
if (Equity <= Balance * 0.85 || Balance <= 0.)
{
return;
}
if (LotAmount <= 0. || PIPCost <= 0. || PIP <= 0. || MarginCost <= 0.)
{
return;
}
Stop = PIP * 35.;
TakeProfit = PIP * 50.;
LifeTime = max(2., min(48., OUHalfLife * 3.));
LossUnit = PIPCost * (Stop + Spread) / PIP + Commission * LotAmount / 10000.;
if (LossUnit <= 0.)
{
return;
}
Exposure = min(2000. / LotAmount, Balance * 0.005 / LossUnit);
Exposure = min(Exposure, max(0., Equity - MarginVal) * 0.8 / MarginCost);
if (Exposure < 1.)
{
return;
}
Lots = floor(Exposure);
Risk = 0.;
Margin = 0.;
if (OrderSide == 1)
{
enterLong();
}
else
{
enterShort();
}
}
|
|
|
Kalman Curve
[Re: TipmyPip]
#489726
1 hour ago
1 hour ago
|
Joined: Sep 2017
Posts: 346
TipmyPip
OP
Senior Member
|
OP
Senior Member
Joined: Sep 2017
Posts: 346
|
Kalman Curve — The Navigator Behind the Fog Imagine a navigator trying to follow a road whose surface is obscured by fog. The visible clues are returns measured over several horizons. Each clue offers a different perspective, but each is noisy and overlaps with other clues. The navigator's task is not to accept any single reading literally, but to maintain a compact internal map of the road. The internal map contains three features: a general level, a slope across horizons, and a curvature describing how that slope bends. These are latent features, meaning that the program does not observe them directly. It infers them from completed prices through a sequential Kalman filtering procedure. The current implementation rebuilds the filtering sequence from an older observation window on each decision bar. The navigator processes observations chronologically, beginning with older information and ending with the newest completed candle. Before accepting a new reading, it allows a small amount of uncertainty to accumulate. It then compares the reading with the internal prediction and adjusts the latent map according to the model's uncertainty and its assumed observation noise. A surprising reading does not automatically become a trade. The program also asks whether the inferred general direction, the interpretation of the horizon slope, and the newest short horizon innovation agree. A buying opportunity requires an upward latent tendency accompanied by the required short horizon acceleration. Selling requires the corresponding downward alignment. Conflicting clues leave the strategy neutral. The navigator can abandon an existing long position when the inferred general direction becomes nonpositive, or abandon a short position when that direction becomes nonnegative. A maximum holding period and protective price exits provide additional boundaries. Position sizing considers both the actual stop distance and available capital rather than assuming that filter confidence eliminates risk. Model specific diagnostics reconstruct expected readings at selected horizons, estimate uncertainty around those reconstructed readings, and measure how closely the inferred curve describes the observed return curve. These outputs help explain whether a trade follows a broad latent change or merely a short lived observation surprise. The fog analogy also highlights the limitations. A compact map can be wrong, especially when the road changes shape faster than the model permits. Overlapping horizon observations are not fully independent, although the simplified observation updates do not model every dependency. The displayed uncertainty therefore belongs to the chosen filtering assumptions and should not be mistaken for a complete probability statement about future Forex prices. // Independent model 03: latent level/slope/curvature via sequential Kalman updates.
#include <default.c>
var KFPrices[100];
var KFState[3];
var KFUncertainty[9];
var KFSlopeScore = 0.;
var KFInnovationScore = 0.;
int KFHorizons[5] = {2, 4, 8, 16, 32};
int KFFilterCurve()
{
int T, H, A, B;
var Observation, KFCoordinate, R, Variance, Innovation, Predicted;
var KFObservationVector[3];
var KFGainVector[3];
var KFProjectedCov[3];
var KFPreviousCov[9];
KFSlopeScore = 0.;
KFInnovationScore = 0.;
for (T = 0; T < 100; T++)
{
if (!(KFPrices[T] > 0.))
{
return 0;
}
}
R = 0.;
for (T = 1; T <= 48; T++)
{
Observation = log(KFPrices[T] / KFPrices[T + 1]);
R += Observation * Observation / 48.;
}
if (R < 1.e-20)
{
return 0;
}
for (A = 0; A < 3; A++)
{
KFState[A] = 0.;
}
for (A = 0; A < 9; A++)
{
KFUncertainty[A] = 0.;
}
for (A = 0; A < 3; A++)
{
KFUncertainty[A * 3 + A] = R;
}
// Replay old -> new; current decision observations enter only at the end.
for (T = 48; T >= 0; T--)
{
KFUncertainty[0] += R * 0.01;
KFUncertainty[4] += R * 0.002;
KFUncertainty[8] += R * 0.001;
for (H = 0; H < 5; H++)
{
KFCoordinate = log(KFHorizons[H]) / log(2.) - 3.;
KFObservationVector[0] = 1.;
KFObservationVector[1] = KFCoordinate;
KFObservationVector[2] = KFCoordinate * KFCoordinate - 2.;
Observation = log(KFPrices[T] / KFPrices[T + KFHorizons[H]]) / KFHorizons[H];
Predicted = 0.;
Variance = R / KFHorizons[H];
for (A = 0; A < 3; A++)
{
Predicted += KFObservationVector[A] * KFState[A];
KFProjectedCov[A] = 0.;
for (B = 0; B < 3; B++)
{
KFProjectedCov[A] += KFUncertainty[A * 3 + B] * KFObservationVector[B];
}
Variance += KFObservationVector[A] * KFProjectedCov[A];
}
if (Variance <= 1.e-25)
{
return 0;
}
Innovation = Observation - Predicted;
if (T == 0 && H == 0)
{
KFInnovationScore = Innovation / sqrt(Variance);
}
for (A = 0; A < 9; A++)
{
KFPreviousCov[A] = KFUncertainty[A];
}
for (A = 0; A < 3; A++)
{
KFGainVector[A] = KFProjectedCov[A] / Variance;
KFState[A] += KFGainVector[A] * Innovation;
for (B = 0; B < 3; B++)
{
KFUncertainty[A * 3 + B] =
KFPreviousCov[A * 3 + B] - KFGainVector[A] * KFProjectedCov[B];
}
}
}
}
KFSlopeScore = -KFState[1] / sqrt(max(1.e-25, KFUncertainty[4]));
// Short-horizon acceleration must agree with the latent average direction.
if (KFState[0] > 0. && KFSlopeScore > 1. && KFInnovationScore > 0.25)
{
return 1;
}
if (KFState[0] < 0. && KFSlopeScore < -1. && KFInnovationScore < -0.25)
{
return -1;
}
return 0;
}
void KFVerify()
{
int T, Positive, Negative, Bad;
Bad = 0;
for (T = 0; T < 100; T++)
{
KFPrices[T] = 1.;
}
if (KFFilterCurve() != 0)
{
Bad++;
}
for (T = 0; T < 100; T++)
{
KFPrices[T] = exp(-0.0003 * T + 0.002 * cos(0.21 * T));
}
Positive = KFFilterCurve();
if (Positive < -1 || Positive > 1)
{
Bad++;
}
for (T = 0; T < 100; T++)
{
KFPrices[T] = 1. / KFPrices[T];
}
Negative = KFFilterCurve();
if (Negative != -Positive)
{
Bad++;
}
KFPrices[0] = 0.;
if (KFFilterCurve() != 0)
{
Bad++;
}
file_write("Log/TS03_KalmanCurve_test.txt", strf("4 checks; %d failures\n", Bad), 0);
}
var KFPredictHorizon(int Horizon)
{
var Coordinate;
if (Horizon < 1)
{
return 0.;
}
Coordinate = log(Horizon) / log(2.) - 3.;
return KFState[0] + Coordinate * KFState[1] + (Coordinate * Coordinate - 2.) * KFState[2];
}
var KFUncertaintyAt(int Horizon)
{
var H[3];
var Estimate, X;
int A, B;
if (Horizon < 1)
{
return 0.;
}
X = log(Horizon) / log(2.) - 3.;
H[0] = 1.;
H[1] = X;
H[2] = X * X - 2.;
Estimate = 0.;
for (A = 0; A < 3; A++)
{
for (B = 0; B < 3; B++)
{
Estimate += H[A] * KFUncertainty[A * 3 + B] * H[B];
}
}
return sqrt(max(0., Estimate));
}
var KFReconstructionLoss()
{
int H;
var Observed, KFFitError, Sum;
Sum = 0.;
for (H = 0; H < 5; H++)
{
Observed = log(KFPrices[0] / KFPrices[KFHorizons[H]]) / KFHorizons[H];
KFFitError = Observed - KFPredictHorizon(KFHorizons[H]);
Sum += KFFitError * KFFitError / 5.;
}
return sqrt(Sum);
}
void KFResearchDiagnostics()
{
printf("\nKalman level %.6g; slope %.6g; curvature %.6g; horizon8 uncertainty %.6g; fit %.6g",
KFState[0], KFState[1], KFState[2], KFUncertaintyAt(8), KFReconstructionLoss());
}
// ============================================================================
// EXTENDED REGRESSION SUITE: deterministic properties over market regimes
// These functions run ONLY under STX_SELFTEST; they never place orders.
// ============================================================================
int KFExtendedCount = 0;
int KFExtendedFailures = 0;
void KFExpect(int Passed, string Description)
{
KFExtendedCount++;
if (!Passed)
{
KFExtendedFailures++;
file_append("Log/TS03_KalmanCurve_extended.txt", strf("FAIL: %s\n", Description));
}
}
void KFCase_quiet_cycle()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp(0.0005 * sin(0.17 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "quiet_cycle: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "quiet_cycle: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "quiet_cycle: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "quiet_cycle: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "quiet_cycle: missing price rejected");
}
void KFCase_mixed_cycles()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp(0.003 * sin(0.23 * K) + 0.0007 * cos(0.81 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "mixed_cycles: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "mixed_cycles: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "mixed_cycles: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "mixed_cycles: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "mixed_cycles: missing price rejected");
}
void KFCase_persistent_upward()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp(-0.001 * K + 0.0001 * sin(0.53 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "persistent_upward: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "persistent_upward: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "persistent_upward: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "persistent_upward: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "persistent_upward: missing price rejected");
}
void KFCase_persistent_downward()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp(0.001 * K + 0.0001 * cos(0.47 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "persistent_downward: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "persistent_downward: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "persistent_downward: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "persistent_downward: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "persistent_downward: missing price rejected");
}
void KFCase_slow_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp(0.004 * cos(0.08 * K) + 0.0002 * sin(0.7 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "slow_curve: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "slow_curve: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "slow_curve: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "slow_curve: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "slow_curve: missing price rejected");
}
void KFCase_rough_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp(0.001 * sin(1.7 * K) + 0.0005 * cos(2.3 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "rough_curve: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "rough_curve: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "rough_curve: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "rough_curve: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "rough_curve: missing price rejected");
}
void KFCase_accelerating_path()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp(-0.0001 * K - 0.000002 * K * K + 0.0002 * sin(0.41 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "accelerating_path: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "accelerating_path: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "accelerating_path: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "accelerating_path: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "accelerating_path: missing price rejected");
}
void KFCase_decelerating_path()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp(-0.0008 * K + 0.000002 * K * K + 0.0002 * cos(0.31 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "decelerating_path: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "decelerating_path: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "decelerating_path: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "decelerating_path: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "decelerating_path: missing price rejected");
}
void KFCase_volatility_ramp()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp((0.0001 + K * 0.00001) * sin(0.37 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "volatility_ramp: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "volatility_ramp: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "volatility_ramp: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "volatility_ramp: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "volatility_ramp: missing price rejected");
}
void KFCase_volatility_decay()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp(0.006 * exp(-0.02 * K) * cos(0.7 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "volatility_decay: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "volatility_decay: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "volatility_decay: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "volatility_decay: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "volatility_decay: missing price rejected");
}
void KFCase_cycle_with_drift()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp(-0.0002 * K + 0.003 * sin(0.19 * K) + 0.0005 * cos(0.61 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "cycle_with_drift: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "cycle_with_drift: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "cycle_with_drift: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "cycle_with_drift: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "cycle_with_drift: missing price rejected");
}
void KFCase_near_zero_prices()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 0.0001 * exp(0.002 * sin(0.29 * K) + 0.001 * cos(0.13 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "near_zero_prices: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "near_zero_prices: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "near_zero_prices: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "near_zero_prices: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "near_zero_prices: missing price rejected");
}
void KFCase_large_price_units()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 10000.0 * exp(0.003 * sin(0.11 * K) + 0.0008 * cos(0.43 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "large_price_units: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "large_price_units: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "large_price_units: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "large_price_units: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "large_price_units: missing price rejected");
}
void KFCase_high_frequency_chop()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp(0.0008 * cos(2.8 * K) + 0.001 * sin(0.23 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "high_frequency_chop: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "high_frequency_chop: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "high_frequency_chop: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "high_frequency_chop: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "high_frequency_chop: missing price rejected");
}
void KFCase_long_wave_trend()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp(-0.0005 * K + 0.002 * cos(0.07 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "long_wave_trend: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "long_wave_trend: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "long_wave_trend: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "long_wave_trend: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "long_wave_trend: missing price rejected");
}
void KFCase_opposing_scales()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp(0.005 * sin(0.09 * K) - 0.002 * sin(0.59 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "opposing_scales: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "opposing_scales: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "opposing_scales: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "opposing_scales: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "opposing_scales: missing price rejected");
}
void KFCase_nonlinear_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp(0.002 * sin(0.021 * K * K) + 0.001 * cos(0.33 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "nonlinear_curve: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "nonlinear_curve: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "nonlinear_curve: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "nonlinear_curve: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "nonlinear_curve: missing price rejected");
}
void KFCase_amplitude_modulation()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp(0.002 * sin(0.3 * K) * cos(0.04 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "amplitude_modulation: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "amplitude_modulation: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "amplitude_modulation: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "amplitude_modulation: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "amplitude_modulation: missing price rejected");
}
void KFCase_decaying_event()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp(0.01 * exp(-0.2 * K) + 0.0003 * sin(0.71 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "decaying_event: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "decaying_event: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "decaying_event: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "decaying_event: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "decaying_event: missing price rejected");
}
void KFCase_time_varying_frequency()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp(0.002 * cos(0.004 * K * K + 0.11 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "time_varying_frequency: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "time_varying_frequency: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "time_varying_frequency: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "time_varying_frequency: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "time_varying_frequency: missing price rejected");
}
void KFCase_slow_fast_cancellation()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1.0 * exp(0.004 * sin(0.07 * K) + 0.0013 * cos(0.97 * K));
}
Original = KFFilterCurve();
KFExpect(Original >= -1 && Original <= 1, "slow_fast_cancellation: bounded direction");
Again = KFFilterCurve();
KFExpect(Again == Original, "slow_fast_cancellation: repeatable estimate");
for (K = 0; K < 100; K++)
{
KFPrices[K] *= 7.;
}
Scaled = KFFilterCurve();
KFExpect(Scaled == Original, "slow_fast_cancellation: price-unit invariance");
for (K = 0; K < 100; K++)
{
KFPrices[K] = 1. / KFPrices[K];
}
Reflected = KFFilterCurve();
KFExpect(Reflected == -Original, "slow_fast_cancellation: directional reflection");
KFPrices[50] = 0.;
KFExpect(KFFilterCurve() == 0, "slow_fast_cancellation: missing price rejected");
}
void KFExtendedTests()
{
KFExtendedCount = 0;
KFExtendedFailures = 0;
file_write("Log/TS03_KalmanCurve_extended.txt", "TS03_KalmanCurve: extended properties\n", 0);
KFCase_quiet_cycle();
KFCase_mixed_cycles();
KFCase_persistent_upward();
KFCase_persistent_downward();
KFCase_slow_curve();
KFCase_rough_curve();
KFCase_accelerating_path();
KFCase_decelerating_path();
KFCase_volatility_ramp();
KFCase_volatility_decay();
KFCase_cycle_with_drift();
KFCase_near_zero_prices();
KFCase_large_price_units();
KFCase_high_frequency_chop();
KFCase_long_wave_trend();
KFCase_opposing_scales();
KFCase_nonlinear_curve();
KFCase_amplitude_modulation();
KFCase_decaying_event();
KFCase_time_varying_frequency();
KFCase_slow_fast_cancellation();
file_append("Log/TS03_KalmanCurve_extended.txt",
strf("RESULT: %d checks; %d failures\n", KFExtendedCount, KFExtendedFailures));
}
function run()
{
#ifdef STX_SELFTEST
if (is(INITRUN))
{
KFVerify();
KFExtendedTests();
quit("Kalman tests complete");
}
return;
#endif
int T, Forecast;
var KFLotsAllowed, UnitLoss;
BarPeriod = 60;
LookBack = 101;
Capital = 10000.;
StartDate = 20240101;
EndDate = 20241231;
Hedge = 0;
MaxLong = 1;
MaxShort = 1;
if (!asset("EUR/USD"))
{
return;
}
algo("KF03");
if (is(EXITRUN))
{
KFResearchDiagnostics();
return;
}
if (is(LOOKBACK))
{
return;
}
for (T = 0; T < 100; T++)
{
KFPrices[T] = priceClose(T + 1);
}
Forecast = KFFilterCurve();
if (NumOpenLong > 0 && KFState[0] <= 0.)
{
exitLong();
}
if (NumOpenShort > 0 && KFState[0] >= 0.)
{
exitShort();
}
if (Forecast == 0 || NumOpenLong + NumOpenShort > 0)
{
return;
}
if (Balance <= 0. || Equity < 0.85 * Balance || MarginCost <= 0. || LotAmount <= 0.)
{
return;
}
if (PIP <= 0. || PIPCost <= 0.)
{
return;
}
Stop = max(30. * PIP, KFPrices[0] * sqrt(max(0., KFUncertainty[0])) * 4.);
TakeProfit = Stop * 2.5;
LifeTime = 24;
UnitLoss = (Stop + Spread) / PIP * PIPCost + Commission * LotAmount / 10000.;
if (UnitLoss <= 0.)
{
return;
}
KFLotsAllowed = min(Balance * 0.005 / UnitLoss, 2000. / LotAmount);
KFLotsAllowed = min(KFLotsAllowed, max(0., Equity - MarginVal) * 0.8 / MarginCost);
if (KFLotsAllowed < 1.)
{
return;
}
Margin = 0.;
Risk = 0.;
Lots = floor(KFLotsAllowed);
if (Forecast > 0)
{
enterLong();
}
else
{
enterShort();
}
}
|
|
|
Haar Energy
[Re: TipmyPip]
#489727
1 hour ago
1 hour ago
|
Joined: Sep 2017
Posts: 346
TipmyPip
OP
Senior Member
|
OP
Senior Member
Joined: Sep 2017
Posts: 346
|
Haar Energy — The Multiscale Microscope Picture a microscope with several magnification settings. At the finest setting, the market appears full of tiny alternating movements. At broader settings, some of those movements cancel while larger directional structures become visible. This program uses a Haar wavelet transformation to separate those scales rather than fitting a covariance component, an equilibrium spread, or a latent state curve. The input is a chronological block of completed price returns. The transformation repeatedly pairs neighboring observations. Each pair produces an average describing shared movement and a detail describing disagreement inside the pair. The averages are paired again at the next level, producing progressively broader views of the same original information. The program records how much detail energy appears at each scale. A large share of fine scale energy suggests that local noise or rapid alternation dominates the picture. A smaller fine scale share suggests that broader structures deserve more attention. The broadest average is also included when evaluating total energy, preventing persistent drift from disappearing merely because adjacent observations look similar. Direction comes from the signs of recent average components at several intermediate scales. The strategy looks for agreement among the views associated with progressively longer intervals. Positive agreement supports a long position, while negative agreement supports a short position. The program does not infer direction from energy alone, because energy measures strength without distinguishing upward from downward movement. Trades are considered only when the finest details are sufficiently subdued and the broader directional component is meaningful relative to total activity. When fine scale energy becomes dominant, existing positions can be closed. This exit reflects the idea that the clean broad structure has deteriorated into a noisier environment, even if the latest price has not yet crossed a conventional moving average. The diagnostic layer checks energy preservation between the original returns and the transformed representation. It also calculates an entropy measure across detail scales and counts agreement between neighboring phases. These checks help identify whether the transformation is behaving numerically and whether movement is concentrated or dispersed across scales. The microscope reveals structure; it does not prove that the structure will persist. A nonoverlapping Haar transformation is sensitive to where the observation window begins and ends. A trend can also reverse immediately after appearing clean across several scales. Protective stops, constrained sizing, profit targets, and a holding limit remain necessary because visual coherence and predictive reliability are different properties. // Independent model 04: discrete Haar multiresolution energy and phase alignment.
#include <default.c>
var HaarClose[66];
var HaarWork[64];
var HaarApprox[64];
var HaarEnergy[6];
var HaarPhase[6];
var HaarRatio = 0.;
var HaarDirection = 0.;
int HaarTransformSignal()
{
int I, Level, Size;
var Detail, Fine, Coarse, Total;
HaarRatio = 0.;
HaarDirection = 0.;
for (I = 0; I < 66; I++)
{
if (!(HaarClose[I] > 0.))
{
return 0;
}
}
// DWT input is chronological. Boundary phase always ends at latest closed bar.
for (I = 0; I < 64; I++)
{
HaarWork[I] = log(HaarClose[63 - I] / HaarClose[64 - I]);
}
Size = 64;
for (Level = 0; Level < 6; Level++)
{
HaarEnergy[Level] = 0.;
for (I = 0; I < Size / 2; I++)
{
HaarApprox[I] = (HaarWork[2 * I] + HaarWork[2 * I + 1]) / sqrt(2.);
Detail = (HaarWork[2 * I] - HaarWork[2 * I + 1]) / sqrt(2.);
HaarEnergy[Level] += Detail * Detail;
}
HaarPhase[Level] = HaarApprox[Size / 2 - 1];
Size /= 2;
for (I = 0; I < Size; I++)
{
HaarWork[I] = HaarApprox[I];
}
}
Fine = HaarEnergy[0] + HaarEnergy[1];
Coarse = HaarEnergy[2] + HaarEnergy[3] + HaarEnergy[4] + HaarEnergy[5];
Total = Fine + Coarse + HaarWork[0] * HaarWork[0];
if (Total < 1.e-20)
{
return 0;
}
HaarRatio = Fine / Total;
HaarDirection = HaarPhase[3] / sqrt(Total);
// Low fine-scale energy with consistent 4-, 8-, and 16-bar phases.
if (HaarRatio > 0.45 || abs(HaarDirection) < 0.15)
{
return 0;
}
if (HaarPhase[1] > 0. && HaarPhase[2] > 0. && HaarPhase[3] > 0.)
{
return 1;
}
if (HaarPhase[1] < 0. && HaarPhase[2] < 0. && HaarPhase[3] < 0.)
{
return -1;
}
return 0;
}
void HaarSelfCheck()
{
int I, Broken;
Broken = 0;
for (I = 0; I < 66; I++)
{
HaarClose[I] = 1.;
}
if (HaarTransformSignal() != 0)
{
Broken++;
}
for (I = 0; I < 66; I++)
{
HaarClose[I] = exp(-0.001 * I);
}
if (HaarTransformSignal() != 1)
{
Broken++;
}
for (I = 0; I < 66; I++)
{
HaarClose[I] = exp(0.001 * I);
}
if (HaarTransformSignal() != -1)
{
Broken++;
}
HaarClose[8] = -1.;
if (HaarTransformSignal() != 0)
{
Broken++;
}
file_write("Log/TS04_HaarEnergy_test.txt", strf("4 checks; %d failures\n", Broken), 0);
}
var HaarSpectralEntropy()
{
int L;
var Total, Probability, Entropy;
Total = 0.;
Entropy = 0.;
for (L = 0; L < 6; L++)
{
Total += HaarEnergy[L];
}
if (Total < 1.e-24)
{
return 0.;
}
for (L = 0; L < 6; L++)
{
Probability = HaarEnergy[L] / Total;
if (Probability > 1.e-15)
{
Entropy -= Probability * log(Probability) / log(6.);
}
}
return Entropy;
}
var HaarParsevalError()
{
int L;
var TimeEnergy, WaveEnergy, R;
TimeEnergy = 0.;
WaveEnergy = HaarWork[0] * HaarWork[0];
for (L = 0; L < 64; L++)
{
R = log(HaarClose[L] / HaarClose[L + 1]);
TimeEnergy += R * R;
}
for (L = 0; L < 6; L++)
{
WaveEnergy += HaarEnergy[L];
}
if (TimeEnergy < 1.e-24)
{
return 0.;
}
return abs(TimeEnergy - WaveEnergy) / TimeEnergy;
}
int HaarPhaseAgreements()
{
int L, Count;
Count = 0;
for (L = 0; L < 5; L++)
{
if (HaarPhase[L] * HaarPhase[L + 1] > 0.)
{
Count++;
}
}
return Count;
}
void HaarResearchDiagnostics()
{
printf("\nHaar entropy %.6g; Parseval relative error %.6g; phase agreements %d",
HaarSpectralEntropy(), HaarParsevalError(), HaarPhaseAgreements());
}
// ============================================================================
// EXTENDED REGRESSION SUITE: deterministic properties over market regimes
// These functions run ONLY under STX_SELFTEST; they never place orders.
// ============================================================================
int HaarExtendedCount = 0;
int HaarExtendedFailures = 0;
void HaarExpect(int Passed, string Description)
{
HaarExtendedCount++;
if (!Passed)
{
HaarExtendedFailures++;
file_append("Log/TS04_HaarEnergy_extended.txt", strf("FAIL: %s\n", Description));
}
}
void HaarCase_quiet_cycle()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp(0.0005 * sin(0.17 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "quiet_cycle: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "quiet_cycle: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "quiet_cycle: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "quiet_cycle: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "quiet_cycle: missing price rejected");
}
void HaarCase_mixed_cycles()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp(0.003 * sin(0.23 * K) + 0.0007 * cos(0.81 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "mixed_cycles: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "mixed_cycles: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "mixed_cycles: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "mixed_cycles: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "mixed_cycles: missing price rejected");
}
void HaarCase_persistent_upward()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp(-0.001 * K + 0.0001 * sin(0.53 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "persistent_upward: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "persistent_upward: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "persistent_upward: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "persistent_upward: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "persistent_upward: missing price rejected");
}
void HaarCase_persistent_downward()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp(0.001 * K + 0.0001 * cos(0.47 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "persistent_downward: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "persistent_downward: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "persistent_downward: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "persistent_downward: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "persistent_downward: missing price rejected");
}
void HaarCase_slow_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp(0.004 * cos(0.08 * K) + 0.0002 * sin(0.7 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "slow_curve: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "slow_curve: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "slow_curve: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "slow_curve: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "slow_curve: missing price rejected");
}
void HaarCase_rough_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp(0.001 * sin(1.7 * K) + 0.0005 * cos(2.3 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "rough_curve: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "rough_curve: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "rough_curve: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "rough_curve: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "rough_curve: missing price rejected");
}
void HaarCase_accelerating_path()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp(-0.0001 * K - 0.000002 * K * K + 0.0002 * sin(0.41 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "accelerating_path: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "accelerating_path: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "accelerating_path: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "accelerating_path: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "accelerating_path: missing price rejected");
}
void HaarCase_decelerating_path()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp(-0.0008 * K + 0.000002 * K * K + 0.0002 * cos(0.31 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "decelerating_path: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "decelerating_path: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "decelerating_path: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "decelerating_path: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "decelerating_path: missing price rejected");
}
void HaarCase_volatility_ramp()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp((0.0001 + K * 0.00001) * sin(0.37 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "volatility_ramp: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "volatility_ramp: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "volatility_ramp: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "volatility_ramp: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "volatility_ramp: missing price rejected");
}
void HaarCase_volatility_decay()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp(0.006 * exp(-0.02 * K) * cos(0.7 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "volatility_decay: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "volatility_decay: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "volatility_decay: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "volatility_decay: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "volatility_decay: missing price rejected");
}
void HaarCase_cycle_with_drift()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp(-0.0002 * K + 0.003 * sin(0.19 * K) + 0.0005 * cos(0.61 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "cycle_with_drift: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "cycle_with_drift: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "cycle_with_drift: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "cycle_with_drift: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "cycle_with_drift: missing price rejected");
}
void HaarCase_near_zero_prices()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 0.0001 * exp(0.002 * sin(0.29 * K) + 0.001 * cos(0.13 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "near_zero_prices: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "near_zero_prices: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "near_zero_prices: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "near_zero_prices: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "near_zero_prices: missing price rejected");
}
void HaarCase_large_price_units()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 10000.0 * exp(0.003 * sin(0.11 * K) + 0.0008 * cos(0.43 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "large_price_units: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "large_price_units: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "large_price_units: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "large_price_units: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "large_price_units: missing price rejected");
}
void HaarCase_high_frequency_chop()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp(0.0008 * cos(2.8 * K) + 0.001 * sin(0.23 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "high_frequency_chop: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "high_frequency_chop: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "high_frequency_chop: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "high_frequency_chop: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "high_frequency_chop: missing price rejected");
}
void HaarCase_long_wave_trend()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp(-0.0005 * K + 0.002 * cos(0.07 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "long_wave_trend: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "long_wave_trend: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "long_wave_trend: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "long_wave_trend: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "long_wave_trend: missing price rejected");
}
void HaarCase_opposing_scales()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp(0.005 * sin(0.09 * K) - 0.002 * sin(0.59 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "opposing_scales: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "opposing_scales: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "opposing_scales: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "opposing_scales: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "opposing_scales: missing price rejected");
}
void HaarCase_nonlinear_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp(0.002 * sin(0.021 * K * K) + 0.001 * cos(0.33 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "nonlinear_curve: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "nonlinear_curve: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "nonlinear_curve: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "nonlinear_curve: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "nonlinear_curve: missing price rejected");
}
void HaarCase_amplitude_modulation()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp(0.002 * sin(0.3 * K) * cos(0.04 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "amplitude_modulation: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "amplitude_modulation: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "amplitude_modulation: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "amplitude_modulation: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "amplitude_modulation: missing price rejected");
}
void HaarCase_decaying_event()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp(0.01 * exp(-0.2 * K) + 0.0003 * sin(0.71 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "decaying_event: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "decaying_event: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "decaying_event: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "decaying_event: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "decaying_event: missing price rejected");
}
void HaarCase_time_varying_frequency()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp(0.002 * cos(0.004 * K * K + 0.11 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "time_varying_frequency: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "time_varying_frequency: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "time_varying_frequency: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "time_varying_frequency: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "time_varying_frequency: missing price rejected");
}
void HaarCase_slow_fast_cancellation()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1.0 * exp(0.004 * sin(0.07 * K) + 0.0013 * cos(0.97 * K));
}
Original = HaarTransformSignal();
HaarExpect(Original >= -1 && Original <= 1, "slow_fast_cancellation: bounded direction");
Again = HaarTransformSignal();
HaarExpect(Again == Original, "slow_fast_cancellation: repeatable estimate");
for (K = 0; K < 66; K++)
{
HaarClose[K] *= 7.;
}
Scaled = HaarTransformSignal();
HaarExpect(Scaled == Original, "slow_fast_cancellation: price-unit invariance");
for (K = 0; K < 66; K++)
{
HaarClose[K] = 1. / HaarClose[K];
}
Reflected = HaarTransformSignal();
HaarExpect(Reflected == -Original, "slow_fast_cancellation: directional reflection");
HaarClose[33] = 0.;
HaarExpect(HaarTransformSignal() == 0, "slow_fast_cancellation: missing price rejected");
}
void HaarExtendedTests()
{
HaarExtendedCount = 0;
HaarExtendedFailures = 0;
file_write("Log/TS04_HaarEnergy_extended.txt", "TS04_HaarEnergy: extended properties\n", 0);
HaarCase_quiet_cycle();
HaarCase_mixed_cycles();
HaarCase_persistent_upward();
HaarCase_persistent_downward();
HaarCase_slow_curve();
HaarCase_rough_curve();
HaarCase_accelerating_path();
HaarCase_decelerating_path();
HaarCase_volatility_ramp();
HaarCase_volatility_decay();
HaarCase_cycle_with_drift();
HaarCase_near_zero_prices();
HaarCase_large_price_units();
HaarCase_high_frequency_chop();
HaarCase_long_wave_trend();
HaarCase_opposing_scales();
HaarCase_nonlinear_curve();
HaarCase_amplitude_modulation();
HaarCase_decaying_event();
HaarCase_time_varying_frequency();
HaarCase_slow_fast_cancellation();
file_append("Log/TS04_HaarEnergy_extended.txt",
strf("RESULT: %d checks; %d failures\n", HaarExtendedCount, HaarExtendedFailures));
}
function run()
{
#ifdef STX_SELFTEST
if (is(INITRUN))
{
HaarSelfCheck();
HaarExtendedTests();
quit("Haar tests complete");
}
return;
#endif
int I, WaveSide;
var AllowedLots, Cost;
BarPeriod = 60;
LookBack = 67;
StartDate = 20240101;
EndDate = 20241231;
Capital = 10000.;
MaxLong = 1;
MaxShort = 1;
Hedge = 0;
if (!asset("EUR/USD"))
{
return;
}
algo("HAAR04");
if (is(EXITRUN))
{
HaarResearchDiagnostics();
return;
}
if (is(LOOKBACK))
{
return;
}
for (I = 0; I < 66; I++)
{
HaarClose[I] = priceClose(I + 1);
}
WaveSide = HaarTransformSignal();
if (HaarRatio > 0.65)
{
exitLong();
exitShort();
return;
}
if (WaveSide > 0)
{
exitShort();
}
if (WaveSide < 0)
{
exitLong();
}
if (WaveSide == 0 || NumOpenLong + NumOpenShort > 0)
{
return;
}
if (PIP <= 0. || PIPCost <= 0. || LotAmount <= 0. || MarginCost <= 0.)
{
return;
}
if (Balance <= 0. || Equity < Balance * 0.85)
{
return;
}
Stop = 45. * PIP;
TakeProfit = 135. * PIP;
LifeTime = 32;
Cost = PIPCost * (Stop + Spread) / PIP + Commission * LotAmount / 10000.;
if (Cost <= 0.)
{
return;
}
AllowedLots = min(2000. / LotAmount, Balance * 0.005 / Cost);
AllowedLots = min(AllowedLots, max(0., Equity - MarginVal) * 0.8 / MarginCost);
if (AllowedLots < 1.)
{
return;
}
Lots = floor(AllowedLots);
Margin = 0.;
Risk = 0.;
if (WaveSide > 0)
{
enterLong();
}
else
{
enterShort();
}
}
|
|
|
Bipower Jump Fade
[Re: TipmyPip]
#489728
1 hour ago
1 hour ago
|
Joined: Sep 2017
Posts: 346
TipmyPip
OP
Senior Member
|
OP
Senior Member
Joined: Sep 2017
Posts: 346
|
Bipower Jump Fade — The Shock Absorber Imagine the market as a vehicle moving along a road with ordinary vibration and occasional severe bumps. Ordinary vibration represents continuous price fluctuation. A severe bump represents a discontinuity that is unusually large compared with the surrounding movement. This strategy attempts to separate those two sources of activity and then considers whether an isolated bump might be followed by a partial rebound. The program first estimates the ordinary background scale from an older window that excludes the latest possible jump. Its main tool is bipower variation, which uses neighboring absolute returns rather than relying only on squared individual returns. The purpose is to obtain a continuous movement reference that is less easily dominated by one isolated price shock. It then examines a shorter local window, comparing ordinary realized variation with its bipower counterpart. When realized variation substantially exceeds the continuous movement proxy, the excess is interpreted as evidence that discontinuous movement may matter. The program also checks whether the newest completed return is exceptionally large relative to the historical continuous scale. Both checks must support the shock interpretation. A large current move by itself is not enough if the local variation comparison does not agree. The preceding return must also remain below the program's jump threshold. That additional gate is intended to avoid treating a cluster of violent movements as a single temporary interruption. The trading response is deliberately contrarian. An isolated upward shock supports a short position, and an isolated downward shock supports a long position. The program remembers the completed price immediately before the shock as an analytical anchor. If price subsequently returns to that anchor in the expected direction, the position can be closed without waiting for the full profit target. This is a short horizon response, not a patient equilibrium strategy. Positions have a brief maximum lifetime, and the stop distance adapts partly to the estimated continuous scale. Model diagnostics include fourth power activity, truncated variation, and a count of recent unusually large moves. These describe the surrounding shock environment rather than certifying that a reversal is inevitable. The shock absorber metaphor has an important boundary: some bumps are the beginning of a different road. News can permanently reprice an asset, and an apparently isolated jump can initiate sustained movement. The implemented thresholds are heuristic screening rules, not a calibrated statistical significance test. Fading a genuine repricing event can therefore be costly despite disciplined sizing and protective exits. // Independent model 05: realized variance, bipower variation and jump reversal.
#include <default.c>
var BVClose[100];
var BVContinuous = 0.;
var BVJumpShare = 0.;
var BVStandardJump = 0.;
var BVAnchor = 0.;
int BVDetectDiscontinuity()
{
int K;
var RV, BPV, LeftReturn, RightReturn, CurrentReturn, BeforeReturn;
BVContinuous = 0.;
BVJumpShare = 0.;
BVStandardJump = 0.;
for (K = 0; K < 100; K++)
{
if (!(BVClose[K] > 0.))
{
return 0;
}
}
// Historical continuous scale EXCLUDES the current possible jump.
BPV = 0.;
for (K = 1; K <= 64; K++)
{
LeftReturn = log(BVClose[K] / BVClose[K + 1]);
RightReturn = log(BVClose[K + 1] / BVClose[K + 2]);
BPV += abs(LeftReturn) * abs(RightReturn) * 1.5707963267948966 / 64.;
}
if (BPV <= 1.e-20)
{
return 0;
}
BVContinuous = BPV;
// Local 16-bar realized variation compared with its bipower counterpart.
RV = 0.;
BPV = 0.;
for (K = 0; K < 16; K++)
{
LeftReturn = log(BVClose[K] / BVClose[K + 1]);
RightReturn = log(BVClose[K + 1] / BVClose[K + 2]);
RV += LeftReturn * LeftReturn;
BPV += abs(LeftReturn) * abs(RightReturn) * 1.5707963267948966;
}
if (RV <= 1.e-20)
{
return 0;
}
BVJumpShare = max(0., RV - BPV) / RV;
CurrentReturn = log(BVClose[0] / BVClose[1]);
BeforeReturn = log(BVClose[1] / BVClose[2]);
BVStandardJump = CurrentReturn / sqrt(BVContinuous);
// No claim of a formal finite-sample jump-test p-value.
if (BVJumpShare < 0.35 || abs(BVStandardJump) < 4.)
{
return 0;
}
if (abs(BeforeReturn) > 3. * sqrt(BVContinuous))
{
return 0;
}
if (CurrentReturn > 0.)
{
return -1;
}
return 1;
}
void BVRunTests()
{
int K, Faults;
Faults = 0;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.;
}
if (BVDetectDiscontinuity() != 0)
{
Faults++;
}
for (K = 0; K < 100; K++)
{
BVClose[K] = exp(0.0001 * sin(K));
}
BVClose[0] = exp(0.01);
if (BVDetectDiscontinuity() != -1)
{
Faults++;
}
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
if (BVDetectDiscontinuity() != 1)
{
Faults++;
}
BVClose[8] = 0.;
if (BVDetectDiscontinuity() != 0)
{
Faults++;
}
file_write("Log/TS05_BipowerJumpFade_test.txt", strf("4 checks; %d failures\n", Faults), 0);
}
var BVQuarticity()
{
int K;
var R, Estimate;
Estimate = 0.;
for (K = 1; K <= 64; K++)
{
R = log(BVClose[K] / BVClose[K + 1]);
Estimate += pow(R, 4.) / 64.;
}
return Estimate;
}
var BVTruncatedVariation(var SigmaMultiple)
{
int K;
var R, Sum, Boundary;
if (BVContinuous <= 0. || SigmaMultiple <= 0.)
{
return 0.;
}
Boundary = SigmaMultiple * sqrt(BVContinuous);
Sum = 0.;
for (K = 0; K < 32; K++)
{
R = log(BVClose[K] / BVClose[K + 1]);
if (abs(R) <= Boundary)
{
Sum += R * R / 32.;
}
}
return Sum;
}
int BVRecentShockCount()
{
int K, Count;
var R;
Count = 0;
if (BVContinuous <= 0.)
{
return 0;
}
for (K = 0; K < 16; K++)
{
R = log(BVClose[K] / BVClose[K + 1]);
if (abs(R) > 4. * sqrt(BVContinuous))
{
Count++;
}
}
return Count;
}
void BVResearchDiagnostics()
{
printf("\nBipower continuous %.6g; quarticity %.6g; truncated32 %.6g; shocks16 %d",
BVContinuous, BVQuarticity(), BVTruncatedVariation(3.), BVRecentShockCount());
}
// ============================================================================
// EXTENDED REGRESSION SUITE: deterministic properties over market regimes
// These functions run ONLY under STX_SELFTEST; they never place orders.
// ============================================================================
int BVExtendedCount = 0;
int BVExtendedFailures = 0;
void BVExpect(int Passed, string Description)
{
BVExtendedCount++;
if (!Passed)
{
BVExtendedFailures++;
file_append("Log/TS05_BipowerJumpFade_extended.txt", strf("FAIL: %s\n", Description));
}
}
void BVCase_quiet_cycle()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp(0.0005 * sin(0.17 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "quiet_cycle: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "quiet_cycle: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "quiet_cycle: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "quiet_cycle: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "quiet_cycle: missing price rejected");
}
void BVCase_mixed_cycles()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp(0.003 * sin(0.23 * K) + 0.0007 * cos(0.81 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "mixed_cycles: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "mixed_cycles: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "mixed_cycles: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "mixed_cycles: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "mixed_cycles: missing price rejected");
}
void BVCase_persistent_upward()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp(-0.001 * K + 0.0001 * sin(0.53 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "persistent_upward: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "persistent_upward: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "persistent_upward: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "persistent_upward: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "persistent_upward: missing price rejected");
}
void BVCase_persistent_downward()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp(0.001 * K + 0.0001 * cos(0.47 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "persistent_downward: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "persistent_downward: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "persistent_downward: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "persistent_downward: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "persistent_downward: missing price rejected");
}
void BVCase_slow_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp(0.004 * cos(0.08 * K) + 0.0002 * sin(0.7 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "slow_curve: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "slow_curve: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "slow_curve: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "slow_curve: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "slow_curve: missing price rejected");
}
void BVCase_rough_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp(0.001 * sin(1.7 * K) + 0.0005 * cos(2.3 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "rough_curve: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "rough_curve: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "rough_curve: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "rough_curve: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "rough_curve: missing price rejected");
}
void BVCase_accelerating_path()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp(-0.0001 * K - 0.000002 * K * K + 0.0002 * sin(0.41 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "accelerating_path: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "accelerating_path: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "accelerating_path: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "accelerating_path: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "accelerating_path: missing price rejected");
}
void BVCase_decelerating_path()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp(-0.0008 * K + 0.000002 * K * K + 0.0002 * cos(0.31 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "decelerating_path: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "decelerating_path: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "decelerating_path: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "decelerating_path: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "decelerating_path: missing price rejected");
}
void BVCase_volatility_ramp()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp((0.0001 + K * 0.00001) * sin(0.37 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "volatility_ramp: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "volatility_ramp: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "volatility_ramp: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "volatility_ramp: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "volatility_ramp: missing price rejected");
}
void BVCase_volatility_decay()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp(0.006 * exp(-0.02 * K) * cos(0.7 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "volatility_decay: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "volatility_decay: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "volatility_decay: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "volatility_decay: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "volatility_decay: missing price rejected");
}
void BVCase_cycle_with_drift()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp(-0.0002 * K + 0.003 * sin(0.19 * K) + 0.0005 * cos(0.61 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "cycle_with_drift: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "cycle_with_drift: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "cycle_with_drift: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "cycle_with_drift: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "cycle_with_drift: missing price rejected");
}
void BVCase_near_zero_prices()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 0.0001 * exp(0.002 * sin(0.29 * K) + 0.001 * cos(0.13 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "near_zero_prices: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "near_zero_prices: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "near_zero_prices: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "near_zero_prices: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "near_zero_prices: missing price rejected");
}
void BVCase_large_price_units()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 10000.0 * exp(0.003 * sin(0.11 * K) + 0.0008 * cos(0.43 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "large_price_units: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "large_price_units: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "large_price_units: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "large_price_units: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "large_price_units: missing price rejected");
}
void BVCase_high_frequency_chop()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp(0.0008 * cos(2.8 * K) + 0.001 * sin(0.23 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "high_frequency_chop: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "high_frequency_chop: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "high_frequency_chop: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "high_frequency_chop: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "high_frequency_chop: missing price rejected");
}
void BVCase_long_wave_trend()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp(-0.0005 * K + 0.002 * cos(0.07 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "long_wave_trend: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "long_wave_trend: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "long_wave_trend: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "long_wave_trend: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "long_wave_trend: missing price rejected");
}
void BVCase_opposing_scales()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp(0.005 * sin(0.09 * K) - 0.002 * sin(0.59 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "opposing_scales: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "opposing_scales: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "opposing_scales: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "opposing_scales: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "opposing_scales: missing price rejected");
}
void BVCase_nonlinear_curve()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp(0.002 * sin(0.021 * K * K) + 0.001 * cos(0.33 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "nonlinear_curve: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "nonlinear_curve: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "nonlinear_curve: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "nonlinear_curve: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "nonlinear_curve: missing price rejected");
}
void BVCase_amplitude_modulation()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp(0.002 * sin(0.3 * K) * cos(0.04 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "amplitude_modulation: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "amplitude_modulation: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "amplitude_modulation: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "amplitude_modulation: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "amplitude_modulation: missing price rejected");
}
void BVCase_decaying_event()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp(0.01 * exp(-0.2 * K) + 0.0003 * sin(0.71 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "decaying_event: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "decaying_event: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "decaying_event: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "decaying_event: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "decaying_event: missing price rejected");
}
void BVCase_time_varying_frequency()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp(0.002 * cos(0.004 * K * K + 0.11 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "time_varying_frequency: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "time_varying_frequency: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "time_varying_frequency: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "time_varying_frequency: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "time_varying_frequency: missing price rejected");
}
void BVCase_slow_fast_cancellation()
{
int K, Original, Again, Scaled, Reflected;
for (K = 0; K < 100; K++)
{
BVClose[K] = 1.0 * exp(0.004 * sin(0.07 * K) + 0.0013 * cos(0.97 * K));
}
Original = BVDetectDiscontinuity();
BVExpect(Original >= -1 && Original <= 1, "slow_fast_cancellation: bounded direction");
Again = BVDetectDiscontinuity();
BVExpect(Again == Original, "slow_fast_cancellation: repeatable estimate");
for (K = 0; K < 100; K++)
{
BVClose[K] *= 7.;
}
Scaled = BVDetectDiscontinuity();
BVExpect(Scaled == Original, "slow_fast_cancellation: price-unit invariance");
for (K = 0; K < 100; K++)
{
BVClose[K] = 1. / BVClose[K];
}
Reflected = BVDetectDiscontinuity();
BVExpect(Reflected == -Original, "slow_fast_cancellation: directional reflection");
BVClose[50] = 0.;
BVExpect(BVDetectDiscontinuity() == 0, "slow_fast_cancellation: missing price rejected");
}
void BVExtendedTests()
{
BVExtendedCount = 0;
BVExtendedFailures = 0;
file_write("Log/TS05_BipowerJumpFade_extended.txt",
"TS05_BipowerJumpFade: extended properties\n", 0);
BVCase_quiet_cycle();
BVCase_mixed_cycles();
BVCase_persistent_upward();
BVCase_persistent_downward();
BVCase_slow_curve();
BVCase_rough_curve();
BVCase_accelerating_path();
BVCase_decelerating_path();
BVCase_volatility_ramp();
BVCase_volatility_decay();
BVCase_cycle_with_drift();
BVCase_near_zero_prices();
BVCase_large_price_units();
BVCase_high_frequency_chop();
BVCase_long_wave_trend();
BVCase_opposing_scales();
BVCase_nonlinear_curve();
BVCase_amplitude_modulation();
BVCase_decaying_event();
BVCase_time_varying_frequency();
BVCase_slow_fast_cancellation();
file_append("Log/TS05_BipowerJumpFade_extended.txt",
strf("RESULT: %d checks; %d failures\n", BVExtendedCount, BVExtendedFailures));
}
function run()
{
#ifdef STX_SELFTEST
if (is(INITRUN))
{
BVRunTests();
BVExtendedTests();
quit("Bipower tests complete");
}
return;
#endif
int K, Fade;
var Cap, Loss;
BarPeriod = 60;
LookBack = 101;
StartDate = 20240101;
EndDate = 20241231;
Capital = 10000.;
Hedge = 0;
MaxLong = 1;
MaxShort = 1;
if (!asset("EUR/USD"))
{
return;
}
algo("BV05");
if (is(INITRUN))
{
BVAnchor = 0.;
}
if (is(EXITRUN))
{
BVResearchDiagnostics();
return;
}
if (is(LOOKBACK))
{
return;
}
for (K = 0; K < 100; K++)
{
BVClose[K] = priceClose(K + 1);
}
Fade = BVDetectDiscontinuity();
// Exit when the price has retraced to the pre-jump level.
if (BVAnchor > 0.)
{
if (NumOpenLong > 0 && BVClose[0] >= BVAnchor)
{
exitLong();
}
if (NumOpenShort > 0 && BVClose[0] <= BVAnchor)
{
exitShort();
}
}
if (Fade == 0 || NumOpenLong + NumOpenShort > 0)
{
return;
}
if (Balance <= 0. || Equity < Balance * 0.85)
{
return;
}
if (PIP <= 0. || PIPCost <= 0. || MarginCost <= 0. || LotAmount <= 0.)
{
return;
}
Stop = max(30. * PIP, BVClose[0] * sqrt(BVContinuous) * 3.);
TakeProfit = Stop;
LifeTime = 3;
Loss = (Stop + Spread) * PIPCost / PIP + Commission * LotAmount / 10000.;
if (Loss <= 0.)
{
return;
}
Cap = min(Balance * 0.005 / Loss, 2000. / LotAmount);
Cap = min(Cap, max(0., Equity - MarginVal) * 0.8 / MarginCost);
if (Cap < 1.)
{
return;
}
Lots = floor(Cap);
Risk = 0.;
Margin = 0.;
BVAnchor = BVClose[1];
if (Fade > 0)
{
enterLong();
}
else
{
enterShort();
}
}
|
|
|
|