forked from mnbvc188199/Warrior_EA
- Replaced standard library signal modules with custom implementations to allow for named patterns and improved voting. - Added new input parameters for module weights, allowing for optimization of individual signal contributions. - Enhanced the management of trades with new options for breakeven and management cut. - Introduced a mechanism for dynamic ranking of signal weights based on historical performance. - Improved initialization logic to ensure proper registration of filters and handling of trading conditions. - Added detailed logging for trading permissions and account status during initialization.
193 lines
7.9 KiB
MQL5
193 lines
7.9 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| AltDataFeed.mqh |
| |
//| AnimateDread |
| |
//| |
| |
//| THE DAILY MACRO / POSITIONING BLOCK, read once at init. |
| |
//| |
| |
//| Common\Files\ADAltData\<symbol>_D1.csv - 6,096 rows, 2010-01-01 |
| |
//| onward, semicolon separated. Sixteen usable columns: VIX and its |
| |
//| 5-day change, the dollar's 5-day change, COT speculative net plus |
| |
//| its 1y/3y percentiles and 4-week change, three EIA inventory |
| |
//| series, and six macro series (10y, curve, breakeven, output gap, |
| |
//| CPI, unemployment). |
| |
//| |
| |
//| ⚠ ivol AND ivol_chg5 ARE EMPTY IN EVERY ROW and are not loaded. |
| |
//| Measured 2026-09-11: 0.0% filled on EURUSD, against ~100% for the |
| |
//| other sixteen. Loading them would hand the network two constant |
| |
//| zero columns, which is not neutral - it spends input width and |
| |
//| lets the optimiser fit noise through the bias attached to them. |
| |
//| |
| |
//| WHY IT CANNOT LOOK AHEAD. A row dated D carries values known at |
| |
//| D's close. Features are built from shift 1 - the last CLOSED bar |
| |
//| - so the row matching that bar's date was fully observable before |
| |
//| the bar being decided even opened. `Lookup` therefore takes the |
| |
//| newest row whose date is <= the closed bar's date, never the row |
| |
//| after it, and a missing date resolves BACKWARD (a holiday reads |
| |
//| Friday's macro state, which is what a trader would have had). |
| |
//| |
| |
//| AND WHY IT IS NOT ZERO-FILLED. The series begin in 2010. A run |
| |
//| starting earlier must SKIP those bars, not pad them: a column of |
| |
//| zeros for 2005-2009 teaches the network the era rather than the |
| |
//| market, and it would learn "alt data absent" as a tradeable |
| |
//| state. Lookup() returns false and the caller drops the row. |
| |
//+------------------------------------------------------------------+
| |
#ifndef WARRIOR_ALTDATA_FEED_MQH
| |
#define WARRIOR_ALTDATA_FEED_MQH
| |
| |
#define ALT_COLUMNS 16
| |
| |
class CAltDataFeed
| |
{
| |
private:
| |
datetime m_date[]; // ascending, one per row
| |
double m_val[]; // flat, ALT_COLUMNS per row
| |
int m_rows;
| |
string m_why;
| |
string m_loadedFor;
| |
| |
//--- The CSV's own column order, minus the two empty ones. Index into the split line.
| |
//--- 0=date 1=vix 2=vix_chg5 3=usd_chg5 [4=ivol 5=ivol_chg5 SKIPPED] 6=cot_spec_net
| |
//--- 7=cot_idx_1y 8=cot_idx_3y 9=cot_chg_4w 10..12=eia 13..18=mac
| |
static void SourceColumns(int &c[])
| |
{
| |
ArrayResize(c, ALT_COLUMNS);
| |
int k = 0;
| |
c[k++] = 1; c[k++] = 2; c[k++] = 3;
| |
c[k++] = 6; c[k++] = 7; c[k++] = 8; c[k++] = 9;
| |
c[k++] = 10; c[k++] = 11; c[k++] = 12;
| |
c[k++] = 13; c[k++] = 14; c[k++] = 15; c[k++] = 16; c[k++] = 17; c[k++] = 18;
| |
}
| |
| |
public:
| |
CAltDataFeed(void) : m_rows(0), m_why("not loaded"), m_loadedFor("") {}
| |
~CAltDataFeed(void) {}
| |
| |
bool Ok(void) const { return m_rows > 0; }
| |
int Rows(void) const { return m_rows; }
| |
string Why(void) const { return m_why; }
| |
static string ColumnName(const int i);
| |
| |
bool Load(const string symbol);
| |
//--- Values for the bar CLOSING at `when`. False when the date predates the series.
| |
bool Lookup(const datetime when, double &out[]) const;
| |
};
| |
//+------------------------------------------------------------------+
| |
string CAltDataFeed::ColumnName(const int i)
| |
{
| |
switch(i)
| |
{
| |
case 0: return "vix";
| |
case 1: return "vix_chg5";
| |
case 2: return "usd_chg5";
| |
case 3: return "cot_spec_net";
| |
case 4: return "cot_idx_1y";
| |
case 5: return "cot_idx_3y";
| |
case 6: return "cot_chg_4w";
| |
case 7: return "eia_stk_idx1y";
| |
case 8: return "eia_stk_chg4";
| |
case 9: return "eia_util";
| |
case 10: return "mac_y10";
| |
case 11: return "mac_curve";
| |
case 12: return "mac_bei";
| |
case 13: return "mac_gap";
| |
case 14: return "mac_cpi";
| |
case 15: return "mac_unemp";
| |
}
| |
return "?";
| |
}
| |
//+------------------------------------------------------------------+
| |
bool CAltDataFeed::Load(const string symbol)
| |
{
| |
if(m_loadedFor == symbol && m_rows > 0)
| |
return true;
| |
m_rows = 0;
| |
ArrayResize(m_date, 0);
| |
ArrayResize(m_val, 0);
| |
| |
const string path = "ADAltData\\" + symbol + "_D1.csv";
| |
//--- FILE_COMMON: the corpus is shared by the tester and the live terminal, exactly like the
| |
//--- journal database, so one file serves every agent rather than one copy per sandbox.
| |
const int h = FileOpen(path, FILE_READ | FILE_TXT | FILE_ANSI | FILE_COMMON);
| |
if(h == INVALID_HANDLE)
| |
{
| |
m_why = StringFormat("cannot open Common\\Files\\%s (%d)", path, GetLastError());
| |
return false;
| |
}
| |
int src[];
| |
SourceColumns(src);
| |
string parts[];
| |
bool header = true;
| |
while(!FileIsEnding(h))
| |
{
| |
const string line = FileReadString(h);
| |
if(header) // the column names, not data
| |
{ header = false; continue; }
| |
if(StringLen(line) < 10)
| |
continue;
| |
if(StringSplit(line, ';', parts) < 19)
| |
continue;
| |
//--- "2010-01-01" -> "2010.01.01"; StringToTime wants dots.
| |
string d = parts[0];
| |
StringReplace(d, "-", ".");
| |
const datetime when = StringToTime(d);
| |
if(when == 0)
| |
continue;
| |
const int r = m_rows;
| |
ArrayResize(m_date, r + 1);
| |
ArrayResize(m_val, (r + 1) * ALT_COLUMNS);
| |
m_date[r] = when;
| |
for(int c = 0; c < ALT_COLUMNS; c++)
| |
{
| |
const string cell = parts[src[c]];
| |
//--- An empty cell carries the previous row's value forward - a macro series that has not
| |
//--- printed yet has not CHANGED either, and the last print is what a trader would be
| |
//--- acting on. Only the very first rows can have nothing to carry, and they read 0.
| |
double v = 0.0;
| |
if(StringLen(cell) > 0)
| |
v = StringToDouble(cell);
| |
else
| |
if(r > 0)
| |
v = m_val[(r - 1) * ALT_COLUMNS + c];
| |
m_val[r * ALT_COLUMNS + c] = v;
| |
}
| |
m_rows++;
| |
}
| |
FileClose(h);
| |
if(m_rows <= 0)
| |
{
| |
m_why = "file opened but no usable rows";
| |
return false;
| |
}
| |
m_loadedFor = symbol;
| |
m_why = "";
| |
return true;
| |
}
| |
//+------------------------------------------------------------------+
| |
bool CAltDataFeed::Lookup(const datetime when, double &out[]) const
| |
{
| |
ArrayResize(out, ALT_COLUMNS);
| |
ArrayInitialize(out, 0.0);
| |
if(m_rows <= 0 || when < m_date[0])
| |
return false; // predates the series - the caller drops the row
| |
//--- Newest row at or before `when`. Binary search: this runs once per training row, and a
| |
//--- linear scan over 6,000 rows x 4,000 rows of training is 24M comparisons per fit.
| |
int lo = 0, hi = m_rows - 1, best = -1;
| |
while(lo <= hi)
| |
{
| |
const int mid = (lo + hi) / 2;
| |
if(m_date[mid] <= when)
| |
{ best = mid; lo = mid + 1; }
| |
else
| |
hi = mid - 1;
| |
}
| |
if(best < 0)
| |
return false;
| |
for(int c = 0; c < ALT_COLUMNS; c++)
| |
out[c] = m_val[best * ALT_COLUMNS + c];
| |
return true;
| |
}
| |
//+------------------------------------------------------------------+
| |
//| ONE FEED PER CHART, like the Wyckoff one and for the same reason. |
| |
//+------------------------------------------------------------------+
| |
CAltDataFeed g_altData;
| |
#endif // WARRIOR_ALTDATA_FEED_MQH
|