forked from mnbvc188199/Warrior_EA
Systematic audit of the alt-data stack against "any symbol, any timeframe",
prompted by the H4 surprise. Findings, each fixed:
CAPACITY: ALTDATA_MAX_FEATURES was 16 with FX symbols already at 15 - the next
added column would have been silently truncated by a MathMin. Raised to 32,
pin-chars 512 -> 1024.
SYMBOL NAMES: the panel reload path re-derived symbol/timeframe by splitting
the file path on its FIRST underscore - mis-parsing every symbol containing
one (OANDA-style EUR_USD and US_500 are in our own alias lists) and knowing
only three timeframes. It now stores the (symbol, period) Load() was called
with and reuses them verbatim. Path-hostile characters in broker symbols
("EUR/USD") are sanitized by a shared AltDataFileSymbol() used by the panel,
the fetcher and TunedPeriods, so a slash cannot route a write into an
unintended subfolder.
DOWNLOAD VALIDATION: every FRED-family fetch now enforces a per-series
plausibility band (VIX 1-200, yields -5..30, CPI index 20-1000, ...) -
StringToDouble on transport garbage returns 0.0, and one absurd value poisons
every change/percentile feature computed across it (the BatchNorm NaN-latch
incident came from exactly one huge-but-finite input). Rejected rows are
counted and reported, never dropped silently.
LOUD EMPTINESS: a successful response with zero observations on an empty
cache now says so - naming the series (wrong id / format drift) or the COT
predicate (the unverified like-clauses) instead of leaving 0-filled features
unexplained. GEX gains a truncation guard: a day-over-day contract-count
collapse >50% is the fingerprint of a partial 13 MB download, not of markets,
and is skipped rather than recorded as a plausible-but-wrong number.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
122 lines
6.5 KiB
MQL5
122 lines
6.5 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| TunedPeriods.mqh |
|
|
//| AnimateDread |
|
|
//+------------------------------------------------------------------+
|
|
//| Chart-level tuned indicator periods - ONE source of truth for |
|
|
//| every consumer of "what period does this indicator run at". |
|
|
//| |
|
|
//| The 2026-08-16 menu pruning made ALL indicator parameters |
|
|
//| tuner-owned (see Inputs.mqh). That leaves two consumers that must |
|
|
//| agree: the AI features (whose per-model tuned values persist in |
|
|
//| the .nnw) and the classic votes + signal-DB key (chart-level, |
|
|
//| configured once at OnInit). This file is the bridge: a gated |
|
|
//| tuner INSTALL writes the winning periods here; the next attach |
|
|
//| reads them BEFORE the classic signals are configured and BEFORE |
|
|
//| the DB fingerprint is computed, so votes, DB key and tuner seeds |
|
|
//| all describe the same indicators. |
|
|
//| |
|
|
//| ADOPTION IS RESTART-GRAINED BY DESIGN: an install mid-run changes |
|
|
//| the AI features immediately (fresh topology) but classic votes |
|
|
//| and the DB key keep their init-time values until the next attach. |
|
|
//| Re-configuring framework signal indicators mid-run would need the |
|
|
//| ReInitADIndicators treatment (handle release, cache invalidation) |
|
|
//| for machinery that changes at most once per tuning install - |
|
|
//| restart-grained is simpler and cannot leak handles. |
|
|
//| |
|
|
//| The DB SEMANTICS consequence is intended: the DB fingerprint |
|
|
//| hashes these adopted values, so new periods = new database = the |
|
|
//| per-pattern win-rate history never blends rows from two different |
|
|
//| indicator definitions (the January-August 2026 blending trap). |
|
|
//+------------------------------------------------------------------+
|
|
#ifndef WARRIOR_TUNED_PERIODS_MQH
|
|
#define WARRIOR_TUNED_PERIODS_MQH
|
|
//--- Defaults are the seed constants; LoadTunedPeriods() overwrites from the chart's file if present.
|
|
int g_TunedMaPeriod = (int)PeriodMA;
|
|
int g_TunedMaType = (int)MA_Type;
|
|
int g_TunedRsiPeriod = (int)PeriodRSI;
|
|
int g_TunedMacdFast = (int)MACD_PeriodFast;
|
|
int g_TunedMacdSlow = (int)MACD_PeriodSlow;
|
|
int g_TunedMacdSignal = (int)MACD_PeriodSignal;
|
|
int g_TunedIchiTenkan = (int)Ichimoku_PeriodTenkan;
|
|
int g_TunedIchiKijun = (int)Ichimoku_PeriodKijun;
|
|
int g_TunedIchiSenkou = (int)Ichimoku_PeriodSenkou;
|
|
|
|
string TunedPeriodsFileName(void)
|
|
{
|
|
//--- broker symbols can carry path-hostile characters ("EUR/USD"); writing through
|
|
//--- them fails or lands in an unintended subfolder (same rule as AltDataFileSymbol)
|
|
string sym = _Symbol;
|
|
StringReplace(sym, "/", "-");
|
|
StringReplace(sym, "\\", "-");
|
|
StringReplace(sym, ":", "-");
|
|
StringReplace(sym, "*", "-");
|
|
StringReplace(sym, "?", "-");
|
|
StringReplace(sym, "\"", "-");
|
|
StringReplace(sym, "<", "-");
|
|
StringReplace(sym, ">", "-");
|
|
StringReplace(sym, "|", "-");
|
|
return "Warrior_EA\\TunedPeriods_" + sym + "_" +
|
|
StringSubstr(EnumToString((ENUM_TIMEFRAMES)_Period), 7) + ".cfg";
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Read the chart's adopted periods, if any. Call at the TOP of |
|
|
//| OnInit - before ComputeDbConfigFingerprint() and before the |
|
|
//| classic signals are configured. Missing file = seeds stand. |
|
|
//+------------------------------------------------------------------+
|
|
void LoadTunedPeriods(void)
|
|
{
|
|
string name = TunedPeriodsFileName();
|
|
if(!FileIsExist(name, FILE_COMMON))
|
|
return;
|
|
int h = FileOpen(name, FILE_READ | FILE_TXT | FILE_ANSI | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_COMMON);
|
|
if(h == INVALID_HANDLE)
|
|
return;
|
|
string line = FileReadString(h);
|
|
FileClose(h);
|
|
string f[];
|
|
if(StringSplit(line, ';', f) < 9)
|
|
{
|
|
Print("TunedPeriods: " + name + " is malformed ('" + line + "') - keeping the seed defaults.");
|
|
return;
|
|
}
|
|
g_TunedMaPeriod = (int)StringToInteger(f[0]);
|
|
g_TunedMaType = (int)StringToInteger(f[1]);
|
|
g_TunedRsiPeriod = (int)StringToInteger(f[2]);
|
|
g_TunedMacdFast = (int)StringToInteger(f[3]);
|
|
g_TunedMacdSlow = (int)StringToInteger(f[4]);
|
|
g_TunedMacdSignal = (int)StringToInteger(f[5]);
|
|
g_TunedIchiTenkan = (int)StringToInteger(f[6]);
|
|
g_TunedIchiKijun = (int)StringToInteger(f[7]);
|
|
g_TunedIchiSenkou = (int)StringToInteger(f[8]);
|
|
PrintFormat("TunedPeriods: adopted from %s - MA %d/type %d, RSI %d, MACD %d/%d/%d, Ichimoku %d/%d/%d "
|
|
"(classic votes, DB key and tuner seeds all use these).",
|
|
name, g_TunedMaPeriod, g_TunedMaType, g_TunedRsiPeriod,
|
|
g_TunedMacdFast, g_TunedMacdSlow, g_TunedMacdSignal,
|
|
g_TunedIchiTenkan, g_TunedIchiKijun, g_TunedIchiSenkou);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Persist a gated tuner install's winning periods. Called from |
|
|
//| TuneIndicatorsByFilter on INSTALL only - never for a rejected |
|
|
//| winner, never per trial. Last install wins across ensemble |
|
|
//| members; the values take chart-wide effect on the next attach. |
|
|
//+------------------------------------------------------------------+
|
|
void SaveTunedPeriods(int maPeriod, int maType, int rsiPeriod,
|
|
int macdFast, int macdSlow, int macdSignal,
|
|
int ichiTenkan, int ichiKijun, int ichiSenkou)
|
|
{
|
|
string name = TunedPeriodsFileName();
|
|
int h = FileOpen(name, FILE_WRITE | FILE_TXT | FILE_ANSI | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_COMMON);
|
|
if(h == INVALID_HANDLE)
|
|
{
|
|
PrintFormat("TunedPeriods: cannot write %s (%d) - install still applies to this model's "
|
|
"features; classic votes keep their current periods.", name, GetLastError());
|
|
return;
|
|
}
|
|
FileWriteString(h, StringFormat("%d;%d;%d;%d;%d;%d;%d;%d;%d\n",
|
|
maPeriod, maType, rsiPeriod, macdFast, macdSlow, macdSignal,
|
|
ichiTenkan, ichiKijun, ichiSenkou));
|
|
FileClose(h);
|
|
PrintFormat("TunedPeriods: install persisted to %s - classic votes and the signal-DB key adopt "
|
|
"these on the next attach (restart-grained by design).", name);
|
|
}
|
|
#endif // WARRIOR_TUNED_PERIODS_MQH
|