152 lines
5.9 KiB
MQL5
152 lines
5.9 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| BarSeriesBuffer.Indicator.Test |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#property indicator_chart_window
|
||
|
|
#property indicator_plots 0
|
||
|
|
|
||
|
|
#include "..\\Storage\\BarSeriesBuffer.mqh"
|
||
|
|
|
||
|
|
// Instantiate the buffer globally for persistence across ticks
|
||
|
|
CBarSeriesBuffer g_buffer;
|
||
|
|
const int TEST_CAPACITY = 20;
|
||
|
|
bool g_resetTriggered = false;
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Custom indicator initialization function |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
int OnInit()
|
||
|
|
{
|
||
|
|
if(!g_buffer.Init(TEST_CAPACITY))
|
||
|
|
{
|
||
|
|
Print("FAIL: Buffer initialization failed.");
|
||
|
|
return(INIT_FAILED);
|
||
|
|
}
|
||
|
|
|
||
|
|
PrintFormat("PASS: Buffer initialized with capacity: %d", TEST_CAPACITY);
|
||
|
|
g_resetTriggered = false;
|
||
|
|
return(INIT_SUCCEEDED);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Custom indicator iteration function |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
int OnCalculate(const int rates_total,
|
||
|
|
const int prev_calculated,
|
||
|
|
const datetime &time[],
|
||
|
|
const double &open[],
|
||
|
|
const double &high[],
|
||
|
|
const double &low[],
|
||
|
|
const double &close[],
|
||
|
|
const long &tick_volume[],
|
||
|
|
const long &volume[],
|
||
|
|
const int &spread[])
|
||
|
|
{
|
||
|
|
// Check if there are enough bars to perform the historical comparison safely
|
||
|
|
if(rates_total < TEST_CAPACITY + 5)
|
||
|
|
{
|
||
|
|
PrintFormat("INFO: Insufficient data. Rates total: %d, Required: %d", rates_total, TEST_CAPACITY + 5);
|
||
|
|
return(0);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Main historical loop running from limit up to rates_total - 2
|
||
|
|
const int limit = prev_calculated > 0 ? prev_calculated - 1 : 0;
|
||
|
|
|
||
|
|
for(int i = limit; i < rates_total - 1 && !_StopFlag; i++)
|
||
|
|
{
|
||
|
|
ESeriesEvent res = g_buffer.Update(time[i], close[i]);
|
||
|
|
|
||
|
|
// Test 1: First sample validation
|
||
|
|
if(i == 0 && prev_calculated == 0)
|
||
|
|
{
|
||
|
|
if(res != SERIES_FIRST_SAMPLE)
|
||
|
|
PrintFormat("FAIL: Expected SERIES_FIRST_SAMPLE at index 0, got %d", res);
|
||
|
|
else
|
||
|
|
PrintFormat("PASS: SERIES_FIRST_SAMPLE verified at index 0. Time: %s, Price: %f", TimeToString(time[i]), close[i]);
|
||
|
|
}
|
||
|
|
// Test 2: Standard bar appends (Log selectively on the first few elements to prevent terminal spam)
|
||
|
|
else if(i > 0 && !g_resetTriggered)
|
||
|
|
{
|
||
|
|
if(res != SERIES_APPEND)
|
||
|
|
PrintFormat("FAIL: Expected SERIES_APPEND at index %d, got %d", i, res);
|
||
|
|
else if(i <= 3 && prev_calculated == 0)
|
||
|
|
PrintFormat("PASS: SERIES_APPEND verified at historical index %d. Time: %s", i, TimeToString(time[i]));
|
||
|
|
}
|
||
|
|
|
||
|
|
// Test 3: Synthetic History Reset Simulation
|
||
|
|
// Inject a time rewind exactly at index 5 when recalculating from scratch
|
||
|
|
if(i == 5 && prev_calculated == 0 && !g_resetTriggered)
|
||
|
|
{
|
||
|
|
Print("--- Running Synthetic History Reset Verification ---");
|
||
|
|
|
||
|
|
// Feed an older timestamp (1 hour behind current processing index)
|
||
|
|
datetime syntheticPastTime = time[i] - 3600;
|
||
|
|
ESeriesEvent resetRes = g_buffer.Update(syntheticPastTime, close[i]);
|
||
|
|
|
||
|
|
if(resetRes != SERIES_RESET)
|
||
|
|
PrintFormat("FAIL: Expected SERIES_RESET on time rewind, got %d", resetRes);
|
||
|
|
else
|
||
|
|
PrintFormat("PASS: SERIES_RESET verified successfully. Sent old time: %s, Buffer count reset to: %d", TimeToString(syntheticPastTime), g_buffer.Count());
|
||
|
|
|
||
|
|
g_resetTriggered = true;
|
||
|
|
|
||
|
|
// Repopulate the current bar index to resume clean state flow
|
||
|
|
g_buffer.Update(time[i], close[i]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Test 4: Live Market Bar Simulation (i == rates_total - 1)
|
||
|
|
if(!_StopFlag && prev_calculated <= 0)
|
||
|
|
{
|
||
|
|
int liveIdx = rates_total - 1;
|
||
|
|
|
||
|
|
// Simulate Tick 1 on Live Bar (Creates a new bar entry or updates depending on tick history)
|
||
|
|
ESeriesEvent liveRes1 = g_buffer.Update(time[liveIdx], close[liveIdx]);
|
||
|
|
|
||
|
|
// Simulate Tick 2 on the SAME Live Bar with a synthetic fluctuating price
|
||
|
|
double temporaryPrice = close[liveIdx] + 0.0005;
|
||
|
|
ESeriesEvent liveRes2 = g_buffer.Update(time[liveIdx], temporaryPrice);
|
||
|
|
|
||
|
|
if(liveRes2 != SERIES_REPLACE_LAST)
|
||
|
|
PrintFormat("FAIL: Expected SERIES_REPLACE_LAST on live tick update, got %d", liveRes2);
|
||
|
|
else
|
||
|
|
PrintFormat("PASS: SERIES_REPLACE_LAST verified on live bar tick update. Temporary Price: %f", temporaryPrice);
|
||
|
|
|
||
|
|
// Restore the actual closing price to match standard data arrays for index comparison
|
||
|
|
g_buffer.Update(time[liveIdx], close[liveIdx]);
|
||
|
|
|
||
|
|
// Test 5: Inverted Index Comparison Matrix (Buffer 0 vs MQL5 rates_total - 1)
|
||
|
|
bool matchPassed = true;
|
||
|
|
int compareBars = MathMin(g_buffer.Count(), TEST_CAPACITY);
|
||
|
|
|
||
|
|
for(int offset = 0; offset < compareBars; offset++)
|
||
|
|
{
|
||
|
|
double bufferVal = 0;
|
||
|
|
if(!g_buffer.At(offset, bufferVal))
|
||
|
|
{
|
||
|
|
PrintFormat("FAIL: Could not retrieve buffer value at offset %d", offset);
|
||
|
|
matchPassed = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Invert rates total mapping index
|
||
|
|
int arrayIdx = liveIdx - offset;
|
||
|
|
|
||
|
|
if(NormalizeDouble(bufferVal - close[arrayIdx], _Digits) != 0)
|
||
|
|
{
|
||
|
|
PrintFormat("FAIL: Mismatch at offset %d! Buffer: %f, Close Array: %f",
|
||
|
|
offset, bufferVal, close[arrayIdx]);
|
||
|
|
matchPassed = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if(matchPassed)
|
||
|
|
{
|
||
|
|
PrintFormat("PASS: Inverted indexing comparison verified! Matched all past %d bars.", compareBars);
|
||
|
|
PrintFormat("SUCCESS: All 4 state transitions and buffer indices matched perfectly with chart arrays.");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return(rates_total);
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|