mirror of
https://github.com/Valenteeno/RangeBars.git
synced 2026-08-26 02:28:05 +00:00
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:
co-authored by
Claude Opus 4.8
commit
c8e3e30849
@@ -0,0 +1,6 @@
|
|||||||
|
# Compiled binaries / logs / terminal artifacts
|
||||||
|
*.ex4
|
||||||
|
*.ex5
|
||||||
|
*.log
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
Copyright (c) 2023-2026 Teenodi Ltd. All rights reserved.
|
||||||
|
|
||||||
|
This software and its source code are the proprietary and confidential
|
||||||
|
property of Teenodi Ltd. Unauthorized copying, distribution, modification,
|
||||||
|
public display, or use of this software, in whole or in part, via any medium,
|
||||||
|
is strictly prohibited without the express prior written permission of
|
||||||
|
Teenodi Ltd.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. Trading
|
||||||
|
financial instruments carries risk; use at your own risk.
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# RangeBars
|
||||||
|
|
||||||
|
Constant-range (range-bar) charting indicator, with the development iterations that led to it archived.
|
||||||
|
|
||||||
|
> **Proprietary software** — © Teenodi Ltd. All rights reserved. Private repository; not for distribution. See [LICENSE](LICENSE).
|
||||||
|
|
||||||
|
## Range_Bars.mq5
|
||||||
|
|
||||||
|
## Archive
|
||||||
|
|
||||||
|
Earlier versions / forks are preserved under [`archive/`](archive/) for reference.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
1. Copy the source file(s) into your MetaTrader 5 `MQL5/` tree (`Experts/`, `Indicators/`, or `Scripts/` as appropriate).
|
||||||
|
2. In MetaEditor, open the file and compile (**F7**).
|
||||||
|
3. Attach the compiled program to a chart from the terminal Navigator.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Proprietary and confidential. © Teenodi Ltd. All rights reserved. See [LICENSE](LICENSE).
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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]
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -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]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user