kronos-mql5/Experts/Kronos/KronosForecast.mq5
ayantrader 974781180e Add Part 3 evaluation programs and fix tester file access
Adds the two Expert Advisors from Part 3, which were not previously in
the repository:

  Experts/Kronos/KronosForecast.mq5  visualize-only EA, draws the
      forecast candles to the right of live price on each new bar
  Experts/Kronos/KronosSignalEA.mq5  Strategy Tester EA, trades a
      deliberately trivial threshold rule over a precomputed dump

Switches the forecast dump to the shared Common\Files folder. A
Strategy Tester agent is sandboxed to its own private MQL5\Files
directory and cannot read what the script wrote to the terminal's
Files folder, so KronosForecastDump, KronosEvalQuality and
KronosSignalEA now all open the CSV with FILE_COMMON.

Other changes:

  - KronosEvalQuality: drop the unused rw_hit accumulator and correct
    the comment that described it as reported separately
  - KronosForecastDump: shorten two over-long block comments
  - Engine headers and KronosVerifyEncoder: align //--- block comments
    flush-left and tidy trailing comment columns, no logic changes
  - README: document the evaluation workflow, the Common\Files
    requirement, and the measured result
2026-08-21 00:56:06 +05:00

211 lines
8.3 KiB
MQL5

//+------------------------------------------------------------------+
//| KronosForecast.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
#include <Kronos\KronosInference.mqh>
//--- 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
#define KR_OBJ_PREFIX "KronosFC_"
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 (no volume proxy)
};
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
input int InpTopK = 0; // top-k (0 = off)
input double InpTopP = 0.9; // nucleus top-p
input int InpSampleCount = 5; // averaged sample paths
input bool InpGreedy = false; // greedy (deterministic) vs sampling
input color InpUpColor = clrDodgerBlue; // forecast up candle
input color InpDownColor = clrTomato; // forecast down candle
CKronosModel g_model;
bool g_ready = false;
datetime g_last_bar = 0;
//+------------------------------------------------------------------+
//| Expert initialization: load the model and forecast once. |
//+------------------------------------------------------------------+
int OnInit()
{
if(InpLookback < 1 || InpLookback > 512)
{ Print("Lookback must be in 1..512"); return INIT_PARAMETERS_INCORRECT; }
if(InpPredLen < 1)
{ Print("PredLen must be >= 1"); return INIT_PARAMETERS_INCORRECT; }
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 INIT_FAILED;
}
g_ready = true;
Print("Kronos model loaded. Forecasting once per new bar.");
//--- run once immediately on attach
RunForecast();
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization: remove the forecast objects. |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ObjectsDeleteAll(0, KR_OBJ_PREFIX);
ChartRedraw();
}
//+------------------------------------------------------------------+
//| Tick handler: re-forecast only when a new bar has completed. |
//+------------------------------------------------------------------+
void OnTick()
{
if(!g_ready)
return;
datetime t = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);
if(t == g_last_bar)
return; // only on a new completed bar
g_last_bar = t;
RunForecast();
}
//+------------------------------------------------------------------+
//| Build the raw (L,6) window and stamps, predict, draw. |
//+------------------------------------------------------------------+
void RunForecast()
{
const int L = InpLookback, P = InpPredLen;
MqlRates rates[];
ArraySetAsSeries(rates, true);
//--- index 0 is the still-forming bar; use closed bars 1..L
int need = L + 1;
int got = CopyRates(_Symbol, _Period, 0, need, rates);
if(got < need)
{ PrintFormat("CopyRates: got %d of %d bars", got, need); return; }
//--- assemble raw window oldest->newest into (L,6): open,high,low,close,vol,amount
matrix raw = matrix::Zeros((ulong)L, KR_NFEAT);
datetime ctx_time[];
ArrayResize(ctx_time, L);
for(int i = 0; i < L; i++)
{
int src = L - i; // rates[L]..rates[1] -> rows 0..L-1 (oldest first)
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;
}
//--- full stamp (L+P, 5): context times + projected future bar times
int secs = PeriodSeconds(_Period);
matrix full_stamp = matrix::Zeros((ulong)(L + P), 5);
for(int i = 0; i < L; i++)
{
int st[];
KronosStamp(ctx_time[i], st); // verified: weekday remap Sun->Mon
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];
}
//--- predict (raw units out)
matrix forecast;
if(!g_model.Predict(raw, full_stamp, P, InpTemperature, InpTopK, InpTopP,
InpSampleCount, InpGreedy, forecast))
{ Print("Predict failed"); return; }
DrawForecast(forecast, last_time, secs, rates[1].close);
}
//+------------------------------------------------------------------+
//| Draw predicted candles as chart objects to the right. |
//+------------------------------------------------------------------+
void DrawForecast(const matrix &fc, datetime last_time, int secs, double last_close)
{
ObjectsDeleteAll(0, KR_OBJ_PREFIX);
int P = (int)fc.Rows();
double prev_close = last_close;
for(int i = 0; i < P; i++)
{
datetime t = last_time + (datetime)((i + 1) * secs);
double o = fc[i][0], h = fc[i][1], lo = fc[i][2], c = fc[i][3];
color col = (c >= o) ? InpUpColor : InpDownColor;
//--- wick: high-low vertical line
string wick = StringFormat("%swick_%d", KR_OBJ_PREFIX, i);
ObjectCreate(0, wick, OBJ_TREND, 0, t, h, t, lo);
ObjectSetInteger(0, wick, OBJPROP_COLOR, col);
ObjectSetInteger(0, wick, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, wick, OBJPROP_RAY, false);
//--- body: thick open-close line
string body = StringFormat("%sbody_%d", KR_OBJ_PREFIX, i);
ObjectCreate(0, body, OBJ_TREND, 0, t, o, t, c);
ObjectSetInteger(0, body, OBJPROP_COLOR, col);
ObjectSetInteger(0, body, OBJPROP_WIDTH, 4);
ObjectSetInteger(0, body, OBJPROP_RAY, false);
//--- connector close[i-1] -> close[i] (dotted path)
string seg = StringFormat("%sseg_%d", KR_OBJ_PREFIX, i);
datetime t0 = last_time + (datetime)(i * secs);
ObjectCreate(0, seg, OBJ_TREND, 0, t0, prev_close, t, c);
ObjectSetInteger(0, seg, OBJPROP_COLOR, clrSilver);
ObjectSetInteger(0, seg, OBJPROP_STYLE, STYLE_DOT);
ObjectSetInteger(0, seg, OBJPROP_RAY, false);
prev_close = c;
}
string lbl = KR_OBJ_PREFIX + "label";
ObjectCreate(0, lbl, OBJ_TEXT, 0, last_time + (datetime)secs, fc[0][1]);
ObjectSetString(0, lbl, OBJPROP_TEXT, StringFormat("Kronos +%d", P));
ObjectSetInteger(0, lbl, OBJPROP_COLOR, clrWhite);
ChartRedraw();
PrintFormat("Forecast drawn: %d bars ahead, first close=%.5f last close=%.5f",
P, fc[0][3], fc[P - 1][3]);
}
//+------------------------------------------------------------------+