Article-22460-Custom-Tick-C.../Article-22460-Custom-Tick-Chart-MQL5.mq5

218 lines
7.5 KiB
MQL5

//+------------------------------------------------------------------+
//| Article-22460-Custom-Tick-Chart-MQL5.mq5 |
//| Abioye Israel Pelumi |
//| https://Algoyin.com |
//+------------------------------------------------------------------+
input string InpCustomSymbol = "TICK_101"; // Name of the custom symbol to create/use
input bool InpOpenChart = true; // Whether to automatically open the chart
input int InpTicksPerBar = 30; // Number of ticks that will form one candle
//+------------------------------------------------------------------+
//| Tick-based candle state |
//+------------------------------------------------------------------+
int tick_count = 0; // Counts how many ticks have formed the current bar
double open_price = 0; // Open price of current bar
double high_price = 0; // Highest price reached in current bar
double low_price = 0; // Lowest price reached in current bar
double close_price = 0; // Latest price (updates every tick)
datetime bar_time = 0; // Time assigned to the current bar
datetime last_bar_time = 0; // Time of the last completed bar (used to ensure uniqueness)
//+------------------------------------------------------------------+
//| Create or prepare custom symbol |
//+------------------------------------------------------------------+
bool CreateCustomSymbol()
{
//--- If symbol already exists, just select it and continue
if(SymbolInfoInteger(InpCustomSymbol, SYMBOL_EXIST))
{
SymbolSelect(InpCustomSymbol, true); // Make it visible in Market Watch
return true;
}
//--- Create new custom symbol using current symbol as template
if(!CustomSymbolCreate(InpCustomSymbol, "", _Symbol))
{
Print("Failed to create custom symbol: ", GetLastError());
return false;
}
//--- Copy important trading properties from the original symbol
CustomSymbolSetInteger(InpCustomSymbol, SYMBOL_DIGITS, _Digits); // Decimal precision
CustomSymbolSetDouble(InpCustomSymbol, SYMBOL_POINT, _Point); // Smallest price step
CustomSymbolSetDouble(InpCustomSymbol, SYMBOL_TRADE_TICK_SIZE,SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE));
//--- Set descriptive metadata
CustomSymbolSetString(InpCustomSymbol, SYMBOL_DESCRIPTION, "Tick Chart – " + _Symbol);
CustomSymbolSetString(InpCustomSymbol, SYMBOL_CURRENCY_BASE,SymbolInfoString(_Symbol, SYMBOL_CURRENCY_BASE));
CustomSymbolSetString(InpCustomSymbol, SYMBOL_CURRENCY_PROFIT,SymbolInfoString(_Symbol, SYMBOL_CURRENCY_PROFIT));
SymbolSelect(InpCustomSymbol, true); // Make visible
Print("Custom symbol created: ", InpCustomSymbol);
return true;
}
//+------------------------------------------------------------------+
//| Check if a chart is already open |
//+------------------------------------------------------------------+
bool IsChartOpen(string symbol, ENUM_TIMEFRAMES tf)
{
long id = ChartFirst(); // Get first chart ID
//--- Loop through all open charts
while(id != -1)
{
//--- If both symbol and timeframe match, chart is already open
if(ChartSymbol(id) == symbol && ChartPeriod(id) == tf)
return true;
id = ChartNext(id); // Move to next chart
}
return false; // No matching chart found
}
//+------------------------------------------------------------------+
//| Open custom chart if not already open |
//+------------------------------------------------------------------+
void OpenCustomChart()
{
//--- Prevent opening multiple duplicate charts
if(IsChartOpen(InpCustomSymbol, PERIOD_M1))
{
Print("Chart already open");
return;
}
//--- Open chart (M1 is just a container, not actually used for logic)
long id = ChartOpen(InpCustomSymbol, PERIOD_M1);
if(id == 0)
Print("Failed to open chart: ", GetLastError());
}
//+------------------------------------------------------------------+
//| Finalize and store completed candle |
//+------------------------------------------------------------------+
void CommitCompletedBar()
{
//--- Ensure bar time is always increasing
if(bar_time <= last_bar_time)
bar_time = last_bar_time + 1;
last_bar_time = bar_time;
MqlRates rates[1];
//--- Final OHLC values for completed bar
rates[0].time = bar_time;
rates[0].open = open_price;
rates[0].high = high_price;
rates[0].low = low_price;
rates[0].close = close_price;
rates[0].tick_volume = InpTicksPerBar; // fixed tick count
rates[0].spread = 0;
rates[0].real_volume = 0;
//--- Store final bar
if(!CustomRatesUpdate(InpCustomSymbol, rates))
Print("CommitCompletedBar failed: ", GetLastError());
}
//+------------------------------------------------------------------+
//| Update current (forming) candle |
//+------------------------------------------------------------------+
void UpdateCurrentBar()
{
MqlRates rates[1];
//--- Populate current candle structure
rates[0].time = bar_time;
rates[0].open = open_price;
rates[0].high = high_price;
rates[0].low = low_price;
rates[0].close = close_price;
rates[0].tick_volume = (long)tick_count; // number of ticks so far
rates[0].spread = 0;
rates[0].real_volume = 0;
//--- Push update to chart (this redraws candle live)
CustomRatesUpdate(InpCustomSymbol, rates);
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Ensure custom symbol exists
if(!CreateCustomSymbol())
return INIT_FAILED;
//--- Optionally open chart
if(InpOpenChart)
OpenCustomChart();
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
MqlTick tick;
//--- Get latest tick from broker
if(!SymbolInfoTick(_Symbol, tick))
return;
//--- Ignore invalid price
if(tick.bid <= 0)
return;
double price = tick.bid;
datetime now = tick.time;
//--- If first tick of new bar, initialize OHLC
if(tick_count == 0)
{
open_price = price;
high_price = price;
low_price = price;
bar_time = now; // starting time of this bar
}
//--- Update High and Low
if(price > high_price)
high_price = price;
if(price < low_price)
low_price = price;
//--- Always update Close
close_price = price;
//--- Increase tick counter
tick_count++;
//--- Update live (forming) candle
UpdateCurrentBar();
//--- If tick limit reached, close bar
if(tick_count >= InpTicksPerBar)
{
CommitCompletedBar(); // finalize candle
tick_count = 0; // reset for next bar
}
}
//+------------------------------------------------------------------+