//+------------------------------------------------------------------+ //| KronosForecastDump.mq5| //| MMQ — Muhammad Minhas Qamar | //| www.mql5.com | //+------------------------------------------------------------------+ #property copyright "MMQ — Muhammad Minhas Qamar" #property link "https://www.mql5.com" #property version "1.00" #property strict #property script_show_inputs #include //--- tokenizer config (config_tokenizer.json) #define KR_TOK_DIR "kronos_weights\\tokenizer\\" #define KR_TOK_DM 256 #define KR_TOK_HEADS 4 #define KR_TOK_ENC 4 #define KR_TOK_DEC 4 #define KR_TOK_FF 512 //--- predictor config (config_predictor.json) #define KR_PRED_DIR "kronos_weights\\predictor\\" #define KR_PRED_DM 512 #define KR_PRED_HEADS 8 #define KR_PRED_LAYERS 8 #define KR_PRED_FF 1024 enum ENUM_VOL_MODE { VOL_TICK_DERIVED = 0, // tick_volume + derived amount (typical_price * tick_volume) VOL_ZERO_FILL = 1 // zero-fill volume and amount }; input datetime InpStartDate = D'2025.04.01 00:00'; // eval window start (first eval bar) input datetime InpEndDate = D'2025.06.01 00:00'; // eval window end (last eval bar) input int InpStep = 1; // dump density (evaluate every Nth bar) input int InpLookback = 256; // context bars (<=512) input int InpPredLen = 16; // forecast horizon (bars) input ENUM_VOL_MODE InpVolMode = VOL_TICK_DERIVED; // volume/amount handling input double InpTemperature = 1.0; // sampling temperature (greedy ignores) input int InpTopK = 0; // top-k (greedy ignores) input double InpTopP = 0.9; // nucleus top-p (greedy ignores) input int InpSampleCount = 1; // averaged sample paths (1 for greedy) input bool InpGreedy = true; // greedy => deterministic / reproducible input string InpOutFile = "kronos_eval\\eurusd_h1_forecasts.csv"; // output (MQL5/Files) CKronosModel g_model; //+------------------------------------------------------------------+ //| Assemble the raw (L,6) window for an eval bar whose CLOSED bars | //| occupy rates[] indices [base .. base+L-1] (oldest first). | //| Mirrors the window-build in KronosForecast.mq5 exactly. | //+------------------------------------------------------------------+ void BuildWindow(const MqlRates &rates[], int base, int L, matrix &raw, datetime &ctx_time[]) { raw = matrix::Zeros((ulong)L, KR_NFEAT); ArrayResize(ctx_time, L); for(int i = 0; i < L; i++) { int src = base + i; double o = rates[src].open, h = rates[src].high, lo = rates[src].low, c = rates[src].close; double vol = 0.0, amt = 0.0; if(InpVolMode == VOL_TICK_DERIVED) { vol = (double)rates[src].tick_volume; double typical = (o + h + lo + c) / 4.0; amt = vol * typical; } raw[i][0] = o; raw[i][1] = h; raw[i][2] = lo; raw[i][3] = c; raw[i][4] = vol; raw[i][5] = amt; ctx_time[i] = rates[src].time; } } //+------------------------------------------------------------------+ //| Build the full (L+P,5) stamp matrix: context stamps + projected | //| future bar stamps. Mirrors KronosForecast.mq5. | //+------------------------------------------------------------------+ void BuildStamps(const datetime &ctx_time[], int L, int P, int secs, matrix &full_stamp) { full_stamp = matrix::Zeros((ulong)(L + P), 5); for(int i = 0; i < L; i++) { int st[]; KronosStamp(ctx_time[i], st); for(int j = 0; j < 5; j++) full_stamp[i][j] = st[j]; } datetime last_time = ctx_time[L - 1]; for(int i = 0; i < P; i++) { datetime ft = last_time + (datetime)((i + 1) * secs); int st[]; KronosStamp(ft, st); for(int j = 0; j < 5; j++) full_stamp[L + i][j] = st[j]; } } //+------------------------------------------------------------------+ //| Script entry: load model, walk history, dump forecasts. | //+------------------------------------------------------------------+ void OnStart() { const int L = InpLookback, P = InpPredLen; if(L < 1 || L > 512) { Print("Lookback must be in 1..512"); return; } if(P < 1) { Print("PredLen must be >= 1"); return; } if(InpStep < 1) { Print("Step must be >= 1"); return; } if(InpStartDate >= InpEndDate) { Print("StartDate must be before EndDate"); return; } if(!g_model.Init(KR_TOK_DIR, KR_TOK_ENC, KR_TOK_DEC, KR_TOK_DM, KR_TOK_HEADS, KR_TOK_FF, KR_PRED_DIR, KR_PRED_LAYERS, KR_PRED_DM, KR_PRED_HEADS, KR_PRED_FF, 512)) { Print("Kronos model failed to load. Check kronos_weights/ under MQL5/Files/."); return; } Print("Kronos model loaded. Building forecast dump..."); int secs = PeriodSeconds(_Period); //--- Pull enough history: from L bars before the start, through P bars after //--- the end (so every eval bar in [start,end] has both a full context AND P //--- realized future bars to score against). Oldest-first (no series flag). datetime from = InpStartDate - (datetime)((L + 2) * secs); datetime to = InpEndDate + (datetime)((P + 2) * secs); MqlRates rates[]; int got = CopyRates(_Symbol, _Period, from, to, rates); if(got <= 0) { PrintFormat("CopyRates failed (err %d). Load H1 history for %s.", GetLastError(), _Symbol); return; } PrintFormat("History: %d bars from %s to %s", got, TimeToString(rates[0].time), TimeToString(rates[got - 1].time)); //--- open output (truncate + header) int fh = FileOpen(InpOutFile, FILE_WRITE | FILE_CSV | FILE_ANSI, ','); if(fh == INVALID_HANDLE) { PrintFormat("Cannot open %s for write (err %d)", InpOutFile, GetLastError()); return; } WriteHeader(fh, P); //--- Identify eval bars: a bar index t is an eval bar if //--- rates[t].time in [start,end], it has L closed bars before it //--- (indices t-L..t-1) and P realized bars after it (t+1..t+P). //--- The context window is rates[t-L .. t-1]; the "current" bar is t (its close //--- is last_close); forecasts cover t+1..t+P. Using bars strictly before t as //--- context and t's close as the anchor avoids any look-ahead. int rows_written = 0; ulong t0 = GetTickCount64(); for(int t = L; t + P < got; t++) { if(rates[t].time < InpStartDate || rates[t].time > InpEndDate) continue; if(((t - L) % InpStep) != 0) continue; matrix raw; datetime ctx_time[]; BuildWindow(rates, t - L, L, raw, ctx_time); // closed bars t-L..t-1 matrix full_stamp; BuildStamps(ctx_time, L, P, secs, full_stamp); matrix forecast; // (P,6) raw units if(!g_model.Predict(raw, full_stamp, P, InpTemperature, InpTopK, InpTopP, InpSampleCount, InpGreedy, forecast)) { PrintFormat("Predict failed at %s", TimeToString(rates[t].time)); continue; } WriteRow(fh, rates, t, P, forecast); rows_written++; if((rows_written % 25) == 0) { double secs_elapsed = (double)(GetTickCount64() - t0) / 1000.0; PrintFormat(" %d rows | %s | %.1fs elapsed (%.2fs/forecast)", rows_written, TimeToString(rates[t].time), secs_elapsed, secs_elapsed / rows_written); } } FileClose(fh); double total = (double)(GetTickCount64() - t0) / 1000.0; PrintFormat("Done. %d forecasts written to MQL5/Files/%s in %.1fs.", rows_written, InpOutFile, total); } //+------------------------------------------------------------------+ //| CSV header: time, last_close, fc_c1..cP, ac_c1..cP. | //+------------------------------------------------------------------+ void WriteHeader(int fh, int P) { string cols = "time,last_close"; for(int h = 1; h <= P; h++) cols += StringFormat(",fc_c%d", h); for(int h = 1; h <= P; h++) cols += StringFormat(",ac_c%d", h); FileWrite(fh, cols); } //+------------------------------------------------------------------+ //| One row: eval-bar time, its close, forecast closes 1..P, then the| //| REALIZED future closes 1..P (rates[t+1..t+P]). Stored for scoring| //| only — never an input to any forecast/trade decision. | //+------------------------------------------------------------------+ void WriteRow(int fh, const MqlRates &rates[], int t, int P, const matrix &forecast) { string row = TimeToString(rates[t].time, TIME_DATE | TIME_MINUTES); row += StringFormat(",%.6f", rates[t].close); for(int h = 0; h < P; h++) row += StringFormat(",%.6f", forecast[h][3]); // col 3 = close for(int h = 1; h <= P; h++) row += StringFormat(",%.6f", rates[t + h].close); FileWrite(fh, row); } //+------------------------------------------------------------------+