//+------------------------------------------------------------------+ //| 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