|
10/03/26 14:42
// ---- Real-trade scan buffers: rebuilt every bar, nothing carried ---- [R-33] #define SCAN_NA 16 // [R-33] max assets in the list #define SCAN_W 4 // [R-33] events kept per asset and bar int ScanFillN[SCAN_NA], ScanClsN[SCAN_NA]; var ScanFillID[64], ScanFillDir[64], ScanFillPx[64]; // [R-33] 64 = SCAN_NA * SCAN_W var ScanClsID[64], ScanClsPx[64]; int FltBars = 0, FltGreen = 0; // [R-22] filter census, trace only
// [R-33] Index of an asset in the list, -1 if absent. int scanIdx(string Name) { int i; for(i = 0; i < NumAssetsListed; i++) if(strcmp(Name, Assets[i]) == 0) return i; return -1; }
// [R-33] Slot of this bar's fill on asset k in direction Dir, -1 if none. int scanFill(int k, int Dir) { int j; for(j = 0; j < ScanFillN[k]; j++) if(j < SCAN_W && ScanFillDir[k*SCAN_W + j] == Dir) return j; return -1; }
// [R-33] Slot of this bar's close of trade ID on asset k, -1 if none. int scanCls(int k, var ID) { int j; for(j = 0; j < ScanClsN[k]; j++) if(j < SCAN_W && ScanClsID[k*SCAN_W + j] == ID) return j; return -1; }
// [R-28] Classify a closed signal by its move before costs, signed by direction. // [R-28] A move of exactly zero changes nothing. void classify(vars PrevLoss, vars HaveHist, int Dir, int How, var Move) { printf("\n@SIG,%s,%i,%i,%i,%.8f", Asset, (int)Bar, Dir, How, (var)Move); // [R-28] trace if(Move < 0) { PrevLoss[0] = 1; HaveHist[0] = 1; } else if(Move > 0) { PrevLoss[0] = 0; HaveHist[0] = 1; } }
function run() { // ---- Engine / mode setup ---------------------------------- [R-01..R-05] BarPeriod = 1440; // [R-01] daily bars BarOffset = 940; // [R-01] bar starts 15:40 in the bar time zone set(LOGFILE); // [R-03] export logs and statistics files set(TICKS); // [R-02] tick-precise intrabar simulation (D021) Verbose = 3; // [R-03] skipped trades + parameter errors // !! [R-03] Warning 047 verbosity is OBSERVED, not documented -- see 3.1.
// ---- Data source ------------------------------------------------ [R-04] // !! [R-04] '*' reads the year files but keeps Zorro's silent bare-file // !! fallback; the AssetFirstBar preflight below is the only control on it. History = "put your \\Zorro\\History\\*.t6"; // [R-04] NumYears = 20; // [R-05] simulation window EndDate = 20261001; // [R-05] window end pinned (D029 Add. 3)
// ---- Parameter block -------------- [R-07, R-08, R-24, R-26, R-27, R-31] int EntryDays = 20; // [R-07] System 1 entry channel, trading days int ExitDays = 10; // [R-07] System 1 exit channel, trading days int FailDays = 55; // [R-07] failsafe entry channel, trading days int NPeriod = 20; // [R-26] N = average true range over 20 days var StopMult = 2.0; // [R-27] hard stop at 2N from the fill price int BreakTicks = 1; // [R-08] strict break: price increments beyond the level
// ---- Warm-up ---------------------------------------------- [R-09, R-26] LookBack = 70; // [R-09] covers 55-day channel and 20-day N UnstablePeriod = 40; // [R-26] recursive TA-Lib indicators re-run over 40 bars
// ---- Order handling ------------------------------------- [R-10..R-13] EntryTime = 2; // [R-11] pending order lifetime, bars. INERT (see R-18) Fill = 2; // [R-12] alternative order filling (D023) Hedge = 0; // [R-13] netting by ASSET, not by algo
// ---- Universe --------------------------------------------------- [R-06] if(is(INITRUN)) assetList("AssetsDryRunP7"); // [R-06] frozen P7 (D019)
MaxLong = 1; // [R-10] one position per side per asset MaxShort = 1; // [R-10] magnitude live, sign inert (see R-18)
// ---- Real-trade scan: this bar's fills and closes ------------- [R-33] // [R-33] Above the asset loop: enumeration loops cannot be nested. int si, sk, ss; for(si = 0; si < SCAN_NA; si++) { ScanFillN[si] = 0; ScanClsN[si] = 0; } for(past_trades) { if(TradeID != 0) { sk = scanIdx(TradeAsset); if(sk >= 0) { // !! [R-33] TradeInt is 0 on a new trade: OBSERVED, not documented. if(TradeInt[15] == 0) { // [R-33] fill not seen yet TradeInt[15] = 1; if(ScanFillN[sk] < SCAN_W) { ss = sk*SCAN_W + ScanFillN[sk]; ScanFillID[ss] = TradeID; ScanFillDir[ss] = ifelse(TradeIsLong, 1, -1); ScanFillPx[ss] = TradePriceOpen; } ScanFillN[sk] += 1; } if(TradeIsClosed && TradeInt[14] == 0) { // [R-33] close not seen yet TradeInt[14] = 1; if(ScanClsN[sk] < SCAN_W) { ss = sk*SCAN_W + ScanClsN[sk]; ScanClsID[ss] = TradeID; ScanClsPx[ss] = TradePriceClose; } ScanClsN[sk] += 1; } } } } if(is(EXITRUN)) printf("\n@FGR,%i,%i", FltGreen, FltBars); // [R-22] filter census, trace only
for(listed_assets) { // [R-25] for(listed_assets) only assigns Asset; it does not select. if(!asset(Asset)) continue;
// [R-04] Reject any asset whose history does not cover the window. if(AssetFirstBar > 0) { printf("\n SKIP %s -- history starts at bar %i/%i", Asset, (int)AssetFirstBar, (int)NumBars); continue; }
// [R-08] Read PIP inside the loop: it is an asset-specific parameter. var BreakOffset = BreakTicks * PIP;
// ---- Price series ------------------------------------------------ vars H = series(priceHigh()); vars L = series(priceLow());
// ---- Rolling trading-day channels ------------------ [R-15, R-25] // [R-25] Per-asset state lives in series(), NEVER in a bare static. // [R-15] Carried as series so the code can read the level that was // actually RESTING during a given bar -- see R-16. vars ChEntryHigh = series(0), ChEntryLow = series(0); vars ChExitLow = series(0), ChExitHigh = series(0); vars ChFailHigh = series(0), ChFailLow = series(0);
ChEntryHigh[0] = MaxVal(H, EntryDays); // 20-day high ChEntryLow[0] = MinVal(L, EntryDays); // 20-day low ChExitLow[0] = MinVal(L, ExitDays); // 10-day low ChExitHigh[0] = MaxVal(H, ExitDays); // 10-day high ChFailHigh[0] = MaxVal(H, FailDays); // 55-day high ChFailLow[0] = MinVal(L, FailDays); // 55-day low
// ---- Volatility primitive N -------------------------------- [R-26] vars N = series(ATR(NPeriod));
// ---- Warm-up counter ---------------------------------------- [R-09] vars BarsSeen = series(0); BarsSeen[0] = BarsSeen[1] + 1;
// ---- Shadow-tracker series -------------------- [R-22, R-25, R-28] // [R-25] Created ABOVE the warm-up gate: series() calls must never be // skipped by a bar-varying condition. Only their [0] elements are set // conditionally, further down. vars ShDir = series(0); // shadow signal: 0 none, +1 long, -1 short vars ShEntry = series(0); // shadow entry price (level + break offset) vars ShAdv = series(0); // [R-28] the 2N adverse level, fixed at signal vars PrevLoss = series(0); // 1 = last CLOSED shadow signal was a loser vars HaveHist = series(0); // 1 = at least one shadow signal has closed
ShDir[0] = ShDir[1]; ShEntry[0] = ShEntry[1]; ShAdv[0] = ShAdv[1]; PrevLoss[0] = PrevLoss[1]; HaveHist[0] = HaveHist[1];
// ---- Real-trade link series ---------------------------- [R-33, R-25] vars TrkID = series(0); // [R-33] real trade tied to the open signal, 0 none vars PlL = series(0); // [R-33] 1 = a 20-day long entry rested this bar vars PlS = series(0); // [R-33] 1 = a 20-day short entry rested this bar TrkID[0] = TrkID[1];
// [R-09] Warm-up gate. Every series() call for this asset is ABOVE it. if(BarsSeen[0] < 70) continue;
// ---- Level-or-distance preflight -------------------- [R-17, R-32] var HalfPrice = 0.5 * priceClose(); bool badEntryHigh = (ChEntryHigh[0] < HalfPrice); bool badEntryLow = (ChEntryLow[0] < HalfPrice); bool badExitLow = (ChExitLow[0] < HalfPrice); bool badExitHigh = (ChExitHigh[0] < HalfPrice); bool badFailHigh = (ChFailHigh[0] < HalfPrice); bool badFailLow = (ChFailLow[0] < HalfPrice); bool badStopDist = (StopMult * N[0] >= HalfPrice);
if(badEntryHigh || badEntryLow || badExitLow || badExitHigh || badFailHigh || badFailLow) printf("\n!!! %s bar %i: channel level below half the asset price %.5f (EntryHigh %.5f, EntryLow %.5f, ExitLow %.5f, ExitHigh %.5f, FailHigh %.5f, FailLow %.5f) -- Zorro reads such a value as a DISTANCE, not a level (stop.htm).", Asset, (int)Bar, (var)HalfPrice, (var)ChEntryHigh[0], (var)ChEntryLow[0], (var)ChExitLow[0], (var)ChExitHigh[0], (var)ChFailHigh[0], (var)ChFailLow[0]); if(badStopDist) printf("\n!!! %s bar %i: 2N stop distance %.5f is at or above half the asset price %.5f -- Zorro would read it as a LEVEL, not a distance (stop.htm).", Asset, (int)Bar, (var)(StopMult * N[0]), (var)HalfPrice); // [R-32] Exit levels are NOT gated -- suppressing an exit removes protection. if(badExitLow || badExitHigh) printf("\n@XLV,%s,%i,%.8f,%.8f,%.8f", Asset, (int)Bar, (var)ChExitLow[0], (var)ChExitHigh[0], (var)HalfPrice);
// ---- One-bar order-placement lag -------------------------- [R-16] // [R-16] Convention: every TEST reads [1], every ORDER is placed with [0]. var RestEntryHigh = ChEntryHigh[1]; var RestEntryLow = ChEntryLow[1]; var RestExitLow = ChExitLow[1]; var RestExitHigh = ChExitHigh[1]; var RestFailHigh = ChFailHigh[1]; var RestFailLow = ChFailLow[1]; var RestN = N[1];
// [R-08] Entry breaks are STRICT: the offset is in the test and in the // order, so shadow and real fill on the same bar at the same price. bool hitEntryHigh = (H[0] >= RestEntryHigh + BreakOffset); bool hitEntryLow = (L[0] <= RestEntryLow - BreakOffset); // [R-20] Exit stops are taken AT the level -- the break rule is on entries. bool hitExitLow = (L[0] <= RestExitLow); bool hitExitHigh = (H[0] >= RestExitHigh);
// ---- Shadow signal bookkeeping ---------- [R-22, R-28, R-21, R-33] int ScanK = scanIdx(Asset); // [R-33] int RealClosed = 0, Slot = -1, FillSlot = -1, OpenDir = 0; var RealExit = 0, Rested20 = 0; if(TrkID[0] != 0) { Slot = scanCls(ScanK, TrkID[0]); if(Slot >= 0) { RealClosed = 1; RealExit = ScanClsPx[ScanK*SCAN_W + Slot]; } } int ShStart = ShDir[0]; // [R-22] shadow state at bar start if(ShStart != 0 && TrkID[0] != 0) { // [R-33] A taken breakout closes with its real trade, never on the shadow tests. if(RealClosed) { classify(PrevLoss, HaveHist, ShStart, 3, (RealExit - ShEntry[0]) * ShStart); ShDir[0] = 0; } } else if(ShStart > 0) { // [R-28] 2N adverse excursion tested BEFORE the 10-day exit. if(L[0] <= ShAdv[0]) { classify(PrevLoss, HaveHist, 1, 1, ShAdv[0] - ShEntry[0]); ShDir[0] = 0; } else if(hitExitLow) { classify(PrevLoss, HaveHist, 1, 2, RestExitLow - ShEntry[0]); ShDir[0] = 0; } } else if(ShStart < 0) { if(H[0] >= ShAdv[0]) { classify(PrevLoss, HaveHist, -1, 1, ShEntry[0] - ShAdv[0]); ShDir[0] = 0; } else if(hitExitHigh) { classify(PrevLoss, HaveHist, -1, 2, ShEntry[0] - RestExitHigh); ShDir[0] = 0; } } if(RealClosed) TrkID[0] = 0; if(ShStart == 0) { // [R-21] Outside-bar tie-break: long tested first, deterministically. if(hitEntryHigh) OpenDir = 1; else if(hitEntryLow) OpenDir = -1; // [R-22] No same-bar close: a shadow opened here is tested from // the NEXT bar. } else if(ShDir[0] == 0) // [R-22] closed on this bar { // [R-22] An OPPOSITE break on the closing bar opens a new signal. if(ShStart > 0 && hitEntryLow) OpenDir = -1; else if(ShStart < 0 && hitEntryHigh) OpenDir = 1; } if(OpenDir != 0) { ShDir[0] = OpenDir; if(OpenDir > 0) ShEntry[0] = RestEntryHigh + BreakOffset; else ShEntry[0] = RestEntryLow - BreakOffset; ShAdv[0] = ShEntry[0] - OpenDir * StopMult * RestN; // [R-28] // [R-33] Taken: a same-direction fill this bar, from a 20-day order rested last bar. FillSlot = scanFill(ScanK, OpenDir); Rested20 = PlS[1]; if(OpenDir > 0) Rested20 = PlL[1]; if(FillSlot >= 0 && Rested20 == 1) { TrkID[0] = ScanFillID[ScanK*SCAN_W + FillSlot]; ShEntry[0] = ScanFillPx[ScanK*SCAN_W + FillSlot]; // [R-33] the real fill Slot = scanCls(ScanK, TrkID[0]); if(Slot >= 0) { // [R-33] The real trade also closed on its fill bar. classify(PrevLoss, HaveHist, OpenDir, 3, (ScanClsPx[ScanK*SCAN_W + Slot] - ShEntry[0]) * OpenDir); ShDir[0] = 0; TrkID[0] = 0; } } }
// ---- Previous-signal filter ------------------------ [R-22, R-23] // [R-22] Read AFTER this bar's shadow update. // [R-23] First signal on each asset is taken: there is no previous one. bool filterOK = (HaveHist[0] == 0) || (PrevLoss[0] > 0); FltBars += 1; if(filterOK) FltGreen += 1; // [R-22] census, trace only
// ---- Real orders ---- [R-14, R-18..R-21, R-27, R-29..R-32] if(NumOpenLong > 0) { // [R-19] Cancel the opposite pending order left alive by the fill. exitShort(0); // [R-20, R-30] Ratcheted 10-day channel exit, re-issued each bar at // the current level; it can only tighten. [R-27] the 2N stop stays // attached and the nearer of the two closes the trade. exitLong(0, ChExitLow[0]); } else if(NumOpenShort > 0) { exitLong(0); exitShort(0, ChExitHigh[0]); } else { // [R-18] Cancel yesterday's unfilled orders, then re-place fresh. // Reached ONLY when flat -- the branch structure IS the guard. exitLong(0); exitShort(0);
Stop = StopMult * N[0]; // [R-27] 2N hard stop, a DISTANCE to the fill price
// [R-29] Filter green: the 20-day levels. Filter red: the 55-day failsafe. var LongLevel = ChEntryHigh[0]; var ShortLevel = ChEntryLow[0]; bool badLong = badEntryHigh; bool badShort = badEntryLow; if(!filterOK) { LongLevel = ChFailHigh[0]; badLong = badFailHigh; ShortLevel = ChFailLow[0]; badShort = badFailLow; }
// [R-14] Family B: resting stop orders AT the level, not close tests. // [R-08] Entry carries the break offset. // [R-32] An order the engine would misread is suppressed, per side. if(badLong || badStopDist) printf("\n@SUP,%s,%i,L,%.8f,%.8f,%.8f", Asset, (int)Bar, (var)LongLevel, (var)HalfPrice, (var)(StopMult * N[0])); else { enterLong(1, LongLevel + BreakOffset); PlL[0] = filterOK; // [R-33] 1 = rested at the 20-day level }
if(badShort || badStopDist) printf("\n@SUP,%s,%i,S,%.8f,%.8f,%.8f", Asset, (int)Bar, (var)ShortLevel, (var)HalfPrice, (var)(StopMult * N[0])); else { enterShort(1, ShortLevel - BreakOffset); PlS[0] = filterOK; // [R-33] 1 = rested at the 20-day level } } } }
6
2,291
Read More
|