kronos-mql5/Experts/Kronos/KronosSignalEA.mq5

190 lines
7.3 KiB
MQL5
Raw Permalink Normal View History

//+------------------------------------------------------------------+
//| KronosSignalEA.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 <Trade\Trade.mqh>
input string InpInFile = "kronos_eval\\eurusd_h1_forecasts.csv"; // dump file (Common\Files)
input int InpPredLen = 16; // horizon count P stored in the dump
input int InpHorizon = 16; // which horizon's forecast to trade (1..P)
input double InpThreshold = 0.0010; // min predicted move (fraction, e.g. 0.0010 = 0.10%)
input double InpLots = 0.10; // fixed position size
input ulong InpMagic = 920607; // EA magic number
CTrade g_trade;
//--- dump held as parallel arrays, ascending by time
datetime g_time[];
double g_last_close[];
double g_fc[]; // flattened [row*P + (h-1)] forecast close
int g_rows = 0;
int g_hold = 0; // bars remaining to hold the open position
//+------------------------------------------------------------------+
//| Load the dump CSV into parallel arrays. |
//+------------------------------------------------------------------+
bool LoadDump()
{
const int P = InpPredLen;
//--- FILE_COMMON is essential here: a tester agent sees only its own private
//--- Files folder, so the shared Common\Files is the only readable location.
int fh = FileOpen(InpInFile, FILE_READ | FILE_CSV | FILE_ANSI | FILE_COMMON, ',');
if(fh == INVALID_HANDLE)
{ PrintFormat("Cannot open %s (err %d). Run KronosForecastDump first.", InpInFile, GetLastError()); return false; }
int expected = 2 + 2 * P; // time,last_close, fc_c1..cP, ac_c1..cP
ArrayResize(g_time, 0);
ArrayResize(g_last_close, 0);
ArrayResize(g_fc, 0);
g_rows = 0;
while(!FileIsEnding(fh))
{
string time_s = FileReadString(fh);
if(time_s == "")
break;
if(time_s == "time") // header
{ for(int k = 1; k < expected && !FileIsLineEnding(fh); k++) FileReadString(fh); continue; }
double last_close = (double)FileReadString(fh);
double fc[]; ArrayResize(fc, P);
for(int h = 0; h < P; h++) fc[h] = (double)FileReadString(fh);
for(int h = 0; h < P; h++) FileReadString(fh); // skip realized actuals (scoring only)
int idx = g_rows;
ArrayResize(g_time, idx + 1);
ArrayResize(g_last_close, idx + 1);
ArrayResize(g_fc, (idx + 1) * P);
g_time[idx] = StringToTime(time_s);
g_last_close[idx] = last_close;
for(int h = 0; h < P; h++) g_fc[idx * P + h] = fc[h];
g_rows++;
}
FileClose(fh);
PrintFormat("Loaded %d forecast rows from %s", g_rows, InpInFile);
return g_rows > 0;
}
//+------------------------------------------------------------------+
//| Find the dump row whose time == bar_time (exact match, the dump |
//| is keyed on the eval-bar time). Linear from a remembered cursor |
//| since the tester advances forward in time. |
//+------------------------------------------------------------------+
int g_cursor = 0;
int FindRow(datetime bar_time)
{
while(g_cursor < g_rows && g_time[g_cursor] < bar_time) g_cursor++;
if(g_cursor < g_rows && g_time[g_cursor] == bar_time)
return g_cursor;
return -1;
}
//+------------------------------------------------------------------+
int OnInit()
{
const int P = InpPredLen;
if(InpHorizon < 1 || InpHorizon > P)
{ Print("Horizon must be in 1..PredLen"); return INIT_PARAMETERS_INCORRECT; }
g_trade.SetExpertMagicNumber(InpMagic);
if(!LoadDump())
return INIT_FAILED;
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| New-bar detection: act once per completed bar, at its open. |
//+------------------------------------------------------------------+
datetime g_last_bar = 0;
void OnTick()
{
datetime t = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);
if(t == g_last_bar)
return;
g_last_bar = t;
OnBar();
}
//+------------------------------------------------------------------+
//| Current position direction held by THIS EA on this symbol: +1 |
//| long, -1 short, 0 flat. Reading the terminal each bar instead |
//| of a shadow flag keeps the EA in step with the real position |
//| and prevents duplicate same-bar orders. |
//+------------------------------------------------------------------+
int CurrentDir()
{
if(!PositionSelect(_Symbol))
return 0;
if(PositionGetInteger(POSITION_MAGIC) != (long)InpMagic)
return 0;
long ptype = PositionGetInteger(POSITION_TYPE);
return (ptype == POSITION_TYPE_BUY) ? +1 : -1;
}
//+------------------------------------------------------------------+
//| Bar logic. The forecast keyed at the PREVIOUS bar (the just- |
//| closed bar) is the one we may act on now, at this bar's open, |
//| so the decision never uses information from the bar it trades on.|
//+------------------------------------------------------------------+
void OnBar()
{
//--- the just-closed bar is index 1; its time keys the forecast we trade now
datetime closed_time = (datetime)iTime(_Symbol, _Period, 1);
int r = FindRow(closed_time);
int dir = CurrentDir(); // terminal truth, not a shadow flag
//--- time-based exit: decrement hold, close when it runs out. If we hold
//--- nothing (a prior close already settled), keep the counter at zero.
if(dir != 0)
{
g_hold--;
if(g_hold <= 0)
{
CloseAll();
dir = 0; // position is now flat for the rest of this bar
}
}
else
g_hold = 0;
if(r < 0)
return; // no forecast for this bar
double last_close = g_last_close[r];
double fc_h = g_fc[r * InpPredLen + (InpHorizon - 1)];
if(last_close <= 0.0)
return;
double pred_ret = (fc_h - last_close) / last_close;
int want = 0;
if(pred_ret > InpThreshold) want = +1;
else if(pred_ret < -InpThreshold) want = -1;
if(want == 0 || want == dir)
return; // no signal, or already in the wanted direction (no dup order)
//--- flip or fresh entry: close any opposite position first, then open once
if(dir != 0)
CloseAll();
if(want > 0) g_trade.Buy(InpLots);
else g_trade.Sell(InpLots);
g_hold = InpHorizon; // reset hold for the new position
}
//+------------------------------------------------------------------+
//| Close any position this EA owns on the current symbol. |
//+------------------------------------------------------------------+
void CloseAll()
{
if(PositionSelect(_Symbol))
{
if(PositionGetInteger(POSITION_MAGIC) == (long)InpMagic)
g_trade.PositionClose(_Symbol);
}
}
//+------------------------------------------------------------------+