Initial commit: RangeBars

Proprietary trading software. © Teenodi Ltd. All rights reserved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Valentine Chibuike Ozoigboanugo
2026-06-14 15:32:09 +02:00
co-authored by Claude Opus 4.8
commit c8e3e30849
13 changed files with 467 additions and 0 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
+193
View File
@@ -0,0 +1,193 @@
// [Previous property declarations and input parameters remain unchanged]
// Global variables for tick synchronization
datetime g_lastSyncTime = 0;
bool g_initialSyncDone = false;
int g_retryCount = 0;
const int MAX_RETRIES = 3;
ulong g_currentChunkStart = 0;
const ulong CHUNK_SIZE = 2592000000; // 30 days in milliseconds
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// [Previous initialization code remains unchanged until before g_lastProcessedTickTime initialization]
// Initialize with dummy tick to trigger sync
MqlTick dummy;
SymbolInfoTick(_Symbol, dummy);
// Reset sync variables
g_initialSyncDone = false;
g_retryCount = 0;
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[])
{
if(rates_total <= 0)
return(0);
// Calculate historical data on first run
if(!g_initialized)
{
if(!g_initialSyncDone)
{
// Initialize chunk start time
datetime startTime = TimeCurrent() - InpBacklogMinutes * 60;
g_currentChunkStart = startTime * 1000; // Convert to milliseconds
g_initialSyncDone = true;
}
// Process historical data in chunks
bool continueProcessing = true;
MqlTick historical_ticks[];
int totalBars = 0;
while(continueProcessing)
{
ulong chunkEnd = g_currentChunkStart + CHUNK_SIZE;
if(chunkEnd > TimeCurrent() * 1000)
chunkEnd = TimeCurrent() * 1000;
int copied = CopyTicksRange(_Symbol, historical_ticks, COPY_TICKS_INFO,
g_currentChunkStart, chunkEnd);
if(copied > 0)
{
// Reset retry count on successful copy
g_retryCount = 0;
// Process ticks in this chunk
MqlRates tempBuffer[];
for(int i = 0; i < copied && totalBars < InpStartingBars; i++)
{
// Filter out irrelevant ticks
if((historical_ticks[i].flags & TICK_FLAG_BID) == 0)
continue;
if(ProcessHistoricalTick(historical_ticks[i], tempBuffer))
{
totalBars++;
if(totalBars >= InpStartingBars)
{
// Write processed bars to custom symbol
if(CustomRatesUpdate(g_customSymbol, tempBuffer))
{
g_initialized = true;
g_lastProcessedTickTime = historical_ticks[i].time_msc;
continueProcessing = false;
break;
}
}
}
}
// Move to next chunk if we need more bars
g_currentChunkStart = chunkEnd;
}
else
{
int error = GetLastError();
if(error == ERR_HISTORY_TIMEOUT)
{
if(g_retryCount < MAX_RETRIES)
{
// Implement exponential backoff
Sleep(1000 * (g_retryCount + 1));
g_retryCount++;
continue;
}
}
Print("Error copying historical ticks: ", error);
return(0);
}
// Check if we've reached current time
if(chunkEnd >= TimeCurrent() * 1000)
continueProcessing = false;
}
}
else
{
// Process current ticks using chunked approach
MqlTick current_ticks[];
ulong fromTime = (g_lastProcessedTickTime - InpTickTolerance * 1000);
ulong toTime = TimeCurrent() * 1000;
// Split current tick processing into smaller chunks if needed
while(fromTime < toTime)
{
ulong chunkEnd = fromTime + CHUNK_SIZE;
if(chunkEnd > toTime)
chunkEnd = toTime;
int copied = CopyTicksRange(_Symbol, current_ticks, COPY_TICKS_INFO,
fromTime, chunkEnd);
if(copied > 0)
{
for(int i = 0; i < copied; i++)
{
// Filter out irrelevant ticks
if((current_ticks[i].flags & TICK_FLAG_BID) == 0)
continue;
if(current_ticks[i].time_msc > g_lastProcessedTickTime)
{
ProcessTick(current_ticks[i]);
g_lastProcessedTickTime = current_ticks[i].time_msc;
}
}
fromTime = chunkEnd;
}
else
{
int error = GetLastError();
if(error != ERR_HISTORY_TIMEOUT || g_retryCount >= MAX_RETRIES)
{
Print("Error copying current ticks: ", error);
break;
}
Sleep(1000 * (g_retryCount + 1));
g_retryCount++;
}
}
}
ChartRedraw(g_customChartID);
return(rates_total);
}
// [Previous helper functions remain unchanged]
//+------------------------------------------------------------------+
//| Cleanup resources with improved synchronization handling |
//+------------------------------------------------------------------+
void CleanupResources()
{
// Reset sync variables
g_initialSyncDone = false;
g_retryCount = 0;
g_currentChunkStart = 0;
// [Previous cleanup code remains unchanged]
}
BIN
View File
Binary file not shown.
+237
View File
@@ -0,0 +1,237 @@
//+------------------------------------------------------------------+
//| try7.mq5 |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#property indicator_separate_window
#property indicator_minimum 1
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const int begin,
const double &price[])
{
//---
//--- return value of prev_calculated for next call
return(rates_total);
}
//+------------------------------------------------------------------+
//| ChartEvent function |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
//---
}
//+------------------------------------------------------------------+
// [Previous property declarations and input parameters remain unchanged]
// Global variables for tick synchronization
datetime g_lastSyncTime = 0;
bool g_initialSyncDone = false;
int g_retryCount = 0;
const int MAX_RETRIES = 3;
ulong g_currentChunkStart = 0;
const ulong CHUNK_SIZE = 2592000000; // 30 days in milliseconds
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// [Previous initialization code remains unchanged until before g_lastProcessedTickTime initialization]
// Initialize with dummy tick to trigger sync
MqlTick dummy;
SymbolInfoTick(_Symbol, dummy);
// Reset sync variables
g_initialSyncDone = false;
g_retryCount = 0;
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[])
{
if(rates_total <= 0)
return(0);
// Calculate historical data on first run
if(!g_initialized)
{
if(!g_initialSyncDone)
{
// Initialize chunk start time
datetime startTime = TimeCurrent() - InpBacklogMinutes * 60;
g_currentChunkStart = startTime * 1000; // Convert to milliseconds
g_initialSyncDone = true;
}
// Process historical data in chunks
bool continueProcessing = true;
MqlTick historical_ticks[];
int totalBars = 0;
while(continueProcessing)
{
ulong chunkEnd = g_currentChunkStart + CHUNK_SIZE;
if(chunkEnd > TimeCurrent() * 1000)
chunkEnd = TimeCurrent() * 1000;
int copied = CopyTicksRange(_Symbol, historical_ticks, COPY_TICKS_INFO,
g_currentChunkStart, chunkEnd);
if(copied > 0)
{
// Reset retry count on successful copy
g_retryCount = 0;
// Process ticks in this chunk
MqlRates tempBuffer[];
for(int i = 0; i < copied && totalBars < InpStartingBars; i++)
{
// Filter out irrelevant ticks
if((historical_ticks[i].flags & TICK_FLAG_BID) == 0)
continue;
if(ProcessHistoricalTick(historical_ticks[i], tempBuffer))
{
totalBars++;
if(totalBars >= InpStartingBars)
{
// Write processed bars to custom symbol
if(CustomRatesUpdate(g_customSymbol, tempBuffer))
{
g_initialized = true;
g_lastProcessedTickTime = historical_ticks[i].time_msc;
continueProcessing = false;
break;
}
}
}
}
// Move to next chunk if we need more bars
g_currentChunkStart = chunkEnd;
}
else
{
int error = GetLastError();
if(error == ERR_HISTORY_TIMEOUT)
{
if(g_retryCount < MAX_RETRIES)
{
// Implement exponential backoff
Sleep(1000 * (g_retryCount + 1));
g_retryCount++;
continue;
}
}
Print("Error copying historical ticks: ", error);
return(0);
}
// Check if we've reached current time
if(chunkEnd >= TimeCurrent() * 1000)
continueProcessing = false;
}
}
else
{
// Process current ticks using chunked approach
MqlTick current_ticks[];
ulong fromTime = (g_lastProcessedTickTime - InpTickTolerance * 1000);
ulong toTime = TimeCurrent() * 1000;
// Split current tick processing into smaller chunks if needed
while(fromTime < toTime)
{
ulong chunkEnd = fromTime + CHUNK_SIZE;
if(chunkEnd > toTime)
chunkEnd = toTime;
int copied = CopyTicksRange(_Symbol, current_ticks, COPY_TICKS_INFO,
fromTime, chunkEnd);
if(copied > 0)
{
for(int i = 0; i < copied; i++)
{
// Filter out irrelevant ticks
if((current_ticks[i].flags & TICK_FLAG_BID) == 0)
continue;
if(current_ticks[i].time_msc > g_lastProcessedTickTime)
{
ProcessTick(current_ticks[i]);
g_lastProcessedTickTime = current_ticks[i].time_msc;
}
}
fromTime = chunkEnd;
}
else
{
int error = GetLastError();
if(error != ERR_HISTORY_TIMEOUT || g_retryCount >= MAX_RETRIES)
{
Print("Error copying current ticks: ", error);
break;
}
Sleep(1000 * (g_retryCount + 1));
g_retryCount++;
}
}
}
ChartRedraw(g_customChartID);
return(rates_total);
}
// [Previous helper functions remain unchanged]
//+------------------------------------------------------------------+
//| Cleanup resources with improved synchronization handling |
//+------------------------------------------------------------------+
void CleanupResources()
{
// Reset sync variables
g_initialSyncDone = false;
g_retryCount = 0;
g_currentChunkStart = 0;
// [Previous cleanup code remains unchanged]
}