211 lines
8.3 KiB
MQL5
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]);
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|