RenkoBR/CustomCharts_BKP_ComVolume.mqh
super.admin 63d7c86ec6 convert
2025-05-30 16:20:23 +02:00

829 lines
No EOL
65 KiB
MQL5

//+------------------------------------------------------------------+
//| CustomCharts.mqh |
//| Copyright 2019, Guilherme Santos. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Guilherme Santos."
#property link "fishguil@gmail.com"
#property version "1.1"
//+------------------------------------------------------------------+
//| defines |
//+------------------------------------------------------------------+
#define TIME_MIN D'1970.01.01 00:00'
#define TIME_MAX D'3000.12.31 23:59'
#define CUSTOM_SYMBOL_PATH "RenkoBR"
//+------------------------------------------------------------------+
//| types |
//+------------------------------------------------------------------+
enum ENUM_CUSTOM_TYPE
{
RENKO_TYPE_TICKS, //Renko Ticks
RENKO_TYPE_PIPS, //Renko Pips
RENKO_TYPE_POINTS, //Renko Points
RENKO_TYPE_R, //Renko R (-1 Tick)
RENKO_TYPE_ATR, //Renko ATR
POINTS_TYPE_TICKS, //Point Ticks
POINTS_TYPE_PIPS, //Point Pips
POINTS_TYPE_POINTS, //Point
POINTS_TYPE_P, //Point R (-1 Tick)
POINTS_TYPE_ATR //Point ATR
};
//+------------------------------------------------------------------+
//| class |
//+------------------------------------------------------------------+
class CustomCharts
{
//Internal Variables
private:
//Buffers
MqlTick ticks[], //Ticks buffer
custom_ticks[]; //Custom Ticks buffer
MqlRates rates[], //Rates buffer
custom_rates[]; //Custom Rates buffer
datetime real_time[]; //Real time buffer
//Cursor
long last_tick; //Tick cursor
datetime last_rate; //Rate cursor
//Counters
int rates_count, //Rates Count
ticks_count, //Ticks Count
custom_rates_count, //Custom Rates Count
custom_ticks_count; //Custom Ticks Count
//Config
ENUM_CUSTOM_TYPE custom_type; //Custom Type
string chart_symbol, //Original symbol
custom_symbol; //Custom symbol
double chart_size, //Chart size
brick_size, //Brick size
tick_size, //Tick size
up_wick, //Upper wick size
down_wick; //Down wick size
bool show_wicks, //Show wicks
asymetric_reversal, //Asymetric Reversal
gaps, //Gaps
level; //Level
ENUM_TIMEFRAMES open_time; //Open Time
ENUM_TIMEFRAMES atr_time_frame; //ATR Time frame
int atr_ma_period; //ATR MA Period (1 = Off)
ENUM_MA_METHOD atr_ma_method; //ATR MA Method
double atr_percentage; //% of ATR
//History
datetime rates_history; //Rates History
bool real_ticks, //Real Ticks
clear_history, //Clear History
advance; //Ajust time
//Internals
int tick_msc; //Tick msc
long tick_volumes, //Tick Volumes
volumes; //Volumes
int hATR, hMA; //Handles
//Tick counters
datetime calc_tick_time;
long calc_tick_volume, calc_tick_volume_real;
//Methods
public:
CustomCharts();
~CustomCharts();
bool Setup(string symbol, ENUM_CUSTOM_TYPE type, double size, bool wicks, bool asymetric, bool gaps, bool level, bool advance
, ENUM_TIMEFRAMES ATRTimeFrame, int ATRMAPeriod, ENUM_MA_METHOD ATRMAMethod, double ATRPercentage
, datetime rates_history, bool real_ticks, bool clear_history
);
bool Setup(string symbol, ENUM_CUSTOM_TYPE type, double size, bool wicks, bool asymetric, bool gaps, bool level, bool advance
, datetime rates_history, bool real_ticks, bool clear_history
);
double GetValue(int buffer, int index);
double GetValueAsSeries(int buffer, int index);
MqlRates GetRate(int index);
MqlRates GetRateAsSeries(int index);
int UpdateRates(datetime from);
int UpdateTicks(datetime from, bool last);
//Custom Symbol Methods
string GetSymbolName();
bool CreateCustomSymbol(string name);
bool DeleteCustomSymbol();
int ReplaceCustomSymbol();
long OpenCustomSymbol();
void SetCustomSymbol(long chart_id);
bool ValidateSymbol(string &name);
int UpdateCustomRates();
int AddCustomTicks();
//Event Methods
void Refresh();
//Internal Methods
private:
double ATRBrickSize();
int AddOne(datetime time, double price);
int CloseUp(double points, datetime time, int spread);
int CloseDown(double points, datetime time, int spread);
int OnCalculate(double price, datetime time, long tick_volume, long volume, int spread);
int OnCalculate(const MqlRates &price);
int OnCalculate(const MqlTick &price);
int OnCalculateOHLC(const MqlRates &price);
};
//+------------------------------------------------------------------+
//| methods |
//+------------------------------------------------------------------+
//Default Constructors
CustomCharts::CustomCharts()
{
return;
}
CustomCharts::~CustomCharts()
{
ArrayFree(rates);
ArrayFree(custom_rates);
ArrayFree(ticks);
ArrayFree(custom_ticks);
SymbolSelect(custom_symbol,false);
CustomSymbolDelete(custom_symbol);
}
//Setup Renko
bool CustomCharts::Setup(
string wp_symbol = "", // Symbol (Default = current)
ENUM_CUSTOM_TYPE wp_type = RENKO_TYPE_TICKS, // Type
double wp_size = 14, // Brick Size (Ticks, Pips, Points or ATR)
bool wp_wicks = true, // Show Wicks
bool wp_asymetric = true, // Asymetric Reversals (Artificial open on reversal. RENKO ONLY!)
bool wp_gaps = false, // Gaps
bool wp_level = true, // Level Bricks
bool wp_advance = true, // Advance in time
ENUM_TIMEFRAMES wp_ATRTimeFrame = PERIOD_H1, // ATR Time frame
int wp_ATRMAPeriod = 1, // ATR MA Period (1 = Off)
ENUM_MA_METHOD wp_ATRMAMethod = MODE_SMA, // ATR MA Method
double wp_ATRPercentage = 100, // % of ATR
datetime wp_rates_history = 0, // History: Rates (Default is 7 Days 1M OHLC rates.)
bool wp_real_ticks = false, // History: Real Ticks (History based on real ticks. SLOWER!)
bool wp_clear_history = true // History: Clear Custom Symbol
)
{
//Check Symbol
if(wp_symbol == "" || wp_symbol == NULL)
{
Print("Line:",__LINE__," - Invalid symbol selected.");
return(false);
}
//Select Symbol
if(SymbolSelect(wp_symbol, true) == false)
{
Print("Line:",__LINE__," - Symbol selection error.");
return(false);
}
//Buffers
this.rates_count = this.custom_rates_count = ArrayResize(custom_rates, 0, 1000);
this.ticks_count = this.custom_ticks_count = ArrayResize(custom_ticks, 0, 1000);
this.last_rate = 0;
this.last_tick = 0;
//Chart setup
this.chart_symbol = wp_symbol;
this.custom_type = wp_type;
this.chart_size = wp_size;
this.show_wicks = wp_wicks;
this.asymetric_reversal = (custom_type >= RENKO_TYPE_TICKS && custom_type <= RENKO_TYPE_ATR) ? wp_asymetric : false;
this.gaps = wp_gaps;
this.level = wp_level;
//ATR setup
this.atr_time_frame = wp_ATRTimeFrame;
this.atr_ma_period = wp_ATRMAPeriod;
this.atr_ma_method = wp_ATRMAMethod;
this.atr_percentage = wp_ATRPercentage;
//History
if(wp_rates_history == 0)
wp_rates_history = TruncateTime(TimeTradeServer(), PERIOD_D1) - 7 * 86400; //Default is 7 days
this.rates_history = wp_rates_history;
this.real_ticks = wp_real_ticks;
this.clear_history = wp_clear_history;
this.advance = wp_advance;
//Brick size
int digits = (int) SymbolInfoInteger(wp_symbol, SYMBOL_DIGITS);
double points = SymbolInfoDouble(wp_symbol, SYMBOL_POINT);
this.tick_size = SymbolInfoDouble(wp_symbol, SYMBOL_TRADE_TICK_SIZE);
double pip_size = (digits == 5 || digits == 3) ? points * 10 : points;
if(custom_type == POINTS_TYPE_TICKS || custom_type == RENKO_TYPE_TICKS) brick_size = chart_size * tick_size;
else if(custom_type == POINTS_TYPE_PIPS || custom_type == RENKO_TYPE_PIPS) brick_size = chart_size * pip_size;
else if(custom_type == POINTS_TYPE_P || custom_type == RENKO_TYPE_R) brick_size = chart_size * tick_size - tick_size;
else if(custom_type == POINTS_TYPE_ATR || custom_type == RENKO_TYPE_ATR) brick_size = this.ATRBrickSize();
else brick_size = chart_size;
brick_size = NormalizeDouble(brick_size, digits);
//Invalid brick size
if(brick_size <= 0)
{
Print("Line:",__LINE__," - Invalid brick size. Value of ", brick_size, " selected.");
return(false);
}
//Minimum brick size
if(brick_size < tick_size)
{
Print("Line:",__LINE__," - Invalid brick size. Minimum value of ", tick_size, " will be used.");
brick_size = tick_size;
}
//Success
return(true);
}
bool CustomCharts::Setup(
string wp_symbol = "", // Symbol (Default = current)
ENUM_CUSTOM_TYPE wp_type = RENKO_TYPE_TICKS, // Type
double wp_size = 14, // Brick Size (Ticks, Pips, Points or ATR)
bool wp_wicks = true, // Show Wicks
bool wp_asymetric = true, // Asymetric Reversals (Artificial open on reversal. RENKO ONLY!)
bool wp_gaps = false, // Gaps
bool wp_level = true, // Level Bricks
bool wp_advance = true, // Advance in time
datetime wp_rates_history = 0, // History: Rates (Default is 7 Days 1M OHLC rates.)
bool wp_real_ticks = false, // History: Real Ticks (History based on real ticks. SLOWER!)
bool wp_clear_history = true // History: Clear Custom Symbol
)
{
if(wp_type == RENKO_TYPE_ATR || wp_type == POINTS_TYPE_ATR)
return false;
else
return this.Setup(wp_symbol, wp_type, wp_size, wp_wicks, wp_asymetric, wp_gaps, wp_level, wp_advance, PERIOD_H1, 1, MODE_SMA, 100, wp_rates_history, wp_real_ticks, wp_clear_history);
}
//ATR brick size
double CustomCharts::ATRBrickSize()
{
double atr_size = 0;
// ATR Config
hATR = iATR(chart_symbol, atr_time_frame, (int) chart_size);
hMA = iMA(chart_symbol, atr_time_frame, atr_ma_period, 0, atr_ma_method, hATR);
if(hATR==INVALID_HANDLE || hMA==INVALID_HANDLE)
{
MessageBox("Renko ATR error!", __FILE__, MB_OK);
return 0;
}
// ATR buffer
double buffer[1];
int copied = 0;
if(atr_ma_period > 1)
copied = CopyBuffer(hMA, 0, 1, 1, buffer);
else
copied = CopyBuffer(hATR, 0, 1, 1, buffer);
if(copied <= 0)
return 0;
// ATR size
atr_size = NormalizeDouble(buffer[0] * atr_percentage / 100.0, _Digits);
return atr_size;
}
//Add one to buffer array
int CustomCharts::AddOne(datetime time = 0, double price = 0)
{
//Resize buffers
int index = ArrayResize(custom_rates, ArraySize(custom_rates) + 1, 1000) - 1;
ArrayResize(real_time, ArraySize(custom_rates), 1000);
if(index <= 0) return 0;
//Time
if(time == 0) time = TimeTradeServer();
real_time[index] = time;
tick_msc = 0;
//M1 Rates indexing
time = TruncateTime(time, PERIOD_M1);
if(time <= custom_rates[index-1].time)
custom_rates[index].time = custom_rates[index-1].time + 60;
else
custom_rates[index].time = time;
//Defaults
if(price == 0) price = custom_rates[index-1].close;
custom_rates[index].open = custom_rates[index].high = custom_rates[index].low = custom_rates[index].close = price;
custom_rates[index].tick_volume = custom_rates[index].real_volume = 0;
custom_rates[index].spread = 0;
return index;
}
//Add positive points bar
int CustomCharts::CloseUp(double points, datetime time=0, int spread=0)
{
int index = ArraySize(custom_rates) -1;
//OHLC
double price = custom_rates[index-1].close;
if(TruncateTime(time, PERIOD_D1) != TruncateTime(custom_rates[index-1].time, PERIOD_D1))
price = custom_rates[index].open;
if(asymetric_reversal) custom_rates[index].open = price + points - brick_size;
else custom_rates[index].open = price;
custom_rates[index].high = custom_rates[index].close = price + points;
//Wicks
if(show_wicks) custom_rates[index].low = down_wick;
else custom_rates[index].low = custom_rates[index].open;
down_wick = custom_rates[index].close;
//Volumes
custom_rates[index].tick_volume = tick_volumes;
custom_rates[index].real_volume = volumes;
custom_rates[index].spread = spread;
tick_volumes = volumes = 0;
//Add one
return AddOne(time);
}
//Add negative points bar
int CustomCharts::CloseDown(double points, datetime time=0, int spread=0)
{
int index = ArraySize(custom_rates) -1;
//OHLC
double price = custom_rates[index-1].close;
if(TruncateTime(time, PERIOD_D1) != TruncateTime(custom_rates[index-1].time, PERIOD_D1))
price = custom_rates[index].open;
if(asymetric_reversal) custom_rates[index].open = price - points + brick_size;
else custom_rates[index].open = price;
custom_rates[index].low = custom_rates[index].close = price - points;
//Wicks
if(show_wicks) custom_rates[index].high = up_wick;
else custom_rates[index].high = custom_rates[index].open;
up_wick = custom_rates[index].close;
//Volumes
custom_rates[index].tick_volume = tick_volumes;
custom_rates[index].real_volume = volumes;
custom_rates[index].spread = spread;
tick_volumes = volumes = 0;
//Add one
return AddOne(time);
}
//Load price information
datetime calc_rates_time, current_time;
long calc_rates_volume, calc_rates_volume_real;
int CustomCharts::OnCalculate(double price, datetime time=0, long tick_volume=0, long volume=0, int spread=0)
{
//Wicks
up_wick = MathMax(up_wick, price);
down_wick = MathMin(down_wick, price);
if(down_wick <= 0) down_wick = price;
//Time
current_time = TruncateTime(SymbolInfoInteger(chart_symbol, SYMBOL_TIME), PERIOD_M1);
if(time != calc_rates_time)
{
calc_rates_time = time;
tick_volumes += calc_rates_volume;
volumes += calc_rates_volume_real;
}
//Volume
calc_rates_volume = tick_volume;
calc_rates_volume_real = volume;
//Buffer
int size = ArraySize(custom_rates);
int index = size-1;
//First bricks
if(size==0)
{
//1st Buffers
ArrayResize(custom_rates, 2, 1000);
custom_rates[0].time = time - 120;
custom_rates[0].open = NormalizeDouble(MathFloor(price/brick_size) * brick_size,_Digits) - brick_size;
custom_rates[0].high = custom_rates[0].low = custom_rates[0].close = custom_rates[0].open;
custom_rates[0].tick_volume = custom_rates[0].real_volume = 0;
custom_rates[0].spread = 0;
custom_rates[1].time = time - 60;
custom_rates[1].open = custom_rates[1].low = custom_rates[0].close;
custom_rates[1].high = custom_rates[1].close = custom_rates[0].close + brick_size;
custom_rates[1].tick_volume = custom_rates[1].real_volume = 0;
custom_rates[1].spread = 0;
//Current Buffer
up_wick = down_wick = price;
index = AddOne(time);
}
// New Daily Open
if(this.gaps)
if(TruncateTime(time, PERIOD_D1) != TruncateTime(real_time[index], PERIOD_D1))
{
if(this.level)
price = NormalizeDouble(MathFloor(price/brick_size) * brick_size,_Digits);
up_wick = down_wick = price;
index = AddOne(time, price);
}
// Points
if(custom_type == POINTS_TYPE_TICKS
|| custom_type == POINTS_TYPE_PIPS
|| custom_type == POINTS_TYPE_POINTS
|| custom_type == POINTS_TYPE_ATR)
{
//Fill
//Up
if(price >= custom_rates[index].open+brick_size)
for(; price >= custom_rates[index].open+brick_size;)
index = CloseUp(brick_size, time, spread);
//Down
if(price <= custom_rates[index].open-brick_size)
for(; price <= custom_rates[index].open-brick_size;)
index = CloseDown(brick_size, time, spread);
}
else if(custom_type == POINTS_TYPE_P)
{
//Up
if(price > custom_rates[index].open)
for(; price > custom_rates[index].open+brick_size;)
index = CloseUp(brick_size, time, spread);
//Down
if(price < custom_rates[index].open)
for(; price < custom_rates[index].open-brick_size;)
index = CloseDown(brick_size, time, spread);
}
//Renko
else if(custom_type == RENKO_TYPE_TICKS
|| custom_type == RENKO_TYPE_PIPS
|| custom_type == RENKO_TYPE_POINTS
|| custom_type == RENKO_TYPE_ATR)
{
//Up
if(custom_rates[index-1].close >= custom_rates[index-2].close){
if(price >= custom_rates[index-1].close+brick_size)
{
for(; price >= custom_rates[index-1].close+brick_size;)
index = CloseUp(brick_size, time, spread);
}
//Reversal
else if(price <= custom_rates[index-1].close - brick_size * 2.0)
{
index = CloseDown(brick_size * 2.0, time, spread);
for(; price <= custom_rates[index-1].close-brick_size;)
index = CloseDown(brick_size, time, spread);
}
}
//Down
if(custom_rates[index-1].close <= custom_rates[index-2].close){
if(price <= custom_rates[index-1].close-brick_size)
{
for(; price <= custom_rates[index-1].close-brick_size;)
index = CloseDown(brick_size, time, spread);
}
//Reversal
else if(price >= custom_rates[index-1].close + brick_size * 2.0)
{
index = CloseUp(brick_size * 2.0, time, spread);
for(; price >= custom_rates[index-1].close+brick_size;)
index = CloseUp(brick_size, time, spread);
}
}
}
else if(custom_type == RENKO_TYPE_R)
{
//Up
if(custom_rates[index-1].close >= custom_rates[index-2].close)
{
if(price > custom_rates[index-1].close+brick_size)
{
for(; price > custom_rates[index-1].close+brick_size;)
index = CloseUp(brick_size, time, spread);
}
//Reversal
else if(price < custom_rates[index-1].close - brick_size * 2.0)
{
index = CloseDown(brick_size * 2.0, time, spread);
for(; price < custom_rates[index-1].close-brick_size;)
index = CloseDown(brick_size, time, spread);
}
}
//Down
if(custom_rates[index-1].close <= custom_rates[index-2].close)
{
if(price < custom_rates[index-1].close-brick_size)
{
for(; price < custom_rates[index-1].close-brick_size;)
index = CloseDown(brick_size, time, spread);
}
//Reversal
else if(price > custom_rates[index-1].close + brick_size * 2.0)
{
index = CloseUp(brick_size * 2.0, time, spread);
for(; price > custom_rates[index-1].close+brick_size;)
index = CloseUp(brick_size, time, spread);
}
}
}
//Advance in time
if(!this.advance)
if(custom_rates[index].time > current_time)
{
datetime from = 0;
custom_rates[index].time = current_time;
CustomTicksDelete(custom_symbol, current_time * 1000, LONG_MAX);
for(int i = index; i > 1; i--)
if(custom_rates[i].time <= custom_rates[i-1].time)
custom_rates[i-1].time = from = custom_rates[i].time - 60;
else
break;
}
//Buffer
custom_rates[index].high = up_wick;
custom_rates[index].low = down_wick;
custom_rates[index].close = price;
custom_rates[index].tick_volume = tick_volumes + tick_volume;
custom_rates[index].real_volume = volumes + volume;
custom_rates[index].spread = spread;
//Size
//last_rate = custom_rates[index-1].time;
last_rate = custom_rates[index].time;
size = rates_count = ArraySize(custom_rates);
return size;
}
//Load price rates information
int CustomCharts::OnCalculate(const MqlRates &price)
{
//Price
return this.OnCalculate(price.close, price.time, price.tick_volume, price.real_volume, price.spread);
}
//Load price tick information
int CustomCharts::OnCalculate(const MqlTick &price)
{
//Truncate current time
datetime time = TruncateTime(price.time, PERIOD_M1);
//Calculating volume
if(time > calc_tick_time)
calc_tick_volume_real = calc_tick_volume = 0;
calc_tick_volume += (long) price.volume;
calc_tick_volume_real += (long) price.volume_real;
//Calculate bars
int size = 0;
if(price.bid > price.ask) //Auction
size = 0;
else if(SymbolInfoInteger(custom_symbol, SYMBOL_CHART_MODE) == SYMBOL_CHART_MODE_BID && price.bid > 0)
size = this.OnCalculate(price.bid, time, calc_tick_volume, calc_tick_volume_real);
else if(SymbolInfoInteger(custom_symbol, SYMBOL_CHART_MODE) == SYMBOL_CHART_MODE_LAST && price.last > 0)
size = this.OnCalculate(price.last, time, calc_tick_volume, calc_tick_volume_real);
calc_tick_time = time;
return size;
}
//Load OHLC price rates information
int CustomCharts::OnCalculateOHLC(const MqlRates &price)
{
this.OnCalculate(price.open, price.time, 0, 0, price.spread);
if(price.close > price.open)
{
this.OnCalculate(price.low, price.time, 0, 0, price.spread);
this.OnCalculate(price.high, price.time, 0, 0, price.spread);
}
else
{
this.OnCalculate(price.high, price.time, 0, 0, price.spread);
this.OnCalculate(price.low, price.time, 0, 0, price.spread);
}
return this.OnCalculate(price.close, price.time, price.tick_volume, price.real_volume, price.spread);
}
//Update rates
int CustomCharts::UpdateRates(datetime from = 0)
{
ResetLastError();
int copied = 0;
//Copy rates history
if(from == 0) from = TimeTradeServer();
if(rates_count == 0)
{
//History
copied = CopyRates(chart_symbol, PERIOD_M1, from, TimeTradeServer(), rates);
if(copied > 0)
for(int i = 0; i < copied; i++)
this.OnCalculateOHLC(rates[i]);
}
else
{
//Last rate
copied = CopyRates(chart_symbol, PERIOD_M1, last_rate, TimeTradeServer(), rates);
if(copied > 0)
for(int i = 0; i < copied; i++)
this.OnCalculate(rates[i]);
}
//Return
copied = ArraySize(custom_rates);
return copied;
}
//Update ticks
int CustomCharts::UpdateTicks(datetime from = 0, bool last = false)
{
ResetLastError();
//Copy ticks history
if(from == 0) from = TimeTradeServer();
int copied = ArrayResize(custom_ticks, 0, 1000);
if(ticks_count == 0)
if(rates_count == 0)
copied = CopyTicksRange(chart_symbol, custom_ticks, COPY_TICKS_ALL, 1000 * (long) from);
else
copied = CopyTicks(chart_symbol, custom_ticks, COPY_TICKS_ALL, 0, 1);
else if(last == true)
copied = CopyTicks(chart_symbol, custom_ticks, COPY_TICKS_ALL, 0, 1);
else
copied = CopyTicks(chart_symbol, custom_ticks, COPY_TICKS_ALL, last_tick + 1, 1000);
//Discard repeated ticks
if(copied <= 0) return 0;
if(custom_ticks[0].time_msc <= last_tick) return 0;
ticks_count += copied;
//Update custom tick
if(tick_msc < 999) tick_msc++;
for(int i = 0; i < copied; i++)
{
this.OnCalculate(custom_ticks[i]);
last_tick = custom_ticks[i].time_msc;
custom_ticks[i].time = (datetime) GetValue(0);
custom_ticks[i].time_msc = custom_ticks[i].time * 1000 + tick_msc;
}
return copied;
}
//+------------------------------------------------------------------+
//| output methods |
//+------------------------------------------------------------------+
//Get rates
MqlRates CustomCharts::GetRate(int index = -1)
{
index = (index < 0) ? ArraySize(custom_rates)-1 : index;
if(index < 0) return custom_rates[0];
return custom_rates[index];
}
//Get rates as series
MqlRates CustomCharts::GetRateAsSeries(int index = -1)
{
index = ArraySize(custom_rates)-index;
index = (index<=0) ? ArraySize(custom_rates)-1 : index-1;
if(index < 0) return custom_rates[0];
return custom_rates[index];
}
//Get values
double CustomCharts::GetValue(int buffer = 0, int index = -1)
{
index = (index < 0) ? ArraySize(custom_rates)-1 : index;
if(index<0) return EMPTY_VALUE;
switch(buffer)
{
case 0: return (double) custom_rates[index].time; break; //Time
case 1: return custom_rates[index].open; break; //Open
case 2: return custom_rates[index].high; break; //High
case 3: return custom_rates[index].low; break; //Low
case 4: return custom_rates[index].close; break; //Close
case 5: return (double) custom_rates[index].tick_volume; break; //Tick volume
case 6: return (double) custom_rates[index].real_volume; break; //Volume
case 7: return (double) custom_rates[index].spread; break; //Spread
case 8: return (double) real_time[index]; break; //Real time
default: return EMPTY_VALUE; break;
}
}
//Get values as series
double CustomCharts::GetValueAsSeries(int buffer = 0, int index = -1)
{
index = ArraySize(custom_rates)-index;
index = (index<=0) ? ArraySize(custom_rates)-1 : index-1;
if(index<0) return EMPTY_VALUE;
switch(buffer)
{
case 0: return (double) custom_rates[index].time; break; //Time
case 1: return custom_rates[index].open; break; //Open
case 2: return custom_rates[index].high; break; //High
case 3: return custom_rates[index].low; break; //Low
case 4: return custom_rates[index].close; break; //Close
case 5: return (double) custom_rates[index].tick_volume; break; //Tick volume
case 6: return (double) custom_rates[index].real_volume; break; //Volume
case 7: return (double) custom_rates[index].spread; break; //Spread
case 8: return (double) real_time[index]; break; //Real time
default: return EMPTY_VALUE; break;
}
}
//+------------------------------------------------------------------+
//| custom symbol methods |
//+------------------------------------------------------------------+
//Return custom symbol name
string CustomCharts::GetSymbolName()
{
return custom_symbol;
}
//Create points custom symbol
bool CustomCharts::CreateCustomSymbol(string name = "")
{
ResetLastError();
custom_symbol = name;
//Symbol name
if(name == "" || name == NULL)
{
if(custom_type == RENKO_TYPE_TICKS) custom_symbol = StringFormat("%s.R%gtks", chart_symbol, chart_size);
else if(custom_type == RENKO_TYPE_PIPS) custom_symbol = StringFormat("%s.R%gpip", chart_symbol, chart_size);
else if(custom_type == RENKO_TYPE_POINTS) custom_symbol = StringFormat("%s.R%gpts", chart_symbol, chart_size);
else if(custom_type == RENKO_TYPE_R) custom_symbol = StringFormat("%s.R%g", chart_symbol, chart_size);
else if(custom_type == POINTS_TYPE_TICKS) custom_symbol = StringFormat("%s.P%gtks", chart_symbol, chart_size);
else if(custom_type == POINTS_TYPE_PIPS) custom_symbol = StringFormat("%s.P%gpip", chart_symbol, chart_size);
else if(custom_type == POINTS_TYPE_POINTS) custom_symbol = StringFormat("%s.P%gpts", chart_symbol, chart_size);
else if(custom_type == POINTS_TYPE_P) custom_symbol = StringFormat("%s.P%g", chart_symbol, chart_size);
else if(custom_type == RENKO_TYPE_ATR) custom_symbol = StringFormat("%s.R%gatr.%s", chart_symbol, chart_size, StringPeriod(atr_time_frame));
else if(custom_type == POINTS_TYPE_ATR) custom_symbol = StringFormat("%s.P%gatr.%s", chart_symbol, chart_size, StringPeriod(atr_time_frame));
//ATR
if(custom_type == RENKO_TYPE_ATR || custom_type == POINTS_TYPE_ATR)
StringConcatenate(custom_symbol
, custom_symbol
, (atr_ma_period > 1) ? StringFormat(".%s%g", StringMethod(atr_ma_method), atr_ma_period) : ""
, (atr_percentage != 100) ? StringFormat(".%gpc", atr_percentage) : ""
);
}
//Delete symbol information
bool is_custom = false;
if(SymbolExist(custom_symbol, is_custom)){
CustomTicksDelete(custom_symbol, 1000 * TIME_MIN, 1000 * TIME_MAX);
CustomRatesDelete(custom_symbol, TIME_MIN, TIME_MAX);
return true;
}
//Create symbol
return CustomSymbolCreate(custom_symbol, CUSTOM_SYMBOL_PATH, chart_symbol);
}
//Update custom rates
int CustomCharts::UpdateCustomRates()
{
ResetLastError();
if(rates_count <= 0) return 0;
if(rates_count == custom_rates_count) return 0;
int copied = CustomRatesUpdate(custom_symbol, custom_rates);
if(copied > 0) custom_rates_count = copied;
/*
//Info
if(_LastError > 0)
{
printf("[%s] CustomRatesUpdate error: %d, %d of %d rates copied.", _Symbol, _LastError, copied, ArraySize(custom_rates));
ArrayPrint(custom_rates, _Digits, NULL, 0, 10);
}
//Return
*/
return copied;
}
//Update custom tick
int CustomCharts::AddCustomTicks()
{
ResetLastError();
if(ArraySize(custom_ticks) <= 0) return 0;
int copied = CustomTicksAdd(custom_symbol, custom_ticks);
if(copied > 0) custom_ticks_count += copied;
/*
//Info
if(_LastError > 0)
{
printf("[%s] CustomTicksAdd error: %d, %d of %d ticks added.", _Symbol, _LastError, copied, ArraySize(custom_ticks));
ArrayPrint(custom_ticks, _Digits, NULL, 0, 10);
}
*/
return copied;
}
//Refresh custom data
void CustomCharts::Refresh()
{
//History
if(rates_count == 0)
{
if(real_ticks)
UpdateTicks(rates_history);
else
UpdateRates(rates_history);
UpdateCustomRates();
return;
}
//Update custom symbol
if(UpdateTicks() > 0) AddCustomTicks();
UpdateCustomRates();
}
//+------------------------------------------------------------------+
//| custom functions |
//+------------------------------------------------------------------+
string StringAt(string text, string separator, int position = 0)
{
string result[];
ushort s = StringGetCharacter(separator, 0);
int n = StringSplit(text, s, result);
if(position < n)
return result[position];
else
return "";
}
string StringPeriod(ENUM_TIMEFRAMES value)
{
if(value == PERIOD_CURRENT) value = (ENUM_TIMEFRAMES) _Period;
string period = EnumToString(value);
return StringSubstr(period, 7);
}
string StringMethod(ENUM_MA_METHOD value)
{
string method = EnumToString(value);
return StringSubstr(method, 5);
}
datetime TruncateTime(datetime tm, ENUM_TIMEFRAMES tf) {
MqlDateTime ts;
switch (tf) {
case PERIOD_MN1:
TimeToStruct(tm, ts);
return tm - (tm % 86400) - ((ts.day - 1) * 86400);
case PERIOD_W1:
return (((tm - 259200) / 604800) * 604800) + 259200;
default:
return tm - (tm % (PeriodSeconds(tf)));
}
}