This commit is contained in:
Matt Corcoran
2025-07-04 16:56:12 +02:00
parent b3357f7bb7
commit 10c1d86a0c
27 changed files with 3161 additions and 2040 deletions
+52
View File
@@ -0,0 +1,52 @@
#include <Trade/Trade.mqh>
enum CUSTOM_MAX_TYPE {
CM_WIN_LOSS_RATIO,
CM_WIN_PERCENT
};
class CustomMax : public CObject {
protected:
double custom_criteria;
double win_loss_ratio(int min_required_trades);
double win_percent_min_trades(int min_required_trades);
public:
double calculate_custom_criteria(CUSTOM_MAX_TYPE cm_type, int min_trades = 0);
};
double CustomMax::calculate_custom_criteria(CUSTOM_MAX_TYPE cm_type, int min_trades) {
switch(cm_type) {
case CM_WIN_LOSS_RATIO:
custom_criteria = win_loss_ratio(min_trades);
break;
case CM_WIN_PERCENT:
custom_criteria = win_percent_min_trades(min_trades);
break;
default:
custom_criteria = 0;
break;
}
return custom_criteria;
}
// Returns the win/loss ratio, with min trades check
double CustomMax::win_loss_ratio(int min_required_trades) {
double wins = TesterStatistics(STAT_PROFIT_TRADES);
double losses = TesterStatistics(STAT_LOSS_TRADES);
double total_trades = TesterStatistics(STAT_TRADES);
if((min_required_trades > 0 && total_trades < min_required_trades) || total_trades == 0) return 0;
if(losses == 0) return 0; // Prevent division by zero
return wins / losses;
}
// Returns the win percentage, with min trades check
double CustomMax::win_percent_min_trades(int min_required_trades) {
double wins = TesterStatistics(STAT_PROFIT_TRADES);
double total_trades = TesterStatistics(STAT_TRADES);
if((min_required_trades > 0 && total_trades < min_required_trades) || total_trades == 0) return 0;
double result = wins / total_trades * 100;
if(!MathIsValidNumber(result)) return 0;
return result;
}
+51
View File
@@ -0,0 +1,51 @@
enum MODE_SPLIT_DATA{
NO_SPLIT,
ODD_YEARS,
EVEN_YEARS,
ODD_MONTHS,
EVEN_MONTHS,
ODD_WEEKS,
EVEN_WEEKS
};
class TestDataSplit {
public:
bool in_test_period(MODE_SPLIT_DATA data_split_method);
};
bool TestDataSplit::in_test_period(MODE_SPLIT_DATA data_split_method) {
string result[];
string string_tc = TimeToString(TimeCurrent());
// Extract components from datetime string (assumes YYYY.MM.DD format)
ushort u_sep = StringGetCharacter(".", 0);
StringSplit(string_tc, u_sep, result);
bool odd_year = int(result[0]) % 2;
bool odd_month = int(result[1]) % 2;
// Calculate week of the year (basic approximation)
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
int iDay = (dt.day_of_week + 6) % 7 + 1; // Convert to 1=Mon,...,7=Sun
int iWeek = (dt.day_of_year - iDay + 10) / 7; // Estimate ISO week number
bool odd_week = iWeek % 2;
// Split logic depending on mode
if (data_split_method == NO_SPLIT)
return true;
if (data_split_method == ODD_YEARS && odd_year)
return true;
if (data_split_method == EVEN_YEARS && !odd_year)
return true;
if (data_split_method == ODD_MONTHS && odd_month)
return true;
if (data_split_method == EVEN_MONTHS && !odd_month)
return true;
if (data_split_method == ODD_WEEKS && odd_week)
return true;
if (data_split_method == EVEN_WEEKS && !odd_week)
return true;
return false;
}
-90
View File
@@ -1,90 +0,0 @@
#property library
#include <Trade/Trade.mqh>
enum CUSTOM_MAX_TYPE{
CM_WIN_LOSS_RATIO,
CM_WIN_PERCENT,
CM_WIN_PERCENT_200T,
CM_WIN_PERCENT_300T,
CM_WIN_PERCENT_400T,
CM_WIN_PERCENT_500T,
CM_WIN_PERCENT_600T,
CM_WIN_PERCENT_700T,
CM_WIN_PERCENT_800T,
CM_WIN_PERCENT_900T,
CM_WIN_PERCENT_1000T,
};
class CustomMax : public CObject{
protected:
double custom_criteria;
double CustomMax::win_loss_ratio();
double CustomMax::win_percent();
double CustomMax::win_percent_min_trades(int min_resuired_trades);
public:
double CustomMax::calculate_custom_criteria(CUSTOM_MAX_TYPE cm_type);
};
// CM_WIN_LOSS_RATIO,
// CM_WIN_PERCENT
double CustomMax::calculate_custom_criteria(CUSTOM_MAX_TYPE cm_type){
if(cm_type==CM_WIN_LOSS_RATIO){
custom_criteria = win_loss_ratio();
}
if(cm_type==CM_WIN_PERCENT){
custom_criteria = win_percent_min_trades(0);
}
if(cm_type==CM_WIN_PERCENT_200T){
custom_criteria = win_percent_min_trades(200);
}
if(cm_type==CM_WIN_PERCENT_300T){
custom_criteria = win_percent_min_trades(300);
}
if(cm_type==CM_WIN_PERCENT_400T){
custom_criteria = win_percent_min_trades(400);
}
if(cm_type==CM_WIN_PERCENT_500T){
custom_criteria = win_percent_min_trades(500);
}
if(cm_type==CM_WIN_PERCENT_600T){
custom_criteria = win_percent_min_trades(600);
}
if(cm_type==CM_WIN_PERCENT_700T){
custom_criteria = win_percent_min_trades(700);
}
if(cm_type==CM_WIN_PERCENT_800T){
custom_criteria = win_percent_min_trades(800);
}
if(cm_type==CM_WIN_PERCENT_900T){
custom_criteria = win_percent_min_trades(900);
}
if(cm_type==CM_WIN_PERCENT_1000T){
custom_criteria = win_percent_min_trades(1000);
}
return custom_criteria;
}
double CustomMax::win_loss_ratio(){
double wins = TesterStatistics(STAT_PROFIT_TRADES);
double losses = TesterStatistics(STAT_LOSS_TRADES);
return wins/losses;
}
double CustomMax::win_percent_min_trades(int min_resuired_trades){
double wins = TesterStatistics(STAT_PROFIT_TRADES);
double total_trades = TesterStatistics(STAT_TRADES);
double result = wins / total_trades * 100;
if(!MathIsValidNumber(result) || total_trades<min_resuired_trades){
return 0;
}
else{
return result;
}
}
+274
View File
@@ -0,0 +1,274 @@
#include <Trade/Trade.mqh>
#include <MyLibs/Utils/TimeZones.mqh>
#include <MyLibs/Utils/MarketDataUtils.mqh>
class CalculatePositionData : public CObject{
protected:
CTrade trade;
CPositionInfo position;
MarketDataUtils mdu;
bool check_lots(double &lots, string symbol);
bool normalise_price(double price, double &normalizedPrice, string symbol);
public:
double calculate_stoploss(string symbol, double price, int order_side, string _sl_mode, double sl_var, ENUM_TIMEFRAMES atr_period);
double calculate_take_profit(string symbol, double price, double stoploss, int order_side, string mode_tp, double tp_var, ENUM_TIMEFRAMES atr_period);
double calculate_lots(string symbol, double sl_distance, double price, string mode_lot, double lot_var);
double calculate_trading_cost(string symbol, ulong position_ticket);
};
double CalculatePositionData::calculate_stoploss(string symbol, double price, int order_side, string mode_sl, double sl_var, ENUM_TIMEFRAMES atr_period){
// order_side int must be 1 for BUY or 2 for
double sl=0;
if(mode_sl=="NO_STOPLOSS"){
sl=0;
}
if(mode_sl=="SL_BREAKEVEN"){
// https://www.youtube.com/watch?v=idPulZ3_iR0
Alert("Not implemented yet yet");
}
if(mode_sl=="SL_FIXED_PIPS"){
// pips/poins = https://www.mql5.com/en/forum/187757
double adj_point = mdu.adjusted_point(symbol);
if(order_side == 1){
sl = price - sl_var * adj_point;
if(!normalise_price(sl,sl,symbol)){return false;}
}
if(order_side == 2){
sl = price + sl_var * adj_point;
if(!normalise_price(sl,sl,symbol)){return false;}
}
}
if(mode_sl=="SL_FIXED_PERCENT"){
if(order_side == 1){
sl = (-1.0 * sl_var * price / 100.00) + price;
if(!normalise_price(sl,sl,symbol)){return false;}
}
if(order_side == 2){
sl = sl_var * price / 100.00 + price;
if(!normalise_price(sl,sl,symbol)){return false;}
}
}
if(mode_sl=="SL_ATR_MULTIPLE"){
int _atr_handle = iATR(symbol,atr_period,14);
double atr[];
ArraySetAsSeries(atr,true);
CopyBuffer(_atr_handle,MAIN_LINE,1,1,atr);
if(order_side == 1){
sl = price - (atr[0] * sl_var);
if(!normalise_price(sl,sl,symbol)){return false;}
}
if(order_side == 2){
sl = price + (atr[0] * sl_var);
if(!normalise_price(sl,sl,symbol)){return false;}
}
}
if(mode_sl=="SL_SPECIFIED_VALUE"){
double adj_point = mdu.adjusted_point(symbol);
if(order_side == 1){
double pip_50_sl = price - 10 * adj_point;
if(sl_var >= pip_50_sl){
sl = pip_50_sl;
}
else sl = sl_var;
if(!normalise_price(sl,sl,symbol)){return false;}
}
if(order_side == 2){
double pip_50_sl = price + 10 * adj_point;
if(sl_var <= pip_50_sl){
sl = pip_50_sl;
}
else sl = sl_var;
sl = sl = sl_var;
if(!normalise_price(sl,sl,symbol)){return false;}
}
}
return sl;
}
double CalculatePositionData::calculate_take_profit(string symbol, double price, double stoploss, int order_side, string mode_tp, double _tp_var, ENUM_TIMEFRAMES atr_period){
// order_side int must be 1 for BUY or 2 for SELL
double tp=0;
if(mode_tp=="NO_TAKE_PROFIT"){
tp=0;
}
if(mode_tp=="TP_FIXED_PIPS"){
double adj_point = mdu.adjusted_point(symbol);
if(order_side == 1){
tp = price + _tp_var * adj_point;
if(!normalise_price(tp,tp,symbol)){return false;}
}
if(order_side == 2){
tp = price - _tp_var * adj_point;
if(!normalise_price(tp,tp,symbol)){return false;}
}
}
if(mode_tp=="TP_FIXED_PERCENT"){
if(order_side == 1){
tp = _tp_var * price / 100.00 + price;
if(!normalise_price(tp,tp,symbol)){return false;}
}
if(order_side == 2){
tp = (-1 * _tp_var * price / 100.00) + price;
if(!normalise_price(tp,tp,symbol)){return false;}
}
}
if(mode_tp=="TP_ATR_MULTIPLE"){
int _atr_handle = iATR(symbol,atr_period,14);
double atr[];
ArraySetAsSeries(atr,true);
CopyBuffer(_atr_handle,MAIN_LINE,1,1,atr);
if(order_side == 1){
tp = price + (atr[0] * _tp_var);
if(!normalise_price(tp,tp,symbol)){return false;}
}
if(order_side == 2){
tp = price - (atr[0] * _tp_var);
if(!normalise_price(tp,tp,symbol)){return false;}
}
}
if(mode_tp=="TP_SL_MULTIPLE"){
if(order_side == 1){
double sl_size = price - stoploss;
tp = price + (_tp_var * sl_size);
if(!normalise_price(tp,tp,symbol)){return false;}
}
if(order_side == 2){
double sl_size = stoploss - price;
tp = price - (_tp_var * sl_size);
if(!normalise_price(tp,tp,symbol)){return false;}
}
}
if(mode_tp=="TP_SPECIFIED_VALUE"){
if(_tp_var!=0){
double adj_point = mdu.adjusted_point(symbol);
if(order_side == 1){
double pip_limit = price + 10 * adj_point;
if(_tp_var <= pip_limit){
tp = pip_limit;
}
else tp = _tp_var;
if(!normalise_price(tp,tp,symbol)){return false;}
}
if(order_side == 2){
double pip_limit = price - 10 * adj_point;
if(_tp_var >= pip_limit){
tp = pip_limit;
}
else tp = _tp_var;
tp = tp = _tp_var;
if(!normalise_price(tp,tp,symbol)){return false;}
}
}
}
return tp;
}
double CalculatePositionData::calculate_lots(string symbol, double sl_distance, double price, string mode_lot, double lot_var){
double lots = 0;
double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double volume_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
double account_value = fmin(fmin(AccountInfoDouble(ACCOUNT_EQUITY),AccountInfoDouble(ACCOUNT_BALANCE)),AccountInfoDouble(ACCOUNT_MARGIN_FREE));
double risk_money = account_value * lot_var / 100;
if(mode_lot=="LOT_MODE_FIXED"){
lots = lot_var;
}
if(mode_lot=="LOT_MODE_PCT_RISK"){
double money_lot_step = (sl_distance / tick_size) * tick_value * volume_step;
lots = MathFloor(risk_money/money_lot_step) * volume_step;
}
if(mode_lot=="LOT_MODE_PCT_ACCOUNT"){
double money_lot_step = (price / tick_size) * tick_value * volume_step;
lots = MathFloor(risk_money/money_lot_step) * volume_step;
}
if(!check_lots(lots, symbol)){return false;}
return lots;
}
bool CalculatePositionData::check_lots(double &lots, string symbol){
double min = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double max = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
if(lots<min){
Print("Lot size will be set to minimum allowed volume");
lots = min;
return true;
}
if(lots>max){
Print("Lot size greater than maximum allowed volume. lots:",lots,"max:",max);
return false;
}
lots = (int)MathFloor(lots/step) * step;
return true;
}
bool CalculatePositionData::normalise_price(double price, double &normalizedPrice, string symbol){
double tickSize;
if(!SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_SIZE,tickSize)){
Print("Failed to get tick size");
return false;
}
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
normalizedPrice = NormalizeDouble(MathRound(price/tickSize)*tickSize, symbol_digits);
return true;
}
double CalculatePositionData::calculate_trading_cost(string symbol, ulong position_ticket){
position.SelectByTicket(position_ticket);
double swap = PositionGetDouble(POSITION_SWAP);
double commission = PositionGetDouble(POSITION_COMMISSION);
double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double lot_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
double lots = PositionGetDouble(POSITION_VOLUME);
double trading_cost = -1 * ((commission + swap) / tick_value * tick_size / lots);
return trading_cost;
}
+112
View File
@@ -0,0 +1,112 @@
#include <Trade/Trade.mqh>
#include <MyLibs/Orders/CalculatePositionData.mqh>
class EntryOrders {
protected:
CTrade trade;
CalculatePositionData calc;
double stop_loss;
double take_profit;
int total_open_buy_orders;
int total_open_sell_orders;
double current_price;
int count_open_positions(string symbol, int order_side, long magic_number);
public:
bool open_buy_orders(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var,string _tp_mode, double tp_var, string _lot_mode, double lot_var, long magic_number);
bool open_sell_orders(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var, long magic_number);
bool open_buy_stop_order(string symbol, bool condition, double entry_price, datetime experation,ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var, long magic_number);
bool open_sell_stop_order(string symbol, bool condition, double entry_price, datetime experation,ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var, string _tp_mode, double tp_var,string _lot_mode, double lot_var, long magic_number);
};
int EntryOrders::count_open_positions(string symbol, int order_side, long magic_number) {
int count = 0;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == symbol &&
PositionGetInteger(POSITION_MAGIC) == magic_number) {
if ((order_side == 1 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ||
(order_side == 2 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)) {
count++;
}
}
}
return count;
}
bool EntryOrders::open_buy_orders(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode,
double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var,
long magic_number) {
if (condition) {
current_price = SymbolInfoDouble(symbol, SYMBOL_ASK);
total_open_buy_orders = count_open_positions(symbol, 1, magic_number);
if (total_open_buy_orders == 0) {
stop_loss = calc.calculate_stoploss(symbol, current_price, 1, _sl_mode, sl_var, atr_period);
take_profit = calc.calculate_take_profit(symbol, current_price, stop_loss, 1, _tp_mode, tp_var, atr_period);
double sl_distance = current_price - stop_loss;
double lots = calc.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
trade.SetExpertMagicNumber(magic_number);
string comment = "Magic Number: " + IntegerToString(magic_number);
trade.PositionOpen(symbol, ORDER_TYPE_BUY, lots, current_price, stop_loss, take_profit, comment);
}
}
return true;
}
bool EntryOrders::open_sell_orders(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode,
double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var,
long magic_number) {
if (condition) {
current_price = SymbolInfoDouble(symbol, SYMBOL_BID);
total_open_sell_orders = count_open_positions(symbol, 2, magic_number);
if (total_open_sell_orders == 0) {
stop_loss = calc.calculate_stoploss(symbol, current_price, 2, _sl_mode, sl_var, atr_period);
take_profit = calc.calculate_take_profit(symbol, current_price, stop_loss, 2, _tp_mode, tp_var, atr_period);
double sl_distance = stop_loss - current_price;
double lots = calc.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
trade.SetExpertMagicNumber(magic_number);
string comment = "Magic Number: " + IntegerToString(magic_number);
trade.PositionOpen(symbol, ORDER_TYPE_SELL, lots, current_price, stop_loss, take_profit, comment);
}
}
return true;
}
bool EntryOrders::open_buy_stop_order(string symbol, bool condition, double entry_price, datetime experation,
ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var, string _tp_mode,
double tp_var, string _lot_mode, double lot_var, long magic_number) {
if (condition) {
total_open_buy_orders = count_open_positions(symbol, 1, magic_number);
if (total_open_buy_orders == 0) {
stop_loss = calc.calculate_stoploss(symbol, entry_price, 1, _sl_mode, sl_var, atr_period);
take_profit = calc.calculate_take_profit(symbol, entry_price, stop_loss, 1, _tp_mode, tp_var, atr_period);
double sl_distance = entry_price - stop_loss;
double lots = calc.calculate_lots(symbol, sl_distance, entry_price, _lot_mode, lot_var);
trade.SetExpertMagicNumber(magic_number);
string comment = "Magic Number: " + IntegerToString(magic_number);
trade.BuyStop(lots, entry_price, symbol, stop_loss, take_profit, ORDER_TIME_SPECIFIED, experation, comment);
}
}
return true;
}
bool EntryOrders::open_sell_stop_order(string symbol, bool condition, double entry_price, datetime experation,
ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var, string _tp_mode,
double tp_var, string _lot_mode, double lot_var, long magic_number) {
if (condition) {
total_open_sell_orders = count_open_positions(symbol, 2, magic_number);
if (total_open_sell_orders == 0) {
stop_loss = calc.calculate_stoploss(symbol, entry_price, 2, _sl_mode, sl_var, atr_period);
take_profit = calc.calculate_take_profit(symbol, entry_price, stop_loss, 2, _tp_mode, tp_var, atr_period);
double sl_distance = stop_loss - entry_price;
double lots = calc.calculate_lots(symbol, sl_distance, entry_price, _lot_mode, lot_var);
trade.SetExpertMagicNumber(magic_number);
string comment = "Magic Number: " + IntegerToString(magic_number);
trade.SellStop(lots, entry_price, symbol, stop_loss, take_profit, ORDER_TIME_SPECIFIED, experation, comment);
}
}
return true;
}
+153
View File
@@ -0,0 +1,153 @@
#include <Trade/Trade.mqh>
#include <MyLibs/Utils/TimeZones.mqh>
#include <MyLibs/Orders/CalculatePositionData.mqh>
class ExitOrders {
protected:
CTrade trade;
TimeZones tz;
CalculatePositionData cpd;
ulong posTicket;
long position_open_time;
long first_allowed_close_time;
public:
bool close_buy_orders(string symbol, bool condition, int close_bars, ENUM_TIMEFRAMES close_bar_period,long magic_number);
bool close_sell_orders(string symbol, bool condition, int close_bars, ENUM_TIMEFRAMES close_bar_period,long magic_number);
bool daily_timed_exit(string symbol, datetime exit_time, int delay_days, long magic_number);
bool daily_timed_profit_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, string exit_time, string cw_tzone, int delay_days, long magic_number);
bool first_profitable_close_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, long magic_number);
};
bool ExitOrders::close_buy_orders(string symbol, bool condition, int close_bars, ENUM_TIMEFRAMES close_bar_period,
long magic_number) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
posTicket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number) {
int time_difference = Bars(symbol, close_bar_period, PositionGetInteger(POSITION_TIME), TimeCurrent()) - 1;
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
if (condition || (close_bars > 0 && time_difference >= close_bars)) {
trade.PositionClose(posTicket);
}
}
}
}
return true;
}
bool ExitOrders::close_sell_orders(string symbol, bool condition, int close_bars, ENUM_TIMEFRAMES close_bar_period,
long magic_number) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
posTicket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number) {
int time_difference = Bars(symbol, close_bar_period, PositionGetInteger(POSITION_TIME), TimeCurrent()) - 1;
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
if (condition || (close_bars > 0 && time_difference >= close_bars)) {
trade.PositionClose(posTicket);
}
}
}
}
return true;
}
bool ExitOrders::daily_timed_exit(string symbol, datetime exit_time, int delay_days, long magic_number) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
posTicket = PositionGetTicket(i);
position_open_time = PositionGetInteger(POSITION_TIME);
if ((int)position_open_time > 0) {
first_allowed_close_time = position_open_time + (delay_days * PeriodSeconds(PERIOD_D1));
if (TimeCurrent() > first_allowed_close_time && TimeCurrent() >= exit_time) {
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number) {
trade.PositionClose(posTicket);
}
}
}
}
return true;
}
bool ExitOrders::daily_timed_profit_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, string exit_time,
string cw_tzone, int delay_days, long magic_number) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
posTicket = PositionGetTicket(i);
position_open_time = PositionGetInteger(POSITION_TIME);
if ((int)position_open_time > 0) {
first_allowed_close_time = position_open_time + (delay_days * PeriodSeconds(PERIOD_D1));
if (TimeCurrent() > first_allowed_close_time) {
datetime broker_close_time = tz.timezone_conversions(cw_tzone, StringToTime(exit_time), "Broker");
if (TimeCurrent() >= broker_close_time &&
PositionGetString(POSITION_SYMBOL) == symbol &&
PositionGetInteger(POSITION_MAGIC) == magic_number) {
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double spread = SymbolInfoDouble(symbol, SYMBOL_ASK) - SymbolInfoDouble(symbol, SYMBOL_BID);
double bar_close = iClose(_Symbol, close_bar_period, 1); // shift 1 because 0 is live candle
double trading_cost = cpd.calculate_trading_cost(symbol, posTicket);
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY &&
bar_close > (position_open_price + spread + trading_cost)) {
trade.PositionClose(posTicket);
}
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL &&
bar_close < (position_open_price - spread - trading_cost)) {
trade.PositionClose(posTicket);
}
}
}
}
}
return true;
}
bool ExitOrders::first_profitable_close_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, long magic_number) {
position_open_time = PositionGetInteger(POSITION_TIME);
first_allowed_close_time = position_open_time + PeriodSeconds(close_bar_period);
if ((int)position_open_time > 0 && TimeCurrent() > first_allowed_close_time) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
posTicket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number) {
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double spread = SymbolInfoDouble(symbol, SYMBOL_ASK) - SymbolInfoDouble(symbol, SYMBOL_BID);
double bar_close = iClose(_Symbol, close_bar_period, 1);
double trading_cost = cpd.calculate_trading_cost(symbol, posTicket);
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY &&
bar_close > (position_open_price + spread + trading_cost)) {
trade.PositionClose(posTicket);
}
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL &&
bar_close < (position_open_price - spread - trading_cost)) {
trade.PositionClose(posTicket);
}
}
}
}
return true;
}
+69
View File
@@ -0,0 +1,69 @@
#include <Trade/OrderInfo.mqh>
#include <Trade/PositionInfo.mqh>
class OrderTracker {
protected:
COrderInfo m_order;
CPositionInfo m_position;
public:
int count_open_positions(string symbol, int order_side, long magic_number);
int count_all_positions(string symbol, long magic_number);
int count_pending_orders(string symbol, ENUM_ORDER_TYPE order_type, long magic);
};
int OrderTracker::count_open_positions(string symbol, int order_side, long magic_number) {
int count = 0;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == symbol &&
PositionGetInteger(POSITION_MAGIC) == magic_number) {
if (order_side == 1 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
count++;
}
if (order_side == 2 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
count++;
}
}
}
return count;
}
int OrderTracker::count_all_positions(string symbol, long magic_number) {
int count = 0;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == symbol &&
PositionGetInteger(POSITION_MAGIC) == magic_number) {
count++;
}
}
return count;
}
int OrderTracker::count_pending_orders(string symbol, ENUM_ORDER_TYPE order_type, long magic) {
int count = 0;
for (int i = OrdersTotal() - 1; i >= 0; i--) {
if (m_order.SelectByIndex(i)) {
if (OrderGetInteger(ORDER_MAGIC) == magic && OrderGetString(ORDER_SYMBOL) == symbol) {
if (m_order.OrderType() == order_type) {
count++;
}
}
}
}
return count;
}
+21
View File
@@ -0,0 +1,21 @@
class StopLogic {
public:
double sl_specified_value_switch(string sl_mode, double inp_sl_var, double value);
double tp_specified_value_switch(string tp_mode, double inp_tp_var, double value);
};
double StopLogic::sl_specified_value_switch(string sl_mode, double inp_sl_var, double value) {
if (sl_mode == "SL_SPECIFIED_VALUE") {
return value;
} else {
return inp_sl_var;
}
}
double StopLogic::tp_specified_value_switch(string tp_mode, double inp_tp_var, double value) {
if (tp_mode == "SL_SPECIFIED_VALUE") {
return value;
} else {
return inp_tp_var;
}
}
+95
View File
@@ -0,0 +1,95 @@
#include <Trade/Trade.mqh>
class TrailingLogic {
protected:
CTrade trade;
public:
void break_even_stop(string symbol, ulong magic_number, int be_trigger_points, int be_puffer);
void nnfx_trailing_stop(string symbol, double sl_var, double tp_var, double atr_value, ulong magic_number);
};
void TrailingLogic::break_even_stop(string symbol, ulong magic_number, int be_trigger_points, int be_puffer) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number) {
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double symbol_point = SymbolInfoDouble(symbol, SYMBOL_POINT);
double ask = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_ASK), symbol_digits);
double bid = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_BID), symbol_digits);
if (be_trigger_points != 0) {
ulong ticket = PositionGetTicket(i);
if (PositionSelectByTicket(ticket)) {
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double position_volume = PositionGetDouble(POSITION_VOLUME);
double position_sl = PositionGetDouble(POSITION_SL);
double position_tp = PositionGetDouble(POSITION_TP);
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if (position_type == POSITION_TYPE_BUY &&
bid > position_open_price + be_trigger_points * symbol_point) {
double sl = NormalizeDouble(position_open_price + be_puffer * symbol_point, symbol_digits);
if (sl > position_sl) {
trade.PositionModify(ticket, sl, position_tp);
Print("-----------------------------------Stop moved to break even");
}
}
if (position_type == POSITION_TYPE_SELL &&
ask < position_open_price - be_trigger_points * symbol_point) {
double sl = NormalizeDouble(position_open_price - be_puffer * symbol_point, symbol_digits);
if (sl < position_sl) {
trade.PositionModify(ticket, sl, position_tp);
Print("-----------------------------------Stop moved to break even");
}
}
}
}
}
}
}
void TrailingLogic::nnfx_trailing_stop(string symbol, double sl_var, double tp_var, double atr_value, ulong magic_number) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionSelectByTicket(ticket)) {
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number) {
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double symbol_point = SymbolInfoDouble(symbol, SYMBOL_POINT);
double ask = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_ASK), symbol_digits);
double bid = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_BID), symbol_digits);
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double position_sl = PositionGetDouble(POSITION_SL);
double position_tp = PositionGetDouble(POSITION_TP);
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if (position_type == POSITION_TYPE_BUY &&
bid > position_open_price + (atr_value * tp_var)) {
double sl = NormalizeDouble(bid - (atr_value * sl_var), symbol_digits);
if (sl > (position_sl + (atr_value * 0.5))) {
trade.PositionModify(ticket, sl, position_tp);
}
}
if (position_type == POSITION_TYPE_SELL &&
ask < position_open_price - (atr_value * tp_var)) {
double sl = NormalizeDouble(ask + (atr_value * sl_var), symbol_digits);
if (sl < (position_sl + (atr_value * 0.5))) {
trade.PositionModify(ticket, sl, position_tp);
}
}
}
}
}
}
@@ -1,205 +1,205 @@
#property library #property library
#include <Trade/Trade.mqh> #include <Trade/Trade.mqh>
#include <MyLibs/MyFunctions.mqh> #include <MyLibs/Utils/BarUtils.mqh>
class DrawdownControl : public CObject { class DrawdownControl : public CObject {
protected: protected:
CTrade trade; CTrade trade;
MyFunctions mf; BarUtils bar_utils;
string data_file; string data_file;
double daily_max_dd_per; double daily_max_dd_per;
string daily_reset_time; string daily_reset_time;
bool print_statments; bool print_statments;
double acc_max_dd_per; double acc_max_dd_per;
double equaty_control_high; double equaty_control_high;
double equaty_control_low; double equaty_control_low;
double daily_equity_start; double daily_equity_start;
double daily_max_dd_target; double daily_max_dd_target;
bool daily_dd_limit_reached; bool daily_dd_limit_reached;
bool write_global_var_data(); bool write_global_var_data();
bool print_messages(); bool print_messages();
public: public:
void init_dd_control(string inp_data_file, double inp_acc_max_dd_per, double inp_daily_max_dd_per, string inp_daily_reset_time, bool inp_print_statments = true); void init_dd_control(string inp_data_file, double inp_acc_max_dd_per, double inp_daily_max_dd_per, string inp_daily_reset_time, bool inp_print_statments = true);
bool determine_daily_dd_limit(); bool determine_daily_dd_limit();
double lot_correction_factor(double acc_equity_start, double min_lot_factor, double max_lot_factor, bool dynm_lot_factor=false, double dlf_trail_per=20); double lot_correction_factor(double acc_equity_start, double min_lot_factor, double max_lot_factor, bool dynm_lot_factor=false, double dlf_trail_per=20);
double lot_correction_dynamic(double acc_dd_percent, double min_lot_factor, double max_lot_factor); double lot_correction_dynamic(double acc_dd_percent, double min_lot_factor, double max_lot_factor);
}; };
void DrawdownControl::init_dd_control(string inp_data_file, double inp_acc_max_dd_per, double inp_daily_max_dd_per, string inp_daily_reset_time, bool inp_print_statments = true) { void DrawdownControl::init_dd_control(string inp_data_file, double inp_acc_max_dd_per, double inp_daily_max_dd_per, string inp_daily_reset_time, bool inp_print_statments = true) {
data_file = inp_data_file; data_file = inp_data_file;
acc_max_dd_per = inp_acc_max_dd_per; acc_max_dd_per = inp_acc_max_dd_per;
daily_max_dd_per = inp_daily_max_dd_per; daily_max_dd_per = inp_daily_max_dd_per;
daily_reset_time = inp_daily_reset_time; daily_reset_time = inp_daily_reset_time;
print_statments = inp_print_statments; print_statments = inp_print_statments;
// If no data file exisits, create one and set global vairiables: // If no data file exisits, create one and set global vairiables:
if(FileIsExist(data_file) == false) { if(FileIsExist(data_file) == false) {
daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY); daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY);
daily_max_dd_target = daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100)); daily_max_dd_target = daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100));
daily_dd_limit_reached = false; daily_dd_limit_reached = false;
equaty_control_high = 9999999; equaty_control_high = 9999999;
equaty_control_low = 0; equaty_control_low = 0;
write_global_var_data(); write_global_var_data();
} }
// If file exisits read file: // If file exisits read file:
if(FileIsExist(data_file) == true) { if(FileIsExist(data_file) == true) {
int file_handle = FileOpen(data_file, FILE_READ | FILE_ANSI | FILE_TXT); int file_handle = FileOpen(data_file, FILE_READ | FILE_ANSI | FILE_TXT);
if(file_handle == INVALID_HANDLE) { if(file_handle == INVALID_HANDLE) {
Print("Error opening file: ", data_file); Print("Error opening file: ", data_file);
} }
// If data file is older than 24h 10min create a new file and reset global vars: // If data file is older than 24h 10min create a new file and reset global vars:
long modifided_date = FileGetInteger(file_handle, FILE_MODIFY_DATE); long modifided_date = FileGetInteger(file_handle, FILE_MODIFY_DATE);
long time_delta = ((long)TimeCurrent() - modifided_date) / 60; long time_delta = ((long)TimeCurrent() - modifided_date) / 60;
if(time_delta >= 1450) { if(time_delta >= 1450) {
daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY); daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY);
daily_max_dd_target = daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100)); daily_max_dd_target = daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100));
daily_dd_limit_reached = false; daily_dd_limit_reached = false;
equaty_control_high = equaty_control_high; equaty_control_high = equaty_control_high;
equaty_control_low = equaty_control_low; equaty_control_low = equaty_control_low;
write_global_var_data(); write_global_var_data();
Print(data_file, " is older than 24h and 10min; global vars reset!"); Print(data_file, " is older than 24h and 10min; global vars reset!");
} }
// If data file is younger than 24h+10 min read data and set global vars: // If data file is younger than 24h+10 min read data and set global vars:
else { else {
daily_equity_start = (double)FileReadString(file_handle, 0); daily_equity_start = (double)FileReadString(file_handle, 0);
daily_max_dd_target = (double)FileReadString(file_handle, 1); daily_max_dd_target = (double)FileReadString(file_handle, 1);
daily_dd_limit_reached = FileReadBool(file_handle); daily_dd_limit_reached = FileReadBool(file_handle);
equaty_control_high = (double)FileReadString(file_handle, 3); equaty_control_high = (double)FileReadString(file_handle, 3);
equaty_control_low = (double)FileReadString(file_handle, 4);; equaty_control_low = (double)FileReadString(file_handle, 4);;
} }
FileClose(file_handle); FileClose(file_handle);
} }
print_messages(); print_messages();
} }
bool DrawdownControl::determine_daily_dd_limit() { bool DrawdownControl::determine_daily_dd_limit() {
// Reset max equity at the start of each day: // Reset max equity at the start of each day:
string ct = TimeToString(TimeCurrent(), TIME_MINUTES); string ct = TimeToString(TimeCurrent(), TIME_MINUTES);
if(ct == daily_reset_time) { if(ct == daily_reset_time) {
daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY); daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY);
daily_max_dd_target = (daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100))); daily_max_dd_target = (daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100)));
daily_dd_limit_reached = false; daily_dd_limit_reached = false;
write_global_var_data(); write_global_var_data();
print_messages(); print_messages();
} }
// If in drawdown close all positions and delete orders // If in drawdown close all positions and delete orders
if(daily_dd_limit_reached || AccountInfoDouble(ACCOUNT_EQUITY) <= daily_max_dd_target) { if(daily_dd_limit_reached || AccountInfoDouble(ACCOUNT_EQUITY) <= daily_max_dd_target) {
if(daily_dd_limit_reached == false) { if(daily_dd_limit_reached == false) {
daily_dd_limit_reached = true; daily_dd_limit_reached = true;
write_global_var_data(); write_global_var_data();
print_messages(); print_messages();
} }
for(int i = PositionsTotal() - 1; i >= 0; i--) { for(int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i); ulong ticket = PositionGetTicket(i);
trade.PositionClose(ticket); trade.PositionClose(ticket);
} }
for(int i = OrdersTotal() - 1; i >= 0; i--) { for(int i = OrdersTotal() - 1; i >= 0; i--) {
ulong ticket = OrderGetTicket(i); ulong ticket = OrderGetTicket(i);
trade.OrderDelete(ticket); trade.OrderDelete(ticket);
} }
} }
return daily_dd_limit_reached; return daily_dd_limit_reached;
} }
// Reduces lot size as account apporchaes max allowed drawdown limit. // Reduces lot size as account apporchaes max allowed drawdown limit.
double DrawdownControl::lot_correction_factor(double acc_equity_start, double min_lot_factor, double max_lot_factor, bool dynm_lot_factor=false, double dlf_trail_per=20) { double DrawdownControl::lot_correction_factor(double acc_equity_start, double min_lot_factor, double max_lot_factor, bool dynm_lot_factor=false, double dlf_trail_per=20) {
double account_value = fmin(AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE)); double account_value = fmin(AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE));
double lot_factor; double lot_factor;
// Interpolate to find lot factor between given min and max values. // Interpolate to find lot factor between given min and max values.
if (account_value < acc_equity_start){ if (account_value < acc_equity_start){
double acc_equity_min = acc_equity_start - (acc_equity_start * (acc_max_dd_per / 100)); double acc_equity_min = acc_equity_start - (acc_equity_start * (acc_max_dd_per / 100));
double y1 = min_lot_factor; double y1 = min_lot_factor;
double y2 = max_lot_factor; double y2 = max_lot_factor;
double x1 = acc_equity_min; double x1 = acc_equity_min;
double x = account_value; double x = account_value;
double x2 = acc_equity_start; double x2 = acc_equity_start;
lot_factor = y1 + (x - x1) * ((y2 - y1) / (x2 - x1)); lot_factor = y1 + (x - x1) * ((y2 - y1) / (x2 - x1));
} }
else if(account_value >= acc_equity_start) { else if(account_value >= acc_equity_start) {
if(dynm_lot_factor=true){ if(dynm_lot_factor=true){
lot_factor = lot_correction_dynamic(dlf_trail_per, min_lot_factor, max_lot_factor); lot_factor = lot_correction_dynamic(dlf_trail_per, min_lot_factor, max_lot_factor);
} }
else { else {
lot_factor = max_lot_factor; lot_factor = max_lot_factor;
} }
} }
return max_lot_factor; return max_lot_factor;
} }
double DrawdownControl::lot_correction_dynamic(double acc_dd_percent, double min_lot_factor, double max_lot_factor) { double DrawdownControl::lot_correction_dynamic(double acc_dd_percent, double min_lot_factor, double max_lot_factor) {
double account_value = fmin(AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE)); double account_value = fmin(AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE));
double trail_point = account_value - (account_value * (acc_dd_percent / 100)); double trail_point = account_value - (account_value * (acc_dd_percent / 100));
if(equaty_control_low < trail_point){ if(equaty_control_low < trail_point){
equaty_control_low = trail_point; equaty_control_low = trail_point;
} }
if(equaty_control_high < account_value){ if(equaty_control_high < account_value){
equaty_control_high = account_value; equaty_control_high = account_value;
} }
if(account_value < equaty_control_low){ if(account_value < equaty_control_low){
equaty_control_low = account_value; equaty_control_low = account_value;
equaty_control_high = account_value + (account_value * (acc_dd_percent / 100)); equaty_control_high = account_value + (account_value * (acc_dd_percent / 100));
} }
// back-up to file every hour: // back-up to file every hour:
if(mf.is_new_bar(_Symbol, PERIOD_H1) == true){ if(bar_utils.is_new_bar(_Symbol, PERIOD_H1) == true){
write_global_var_data(); write_global_var_data();
} }
// Linear interpolation: // Linear interpolation:
double y1 = min_lot_factor; double y1 = min_lot_factor;
double y2 = max_lot_factor; double y2 = max_lot_factor;
double x1 = equaty_control_low; double x1 = equaty_control_low;
double x = account_value; double x = account_value;
double x2 = equaty_control_high; double x2 = equaty_control_high;
double y = y1 + (x - x1) * ((y2 - y1) / (x2 - x1)); double y = y1 + (x - x1) * ((y2 - y1) / (x2 - x1));
return y; return y;
} }
bool DrawdownControl::write_global_var_data() { bool DrawdownControl::write_global_var_data() {
int file_handle = FileOpen(data_file, FILE_WRITE | FILE_ANSI | FILE_TXT); int file_handle = FileOpen(data_file, FILE_WRITE | FILE_ANSI | FILE_TXT);
FileWrite(file_handle, daily_equity_start); FileWrite(file_handle, daily_equity_start);
FileWrite(file_handle, daily_max_dd_target); FileWrite(file_handle, daily_max_dd_target);
FileWrite(file_handle, daily_dd_limit_reached); FileWrite(file_handle, daily_dd_limit_reached);
FileClose(file_handle); FileClose(file_handle);
Print(data_file, " written"); Print(data_file, " written");
return true; return true;
} }
bool DrawdownControl::print_messages() { bool DrawdownControl::print_messages() {
if(print_statments == true) { if(print_statments == true) {
Print("TimeCurrent(): ", TimeToString(TimeCurrent())); Print("TimeCurrent(): ", TimeToString(TimeCurrent()));
Print("Daily Equity Start: ", (int)daily_equity_start); Print("Daily Equity Start: ", (int)daily_equity_start);
Print("Current Equity: ", (int)AccountInfoDouble(ACCOUNT_EQUITY)); Print("Current Equity: ", (int)AccountInfoDouble(ACCOUNT_EQUITY));
Print("Daily Drawdown Limit: ", (int)daily_max_dd_target, " (", daily_max_dd_per, "%) of DES"); Print("Daily Drawdown Limit: ", (int)daily_max_dd_target, " (", daily_max_dd_per, "%) of DES");
Print("Daily Drawdown Limit Hit: ", daily_dd_limit_reached); Print("Daily Drawdown Limit Hit: ", daily_dd_limit_reached);
} }
return true; return true;
} }
+111
View File
@@ -0,0 +1,111 @@
class EntryState {
public:
int last_trigger_bar_long;
int last_trigger_bar_short;
int last_bl_cross_long;
int last_bl_cross_short;
int last_entry_long;
int last_entry_short;
// Constructor
EntryState() {
reset();
}
void reset() {
last_trigger_bar_long = -1000;
last_trigger_bar_short = -1000;
last_bl_cross_long = -1000;
last_bl_cross_short = -1000;
last_entry_long = -1000;
last_entry_short = -1000;
}
void update_trigger(bool trig_long, bool trig_short, int curr_bar) {
if (trig_long) last_trigger_bar_long = curr_bar;
if (trig_short) last_trigger_bar_short = curr_bar;
}
void update_baseline_cross(int curr_bar, double price, double baseline, double prev_price, double prev_baseline) {
if (prev_price < prev_baseline && price > baseline)
last_bl_cross_long = curr_bar;
if (prev_price > prev_baseline && price < baseline)
last_bl_cross_short = curr_bar;
}
void update_entry(bool is_long, int curr_bar) {
if (is_long)
last_entry_long = curr_bar;
else
last_entry_short = curr_bar;
}
int get_last_trigger(bool is_long) {
return is_long ? last_trigger_bar_long : last_trigger_bar_short;
}
int get_last_cross(bool is_long) {
return is_long ? last_bl_cross_long : last_bl_cross_short;
}
int get_last_entry(bool is_long) {
return is_long ? last_entry_long : last_entry_short;
}
};
// Snapshot of conditions for a potential entry signal
struct EntryContext {
bool trigger;
bool confirm;
bool volume;
bool recent;
bool base_ok;
bool near;
bool far;
int last_entry;
int last_cross;
};
// Utility: check if signal occurred recently
bool is_recent(int signal_bar, int curr_bar, int lookback) {
return (curr_bar - signal_bar) < lookback;
}
// Utility: build current entry condition context
EntryContext build_entry_context(bool is_long, double price, double baseline, double atr,
int last_cross, int last_entry,
bool trigger, bool confirm, bool volume, bool recent) {
EntryContext ctx;
ctx.trigger = trigger;
ctx.confirm = confirm;
ctx.volume = volume;
ctx.recent = recent;
ctx.base_ok = is_long ? (price > baseline) : (price < baseline);
ctx.near = MathAbs(price - baseline) <= atr;
ctx.far = MathAbs(price - baseline) > atr;
ctx.last_entry = last_entry;
ctx.last_cross = last_cross;
return ctx;
}
// Class wrapper for entry logic
class EntryLogic {
public:
bool is_standard_entry(const EntryContext &ctx) {
return ctx.trigger && ctx.confirm && ctx.volume && ctx.recent && ctx.base_ok && ctx.near;
}
bool is_pullback_entry(const EntryContext &ctx, int curr_bar) {
return (curr_bar - ctx.last_cross <= 2) && ctx.confirm && ctx.volume && ctx.base_ok && ctx.far;
}
bool is_baseline_cross_entry(const EntryContext &ctx, double prev_price, double prev_baseline,double price, double baseline, bool is_long){
bool crossed = is_long ? (prev_price < prev_baseline && price > baseline): (prev_price > prev_baseline && price < baseline);
return crossed && ctx.confirm && ctx.volume && ctx.near;
}
bool is_continuation_entry(const EntryContext &ctx, int curr_bar, int look_back) {
return (curr_bar - ctx.last_entry <= look_back) && ctx.last_entry > ctx.last_cross && ctx.trigger && ctx.confirm && ctx.base_ok;
}
};
@@ -1,414 +1,414 @@
#property library #property library
#include <Trade/Trade.mqh> #include <Trade/Trade.mqh>
#include <MyLibs/TimeZones.mqh> #include <MyLibs/Utils/TimeZones.mqh>
class RangeCalculator : public CObject{ class RangeCalculator : public CObject{
protected: protected:
TimeZones tz; TimeZones tz;
bool days_initlised; bool days_initlised;
bool range_initlised; bool range_initlised;
string symbol; string symbol;
ENUM_TIMEFRAMES calc_period; ENUM_TIMEFRAMES calc_period;
string inp_r_start_string; string inp_r_start_string;
int r_duration; int r_duration;
int r_expire; int r_expire;
int r_close; int r_close;
string inp_timezone; string inp_timezone;
bool sun; bool sun;
bool mon; bool mon;
bool tue; bool tue;
bool wed; bool wed;
bool thu; bool thu;
bool fri; bool fri;
bool sat; bool sat;
bool plot_range; bool plot_range;
datetime start_time; // Start of the range datetime start_time; // Start of the range
datetime end_time; // end of the range datetime end_time; // end of the range
datetime order_expire_time; // end of the range datetime order_expire_time; // end of the range
datetime close_time; // Close time datetime close_time; // Close time
double high; // high of the range double high; // high of the range
double low; // low of the range double low; // low of the range
double mid; // mid of the range double mid; // mid of the range
bool f_entry; // flag if we are inside of the range bool f_entry; // flag if we are inside of the range
bool f_high_breakout; // flag if a high breakout occurred bool f_high_breakout; // flag if a high breakout occurred
bool f_low_breakout; // flag if a low breakout occurred bool f_low_breakout; // flag if a low breakout occurred
bool above_last; bool above_last;
bool above_current; bool above_current;
bool below_last; bool below_last;
bool below_current; bool below_current;
// private functions // private functions
void update_objects(); void update_objects();
void draw_objects(); void draw_objects();
void define_new_range(); void define_new_range();
bool convert_input_time_strings(string t1, string t2, string t3, string t4); bool convert_input_time_strings(string t1, string t2, string t3, string t4);
public: public:
void calculate_range(); void calculate_range();
double get_range_high(); double get_range_high();
double get_range_low(); double get_range_low();
double get_range_mid(); double get_range_mid();
datetime get_range_start(); datetime get_range_start();
datetime get_range_end(); datetime get_range_end();
datetime get_order_expire_time(); datetime get_order_expire_time();
datetime get_range_close(); datetime get_range_close();
bool get_range_high_breakout(); bool get_range_high_breakout();
bool get_range_low_breakout(); bool get_range_low_breakout();
bool initilise_range(string inp_symbol, ENUM_TIMEFRAMES _calc_period, string t0, string t1, string t2, string t3, string time_zone, bool plot_range_inp); bool initilise_range(string inp_symbol, ENUM_TIMEFRAMES _calc_period, string t0, string t1, string t2, string t3, string time_zone, bool plot_range_inp);
void range_days(bool _inp_sun, bool _inp_mon, bool _inp_tue, bool _inp_wed, bool _inp_thu, bool _inp_fri, bool _inp_sat); void range_days(bool _inp_sun, bool _inp_mon, bool _inp_tue, bool _inp_wed, bool _inp_thu, bool _inp_fri, bool _inp_sat);
}; };
void RangeCalculator::range_days(bool _inp_sun, bool _inp_mon, bool _inp_tue, bool _inp_wed, bool _inp_thu, bool _inp_fri, bool _inp_sat){ void RangeCalculator::range_days(bool _inp_sun, bool _inp_mon, bool _inp_tue, bool _inp_wed, bool _inp_thu, bool _inp_fri, bool _inp_sat){
sun = _inp_sun; sun = _inp_sun;
mon = _inp_mon; mon = _inp_mon;
tue = _inp_tue; tue = _inp_tue;
wed = _inp_wed; wed = _inp_wed;
thu = _inp_thu; thu = _inp_thu;
fri = _inp_fri; fri = _inp_fri;
sat = _inp_sat; sat = _inp_sat;
days_initlised = true; days_initlised = true;
} }
bool RangeCalculator::initilise_range(string inp_symbol, ENUM_TIMEFRAMES _calc_period, string t1, string t2, string t3, string t4, string time_zone, bool plot_range_inp){ bool RangeCalculator::initilise_range(string inp_symbol, ENUM_TIMEFRAMES _calc_period, string t1, string t2, string t3, string t4, string time_zone, bool plot_range_inp){
inp_r_start_string = t1; inp_r_start_string = t1;
inp_timezone = time_zone; inp_timezone = time_zone;
symbol = inp_symbol; symbol = inp_symbol;
calc_period =_calc_period; calc_period =_calc_period;
plot_range = plot_range_inp; plot_range = plot_range_inp;
start_time = 0; start_time = 0;
end_time = 0; end_time = 0;
close_time = 0; close_time = 0;
high = 0; high = 0;
low = DBL_MAX; low = DBL_MAX;
mid = 0; mid = 0;
f_entry = false; f_entry = false;
f_high_breakout = false; f_high_breakout = false;
f_low_breakout = false; f_low_breakout = false;
above_last = false; above_last = false;
above_current= false; above_current= false;
below_last= false; below_last= false;
below_current= false; below_current= false;
if(!days_initlised){ if(!days_initlised){
sun = true; sun = true;
mon = true; mon = true;
tue = true; tue = true;
wed = true; wed = true;
thu = true; thu = true;
fri = true; fri = true;
sat = true; sat = true;
} }
range_initlised = true; range_initlised = true;
bool corret_inputs = convert_input_time_strings(t1, t2, t3, t4); bool corret_inputs = convert_input_time_strings(t1, t2, t3, t4);
if(corret_inputs = false){ if(corret_inputs = false){
return false; return false;
} }
return true; return true;
} }
bool RangeCalculator::convert_input_time_strings(string t1, string t2, string t3, string t4){ bool RangeCalculator::convert_input_time_strings(string t1, string t2, string t3, string t4){
datetime _t1 = StringToTime(t1); datetime _t1 = StringToTime(t1);
datetime _t2 = StringToTime(t2); datetime _t2 = StringToTime(t2);
datetime _t3 = StringToTime(t3); datetime _t3 = StringToTime(t3);
datetime _t4 = StringToTime(t4); datetime _t4 = StringToTime(t4);
if(_t1 > _t2){ if(_t1 > _t2){
_t2 = _t2 + PeriodSeconds(PERIOD_D1); _t2 = _t2 + PeriodSeconds(PERIOD_D1);
_t3 = _t3 + PeriodSeconds(PERIOD_D1); _t3 = _t3 + PeriodSeconds(PERIOD_D1);
_t4 = _t4 + PeriodSeconds(PERIOD_D1); _t4 = _t4 + PeriodSeconds(PERIOD_D1);
} }
if(_t2 > _t3){ if(_t2 > _t3){
_t3 = _t3 + PeriodSeconds(PERIOD_D1); _t3 = _t3 + PeriodSeconds(PERIOD_D1);
_t4 = _t4 + PeriodSeconds(PERIOD_D1); _t4 = _t4 + PeriodSeconds(PERIOD_D1);
} }
if(_t3 > _t4){ if(_t3 > _t4){
_t4 = _t4 + PeriodSeconds(PERIOD_D1); _t4 = _t4 + PeriodSeconds(PERIOD_D1);
} }
r_duration = (int)(_t2 - _t1); r_duration = (int)(_t2 - _t1);
r_expire = (int)(_t3 - _t1); r_expire = (int)(_t3 - _t1);
r_close = (int)(_t4 - _t1); r_close = (int)(_t4 - _t1);
if(_t4 - _t1 >= PeriodSeconds(PERIOD_D1)){ if(_t4 - _t1 >= PeriodSeconds(PERIOD_D1)){
Alert("INCORRECT RANGE INPUTS!"); Alert("INCORRECT RANGE INPUTS!");
return false; return false;
} }
return true; return true;
} }
// high of the range // high of the range
double RangeCalculator::get_range_high(){ double RangeCalculator::get_range_high(){
return high; return high;
}; };
// low of the range // low of the range
double RangeCalculator::get_range_low(){ double RangeCalculator::get_range_low(){
return low; return low;
}; };
// mid of the range // mid of the range
double RangeCalculator::get_range_mid(){ double RangeCalculator::get_range_mid(){
return mid; return mid;
}; };
datetime RangeCalculator::get_range_start(){ datetime RangeCalculator::get_range_start(){
return start_time; return start_time;
}; };
datetime RangeCalculator::get_range_end(){ datetime RangeCalculator::get_range_end(){
return end_time; return end_time;
}; };
datetime RangeCalculator::get_order_expire_time(){ datetime RangeCalculator::get_order_expire_time(){
return order_expire_time; return order_expire_time;
}; };
datetime RangeCalculator::get_range_close(){ datetime RangeCalculator::get_range_close(){
return close_time; return close_time;
}; };
// flag if a high breakout occurred // flag if a high breakout occurred
bool RangeCalculator::get_range_high_breakout(){ bool RangeCalculator::get_range_high_breakout(){
return f_high_breakout; return f_high_breakout;
}; };
// flag if a low breakout occurred // flag if a low breakout occurred
bool RangeCalculator::get_range_low_breakout(){ bool RangeCalculator::get_range_low_breakout(){
return f_low_breakout; return f_low_breakout;
}; };
void RangeCalculator::calculate_range(){ void RangeCalculator::calculate_range(){
f_high_breakout = false; f_high_breakout = false;
f_low_breakout = false; f_low_breakout = false;
double last_bar_high = iHigh(symbol, calc_period, 1); // shift 1 because 0 = live candle: double last_bar_high = iHigh(symbol, calc_period, 1); // shift 1 because 0 = live candle:
double last_bar_low = iLow(symbol, calc_period, 1); // shift 1 because 0 = live candle: double last_bar_low = iLow(symbol, calc_period, 1); // shift 1 because 0 = live candle:
// range calculation // range calculation
if(TimeCurrent() >= start_time && TimeCurrent() <= end_time){ if(TimeCurrent() >= start_time && TimeCurrent() <= end_time){
// set flag // set flag
f_entry = true; f_entry = true;
// new high // new high
if(last_bar_high > high){ if(last_bar_high > high){
high = last_bar_high; high = last_bar_high;
mid = (high + low)/2; mid = (high + low)/2;
if(plot_range){ if(plot_range){
update_objects(); update_objects();
} }
} }
// new low // new low
if(last_bar_low < low){ if(last_bar_low < low){
low = last_bar_low; low = last_bar_low;
mid = (high + low)/2; mid = (high + low)/2;
if(plot_range){ if(plot_range){
update_objects(); update_objects();
} }
} }
} }
// calculate new reange if // calculate new reange if
if( (TimeCurrent() >= close_time) // close time reached if( (TimeCurrent() >= close_time) // close time reached
|| (end_time == 0) // range not calculated yet || (end_time == 0) // range not calculated yet
|| (end_time !=0 && TimeCurrent() > end_time && !f_entry) // there was a range calculated but no tick inside. || (end_time !=0 && TimeCurrent() > end_time && !f_entry) // there was a range calculated but no tick inside.
){ ){
define_new_range(); define_new_range();
} }
// check if we are after the range end // check if we are after the range end
if(TimeCurrent() >= end_time && end_time > 0 && f_entry){ if(TimeCurrent() >= end_time && end_time > 0 && f_entry){
if(!f_high_breakout && last_bar_high >= high){ if(!f_high_breakout && last_bar_high >= high){
above_last = above_current; above_last = above_current;
above_current= true; above_current= true;
if(above_last==false && above_current == true){ if(above_last==false && above_current == true){
f_high_breakout = true; f_high_breakout = true;
} }
else(f_high_breakout = false); else(f_high_breakout = false);
} }
if(!f_low_breakout && last_bar_low >= low){ if(!f_low_breakout && last_bar_low >= low){
below_last = below_current; below_last = below_current;
below_current = true; below_current = true;
if(below_last == false && below_current == true){ if(below_last == false && below_current == true){
f_low_breakout = true; f_low_breakout = true;
} }
else(f_low_breakout = false); else(f_low_breakout = false);
} }
} }
} }
void RangeCalculator::define_new_range(){ void RangeCalculator::define_new_range(){
// reset range vars // reset range vars
start_time = 0; start_time = 0;
end_time = 0; end_time = 0;
order_expire_time = 0; order_expire_time = 0;
close_time = 0; close_time = 0;
high = 0; high = 0;
low = INT_MAX; low = INT_MAX;
mid = 0; mid = 0;
f_entry = false; f_entry = false;
// calculate range start time: // calculate range start time:
datetime r_st = StringToTime(inp_r_start_string); datetime r_st = StringToTime(inp_r_start_string);
start_time = tz.timezone_conversions(inp_timezone, r_st, "Broker"); start_time = tz.timezone_conversions(inp_timezone, r_st, "Broker");
for(int i=0; i<8; i++){ for(int i=0; i<8; i++){
MqlDateTime tmp; MqlDateTime tmp;
TimeToStruct(start_time,tmp); TimeToStruct(start_time,tmp);
int dow = tmp.day_of_week; int dow = tmp.day_of_week;
if(TimeCurrent()>=start_time if(TimeCurrent()>=start_time
|| (dow==0 && !sun) || (dow==0 && !sun)
|| (dow==1 && !mon) || (dow==1 && !mon)
|| (dow==2 && !tue) || (dow==2 && !tue)
|| (dow==3 && !wed) || (dow==3 && !wed)
|| (dow==4 && !thu) || (dow==4 && !thu)
|| (dow==5 && !fri) || (dow==5 && !fri)
|| (dow==6 && !sat) || (dow==6 && !sat)
){ ){
start_time += PeriodSeconds(PERIOD_D1); start_time += PeriodSeconds(PERIOD_D1);
} }
} }
end_time = start_time + r_duration; end_time = start_time + r_duration;
order_expire_time = start_time + r_expire; order_expire_time = start_time + r_expire;
close_time = start_time + r_close; close_time = start_time + r_close;
if(plot_range){ if(plot_range){
draw_objects(); draw_objects();
} }
} }
void RangeCalculator::update_objects(){ void RangeCalculator::update_objects(){
string name = "Range Mid " + (string)start_time; string name = "Range Mid " + (string)start_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, mid); ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, mid);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, mid); ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, mid);
// ObjectSetString(NULL, name , OBJPROP_TOOLTIP, "Range Mid"); // ObjectSetString(NULL, name , OBJPROP_TOOLTIP, "Range Mid");
name = "Order expire " + (string)order_expire_time; name = "Order expire " + (string)order_expire_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high); ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low); ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
name = "Range start " + (string)start_time; name = "Range start " + (string)start_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high); ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low); ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
name = "Range end " + (string)end_time; name = "Range end " + (string)end_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high); ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low); ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
datetime rct = r_close>=0 ? close_time : INT_MAX; datetime rct = r_close>=0 ? close_time : INT_MAX;
name = "Range close " + (string)rct; name = "Range close " + (string)rct;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high); ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low); ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
name = "Range High " + (string)rct; name = "Range High " + (string)rct;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high); ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, high); ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, high);
name = "Range Low " + (string)rct; name = "Range Low " + (string)rct;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, low); ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, low);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low); ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
name = "range box "+ (string)start_time; name = "range box "+ (string)start_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high); ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low); ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
ObjectSetDouble(NULL, name +" ", OBJPROP_PRICE,0, high); ObjectSetDouble(NULL, name +" ", OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name +" ", OBJPROP_PRICE,1, low); ObjectSetDouble(NULL, name +" ", OBJPROP_PRICE,1, low);
} }
void RangeCalculator::draw_objects(){ void RangeCalculator::draw_objects(){
datetime rct = r_close>=0 ? close_time : INT_MAX; datetime rct = r_close>=0 ? close_time : INT_MAX;
// Range mid line // Range mid line
string name = "Range Mid " + (string)start_time;; string name = "Range Mid " + (string)start_time;;
ObjectCreate(NULL, name, OBJ_TREND, 0, start_time, mid, rct, mid); ObjectCreate(NULL, name, OBJ_TREND, 0, start_time, mid, rct, mid);
ObjectSetString(NULL, name , OBJPROP_TOOLTIP, "Range Mid" + (string)mid); ObjectSetString(NULL, name , OBJPROP_TOOLTIP, "Range Mid" + (string)mid);
ObjectSetInteger(NULL, name, OBJPROP_COLOR, clrGray); ObjectSetInteger(NULL, name, OBJPROP_COLOR, clrGray);
ObjectSetInteger(NULL, name, OBJPROP_WIDTH, 1); ObjectSetInteger(NULL, name, OBJPROP_WIDTH, 1);
ObjectSetInteger(NULL, name, OBJPROP_STYLE, STYLE_DOT); ObjectSetInteger(NULL, name, OBJPROP_STYLE, STYLE_DOT);
// order lines // order lines
string name2 = "Order expire " + (string)order_expire_time; string name2 = "Order expire " + (string)order_expire_time;
ObjectCreate(NULL, name2, OBJ_TREND, 0, order_expire_time, low, order_expire_time, high); ObjectCreate(NULL, name2, OBJ_TREND, 0, order_expire_time, low, order_expire_time, high);
ObjectSetString(NULL, name2, OBJPROP_TOOLTIP, "start of the range \n" + TimeToString(order_expire_time,TIME_DATE|TIME_MINUTES)); ObjectSetString(NULL, name2, OBJPROP_TOOLTIP, "start of the range \n" + TimeToString(order_expire_time,TIME_DATE|TIME_MINUTES));
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, C'139,41,41'); ObjectSetInteger(NULL, name2, OBJPROP_COLOR, C'139,41,41');
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2); ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true); ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range start " + (string)start_time; name2 = "Range start " + (string)start_time;
ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, low, start_time, high); ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, low, start_time, high);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack); ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2); ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true); ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range end " + (string)end_time; name2 = "Range end " + (string)end_time;
ObjectCreate(NULL, name2, OBJ_TREND, 0, end_time, low, end_time, high); ObjectCreate(NULL, name2, OBJ_TREND, 0, end_time, low, end_time, high);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack); ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2); ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true); ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range close " + (string)rct; name2 = "Range close " + (string)rct;
ObjectCreate(NULL, name2, OBJ_TREND, 0, rct, low, rct, high); ObjectCreate(NULL, name2, OBJ_TREND, 0, rct, low, rct, high);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack); ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2); ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true); ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range High " + (string)rct; name2 = "Range High " + (string)rct;
ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, high, rct, high); ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, high, rct, high);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack); ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2); ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true); ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range Low " + (string)rct; name2 = "Range Low " + (string)rct;
ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, low, rct, low); ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, low, rct, low);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack); ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2); ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2 ,OBJPROP_BACK, true); ObjectSetInteger(NULL, name2 ,OBJPROP_BACK, true);
// Box // Box
name = "range box " + (string)start_time; name = "range box " + (string)start_time;
ObjectCreate(NULL, name, OBJ_RECTANGLE, 0, start_time, high, end_time, low); ObjectCreate(NULL, name, OBJ_RECTANGLE, 0, start_time, high, end_time, low);
ObjectSetString(NULL,name,OBJPROP_TOOLTIP,"\n"); ObjectSetString(NULL,name,OBJPROP_TOOLTIP,"\n");
ObjectSetInteger(NULL, name,OBJPROP_COLOR, C'128,177,173'); ObjectSetInteger(NULL, name,OBJPROP_COLOR, C'128,177,173');
ObjectSetInteger(NULL, name,OBJPROP_FILL, true); ObjectSetInteger(NULL, name,OBJPROP_FILL, true);
ObjectSetInteger(NULL, name,OBJPROP_BACK, true); ObjectSetInteger(NULL, name,OBJPROP_BACK, true);
ObjectCreate(NULL, name + " ", OBJ_RECTANGLE, 0, end_time, high, rct, low); ObjectCreate(NULL, name + " ", OBJ_RECTANGLE, 0, end_time, high, rct, low);
ObjectSetString(NULL, name+ " ", OBJPROP_TOOLTIP, "\n"); ObjectSetString(NULL, name+ " ", OBJPROP_TOOLTIP, "\n");
ObjectSetInteger(NULL, name + " ",OBJPROP_FILL, true); ObjectSetInteger(NULL, name + " ",OBJPROP_FILL, true);
ObjectSetInteger(NULL, name + " ",OBJPROP_COLOR, C'165,220,215' ); ObjectSetInteger(NULL, name + " ",OBJPROP_COLOR, C'165,220,215' );
ObjectSetInteger(NULL, name + " ",OBJPROP_BACK, true); ObjectSetInteger(NULL, name + " ",OBJPROP_BACK, true);
ChartRedraw(); ChartRedraw();
} }
+31
View File
@@ -0,0 +1,31 @@
#include <Object.mqh>
class ChartUtils : public CObject {
public:
void draw_line(double value, string name, color clr = clrBlack);
};
void ChartUtils::draw_line(double value, string name, color clr) {
if (ObjectFind(0, name) < 0) {
ResetLastError();
if (!ObjectCreate(0, name, OBJ_HLINE, 0, 0, value)) {
Print(__FUNCTION__, ": failed to create a horizontal line! Error code = ", GetLastError());
return;
}
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
}
ResetLastError();
if (!ObjectMove(0, name, 0, 0, value)) {
Print(__FUNCTION__, ": failed to move the horizontal line! Error code = ", GetLastError());
return;
}
ChartRedraw();
}
+41 -53
View File
@@ -1,53 +1,41 @@
#property library enum LOT_MODE{
LOT_MODE_FIXED, // Fixed Lot Size
enum LOT_MODE{ LOT_MODE_PCT_ACCOUNT, // Percent of Account (fixed)
LOT_MODE_FIXED, // Fixed Lot Size LOT_MODE_PCT_RISK // Percent of Account at Risk (from SL)
LOT_MODE_PCT_ACCOUNT, // Percent of Account (fixed) };
LOT_MODE_PCT_RISK // Percent of Account at Risk (from SL) enum SL_MODE{
}; SL_FIXED_PIPS, // Fixed Pips
enum SL_MODE{ SL_FIXED_PERCENT, // Fixed Percent
SL_FIXED_PIPS, // Fixed Pips SL_ATR_MULTIPLE, // ATR Multiple
SL_FIXED_PERCENT, // Fixed Percent SL_SPECIFIED_VALUE, // Bespoke calculation in code
SL_ATR_MULTIPLE, // ATR Multiple NO_STOPLOSS, // No Stop-loss
SL_SPECIFIED_VALUE, // Bespoke calculation in code SL_BREAKEVEN, // Breakeven
NO_STOPLOSS, // No Stop-loss };
SL_BREAKEVEN, // Breakeven enum TP_MODE{
}; TP_FIXED_PIPS, // Fixed Pips
enum TP_MODE{ TP_FIXED_PERCENT, // Fixed Percent
TP_FIXED_PIPS, // Fixed Pips TP_ATR_MULTIPLE, // ATR Multiple
TP_FIXED_PERCENT, // Fixed Percent TP_SL_MULTIPLE, // Multiple of Risk (from sl)
TP_ATR_MULTIPLE, // ATR Multiple TP_SPECIFIED_VALUE, // Bespoke calculation in code
TP_SL_MULTIPLE, // Multiple of Risk (from sl) NO_TAKE_PROFIT, // No Take-Profit
TP_SPECIFIED_VALUE, // Bespoke calculation in code };
NO_TAKE_PROFIT, // No Take-Profit
}; enum TSL_MODE{
TSL_ATR_MULTIPLE, // ATR Multiple
enum TSL_MODE{ TSL_FIXED_PIPS, // Fixed Pips
TSL_ATR_MULTIPLE, // ATR Multiple TSL_FIXED_PERCENT, // Fixed Percent
TSL_FIXED_PIPS, // Fixed Pips };
TSL_FIXED_PERCENT, // Fixed Percent
}; enum TIME_ZONES{
NY, // New York
enum TIME_ZONES{ Lon, // London
NY, // New York Ffm, // Frankfurt
Lon, // London Syd, // Sidney
Ffm, // Frankfurt Mosc, // Moscow
Syd, // Sidney Tok, // Tokyo - no DST
Mosc, // Moscow };
Tok, // Tokyo - no DST enum MULTI_SYM_MODE{
}; MULTI_SYM_CHART, // Chart Symbol only
enum MULTI_SYM_MODE{ MULTI_SYM_FX_B5, // FX Benchmark 5
MULTI_SYM_CHART, // Chart Symbol only MULTI_SYM_FX_28 // FX 28 Majors
MULTI_SYM_FX_B5, // FX Benchmark 5 };
MULTI_SYM_FX_28 // FX 28 Majors
};
// used to generate in and out of sample data sets
enum MODE_SPLIT_DATA{
NO_SPLIT,
ODD_YEARS,
EVEN_YEARS,
ODD_MONTHS,
EVEN_MONTHS,
ODD_WEEKS,
EVEN_WEEKS
};
@@ -1,28 +1,28 @@
#include <MyLibs/Myfunctions.mqh> #include <MyLibs/Myfunctions.mqh>
#include <MyLibs/OrderManagement.mqh> #include <MyLibs/OrderManagement.mqh>
#include <MyLibs/MyEnums.mqh> #include <MyLibs/Utils/MyEnums.mqh>
#include <MyLibs/CustomMax.mqh> #include <MyLibs/BacktestUtils/CustomMax.mqh>
CustomMax cm; #include <MyLibs/BacktestUtils/TestDataSplit.mqh>
MyFunctions mf; CustomMax c_max;
OrderManagment om; // MyFunctions mf;
//--- // OrderManagment om;
string SymbolsArray[]; //---
input LOT_MODE inp_lot_mode = LOT_MODE_PCT_RISK; // Lot Size Mode input LOT_MODE inp_lot_mode = LOT_MODE_PCT_RISK; // Lot Size Mode
input double inp_lot_var = 2; // Lot Size Var input double inp_lot_var = 2; // Lot Size Var
input SL_MODE inp_sl_mode = SL_ATR_MULTIPLE; // Stop-loss Mode input SL_MODE inp_sl_mode = SL_ATR_MULTIPLE; // Stop-loss Mode
input double inp_sl_var = 1.5; // Stop-loss Var input double inp_sl_var = 1.5; // Stop-loss Var
input TP_MODE inp_tp_mode = TP_ATR_MULTIPLE; // Take-profit Mode input TP_MODE inp_tp_mode = TP_ATR_MULTIPLE; // Take-profit Mode
input double inp_tp_var = 1; // Take-Profit Var input double inp_tp_var = 1; // Take-Profit Var
string lot_mode = EnumToString(inp_lot_mode); string lot_mode = EnumToString(inp_lot_mode);
string sl_mode = EnumToString(inp_sl_mode); string sl_mode = EnumToString(inp_sl_mode);
string tp_mode = EnumToString(inp_tp_mode); string tp_mode = EnumToString(inp_tp_mode);
input CUSTOM_MAX_TYPE inp_custom_criteria = CM_WIN_PERCENT_200T; input CUSTOM_MAX_TYPE inp_custom_criteria = CM_WIN_PERCENT;
input MULTI_SYM_MODE inp_sym_mode = MULTI_SYM_FX_B5; input int inp_opt_min_trades = 0; // 0/off
input MODE_SPLIT_DATA inp_data_split_method = NO_SPLIT; input MODE_SPLIT_DATA inp_data_split_method = NO_SPLIT;
input int inp_force_opt = 1; input int inp_force_opt = 1;
input group "-----------------------------------------" input group "-----------------------------------------"
+74
View File
@@ -0,0 +1,74 @@
class MarketDataUtils {
public:
bool is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, string daily_start_time = "00:10");
double get_latest_buffer_value(int handle);
double get_buffer_value(int handle, int shift);
double adjusted_point(string symbol);
double get_bid_ask_price(string symbol, int price_side);
protected:
datetime previousTime; // Stores the last recorded bar open time
datetime bar_open_time; // Stores the current bar's open time
};
// Checks if a new bar has opened on the given timeframe and symbol
bool MarketDataUtils::is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, string daily_start_time) {
bar_open_time = iTime(symbol, time_frame, 0); // Current open time
if (previousTime != bar_open_time) {
// For daily timeframe, wait for specific time (e.g., 00:10) before triggering
if (PeriodSeconds(time_frame) == PeriodSeconds(PERIOD_D1)) {
if (TimeCurrent() > StringToTime(daily_start_time)) {
previousTime = bar_open_time;
return true;
}
} else {
previousTime = bar_open_time;
return true;
}
}
return false; // No new bar
}
// Retrieves the latest value from an indicator buffer (shift 0)
double MarketDataUtils::get_latest_buffer_value(int handle) {
double val[];
ArraySetAsSeries(val, true); // Aligns array with bar indexing (0 = latest)
if (CopyBuffer(handle, 0, 0, 1, val) == 1)
return val[0]; // Latest value at shift 0
return 0.0;
}
// Retrieves a historical buffer value at specified shift
double MarketDataUtils::get_buffer_value(int handle, int shift) {
double val[];
ArraySetAsSeries(val, true);
if (CopyBuffer(handle, 0, shift, 1, val) == 1)
return val[0]; // Historical value at given shift
return 0.0;
}
// Adjusts the point value for symbol to account for fractional pips (e.g., 5-digit brokers)
double MarketDataUtils::adjusted_point(string symbol) {
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
int digits_adjust = (symbol_digits == 3 || symbol_digits == 5) ? 10 : 1;
double point_val = SymbolInfoDouble(symbol, SYMBOL_POINT);
return point_val * digits_adjust; // Adjusted pip value
}
// Returns current Bid or Ask price for a symbol based on side (1 = Ask, 2 = Bid)
double MarketDataUtils::get_bid_ask_price(string symbol, int price_side) {
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double ask = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_ASK), digits);
double bid = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_BID), digits);
if (price_side == 1) return ask;
if (price_side == 2) return bid;
return 0.0; // Invalid input
}
+143 -143
View File
@@ -1,144 +1,144 @@
#property library #property library
#include <Trade/Trade.mqh> #include <Trade/Trade.mqh>
#include <MyLibs/DealingWithTime.mqh> #include <MyLibs/Utils/DealingWithTime.mqh>
class TimeZones: public CObject{ class TimeZones: public CObject{
protected: protected:
string dt_s; string dt_s;
int len; int len;
string dt_string; string dt_string;
datetime tC, tGMT, tNY, tLon, tFfm, tMosc, tSyd, tTok; datetime tC, tGMT, tNY, tLon, tFfm, tMosc, tSyd, tTok;
datetime tz_time; datetime tz_time;
string tz_date; string tz_date;
datetime time_start; datetime time_start;
datetime time_end; datetime time_end;
bool is_time; bool is_time;
datetime tGIVEN; datetime tGIVEN;
datetime tREQ; datetime tREQ;
datetime tzt; datetime tzt;
datetime tz_req; datetime tz_req;
double ny_daily_close_protected(string symbol, int shift_days, bool print_data=false); double ny_daily_close_protected(string symbol, int shift_days, bool print_data=false);
double required_close; double required_close;
public: public:
string get_date_string_from_datetime(datetime dt); string get_date_string_from_datetime(datetime dt);
datetime get_timezone_time(string time_zone, bool print_time); datetime get_timezone_time(string time_zone, bool print_time);
datetime timezone_conversions(string time_zone_known, datetime time_given, string time_zone_required); datetime timezone_conversions(string time_zone_known, datetime time_given, string time_zone_required);
double ny_daily_close(string symbol, int shift_days, bool print_data=false); double ny_daily_close(string symbol, int shift_days, bool print_data=false);
}; };
string TimeZones::get_date_string_from_datetime(datetime dt){ string TimeZones::get_date_string_from_datetime(datetime dt){
dt_s = TimeToString(dt); dt_s = TimeToString(dt);
len = StringLen(dt_s); len = StringLen(dt_s);
dt_string = StringSubstr(dt_s, 0, len-5); dt_string = StringSubstr(dt_s, 0, len-5);
return dt_string; return dt_string;
} }
datetime TimeZones::get_timezone_time(string time_zone, bool print_time){ datetime TimeZones::get_timezone_time(string time_zone, bool print_time){
// https://www.mql5.com/en/code/45287 // https://www.mql5.com/en/code/45287
// https://www.mql5.com/en/articles/9926 // https://www.mql5.com/en/articles/9926
// https://www.mql5.com/en/articles/9929 // https://www.mql5.com/en/articles/9929
checkTimeOffset(TimeCurrent()); // check changes of DST checkTimeOffset(TimeCurrent()); // check changes of DST
// cto(); // cto();
tC = TimeCurrent(); tC = TimeCurrent();
tGMT = TimeCurrent() + OffsetBroker.actOffset; // GMT tGMT = TimeCurrent() + OffsetBroker.actOffset; // GMT
tNY = tGMT - (NYShift+DST_USD); // time in New York (EST) tNY = tGMT - (NYShift+DST_USD); // time in New York (EST)
tLon = tGMT - (LondonShift+DST_EUR); // time in London tLon = tGMT - (LondonShift+DST_EUR); // time in London
tFfm = tGMT - (FfmShift+DST_EUR); // time in Frankfurt tFfm = tGMT - (FfmShift+DST_EUR); // time in Frankfurt
tSyd = tGMT - (SidneyShift+DST_AUD); // time in Sidney tSyd = tGMT - (SidneyShift+DST_AUD); // time in Sidney
tMosc = tGMT - (MoskwaShift+DST_RUS); // time in Moscow tMosc = tGMT - (MoskwaShift+DST_RUS); // time in Moscow
tTok = tGMT - (TokyoShift); // time in Tokyo - no DST tTok = tGMT - (TokyoShift); // time in Tokyo - no DST
if(print_time==true){ if(print_time==true){
Print("----------------------------------"); Print("----------------------------------");
Print("Broker: ", tC); Print("Broker: ", tC);
Print("GMT: ", tGMT); Print("GMT: ", tGMT);
Print("time in New York: ", tNY); Print("time in New York: ", tNY);
Print("time in London: ", tLon); Print("time in London: ", tLon);
Print("time in Frankfurt: ", tFfm); Print("time in Frankfurt: ", tFfm);
Print("time in Sidney: ", tSyd); Print("time in Sidney: ", tSyd);
Print("time in Moscow: ", tMosc); Print("time in Moscow: ", tMosc);
Print("time in Tokyo: ", tTok); Print("time in Tokyo: ", tTok);
} }
if(time_zone=="NY"){return tNY;} if(time_zone=="NY"){return tNY;}
if(time_zone=="Lon"){return tLon;} if(time_zone=="Lon"){return tLon;}
if(time_zone=="Ffm"){return tFfm;} if(time_zone=="Ffm"){return tFfm;}
if(time_zone=="Syd"){return tSyd;} if(time_zone=="Syd"){return tSyd;}
if(time_zone=="Mosc"){return tMosc;} if(time_zone=="Mosc"){return tMosc;}
if(time_zone=="Tok"){return tTok;} if(time_zone=="Tok"){return tTok;}
return NULL; return NULL;
} }
datetime TimeZones::timezone_conversions(string time_zone_known, datetime time_given, string time_zone_required){ datetime TimeZones::timezone_conversions(string time_zone_known, datetime time_given, string time_zone_required){
// https://www.mql5.com/en/code/45287 // https://www.mql5.com/en/code/45287
// https://www.mql5.com/en/articles/9926 // https://www.mql5.com/en/articles/9926
// https://www.mql5.com/en/articles/9929 // https://www.mql5.com/en/articles/9929
tGIVEN = time_given; //StringToTime(time_given); tGIVEN = time_given; //StringToTime(time_given);
checkTimeOffset(tGIVEN); // check changes of DST checkTimeOffset(tGIVEN); // check changes of DST
// Get GMT: // Get GMT:
if(time_zone_known=="GMT" ){tGMT = tGIVEN;} if(time_zone_known=="GMT" ){tGMT = tGIVEN;}
if(time_zone_known=="Broker" ){tGMT = tGIVEN + OffsetBroker.actOffset;} if(time_zone_known=="Broker" ){tGMT = tGIVEN + OffsetBroker.actOffset;}
if(time_zone_known=="NY" ){tGMT = tGIVEN + (NYShift+DST_USD);} if(time_zone_known=="NY" ){tGMT = tGIVEN + (NYShift+DST_USD);}
if(time_zone_known=="Lon" ){tGMT = tGIVEN + (LondonShift+DST_EUR);} if(time_zone_known=="Lon" ){tGMT = tGIVEN + (LondonShift+DST_EUR);}
if(time_zone_known=="Ffm" ){tGMT = tGIVEN + (FfmShift+DST_EUR);} if(time_zone_known=="Ffm" ){tGMT = tGIVEN + (FfmShift+DST_EUR);}
if(time_zone_known=="Syd" ){tGMT = tGIVEN + (SidneyShift+DST_AUD);} if(time_zone_known=="Syd" ){tGMT = tGIVEN + (SidneyShift+DST_AUD);}
if(time_zone_known=="Mosc" ){tGMT = tGIVEN + (MoskwaShift+DST_RUS);} if(time_zone_known=="Mosc" ){tGMT = tGIVEN + (MoskwaShift+DST_RUS);}
if(time_zone_known=="Tok" ){tGMT = tGIVEN + (TokyoShift);} if(time_zone_known=="Tok" ){tGMT = tGIVEN + (TokyoShift);}
// define the required time: // define the required time:
tREQ = NULL; tREQ = NULL;
if(time_zone_required=="GMT" ){tREQ = tGMT;} if(time_zone_required=="GMT" ){tREQ = tGMT;}
if(time_zone_required=="Broker" ){tREQ = tGMT - OffsetBroker.actOffset;} if(time_zone_required=="Broker" ){tREQ = tGMT - OffsetBroker.actOffset;}
if(time_zone_required=="NY" ){tREQ = tGMT - (NYShift+DST_USD);} if(time_zone_required=="NY" ){tREQ = tGMT - (NYShift+DST_USD);}
if(time_zone_required=="Lon" ){tREQ = tGMT - (LondonShift+DST_EUR);} if(time_zone_required=="Lon" ){tREQ = tGMT - (LondonShift+DST_EUR);}
if(time_zone_required=="Ffm" ){tREQ = tGMT - (FfmShift+DST_EUR);} if(time_zone_required=="Ffm" ){tREQ = tGMT - (FfmShift+DST_EUR);}
if(time_zone_required=="Syd" ){tREQ = tGMT - (SidneyShift+DST_AUD) ;} if(time_zone_required=="Syd" ){tREQ = tGMT - (SidneyShift+DST_AUD) ;}
if(time_zone_required=="Mosc" ){tREQ = tGMT - (MoskwaShift+DST_RUS);} if(time_zone_required=="Mosc" ){tREQ = tGMT - (MoskwaShift+DST_RUS);}
if(time_zone_required=="Tok" ){tREQ = tGMT - (TokyoShift);} if(time_zone_required=="Tok" ){tREQ = tGMT - (TokyoShift);}
return tREQ; return tREQ;
} }
// Calculte NY close time: // Calculte NY close time:
double TimeZones::ny_daily_close(string symbol, int shift_days, bool print_data=false){ double TimeZones::ny_daily_close(string symbol, int shift_days, bool print_data=false){
required_close = ny_daily_close_protected(symbol, shift_days, print_data); required_close = ny_daily_close_protected(symbol, shift_days, print_data);
return required_close; return required_close;
} }
double TimeZones::ny_daily_close_protected(string symbol, int shift_days, bool print_data=false){ double TimeZones::ny_daily_close_protected(string symbol, int shift_days, bool print_data=false){
// Get the brokers times for when NY openend today and tomorrow: // Get the brokers times for when NY openend today and tomorrow:
datetime time_5pm = iTime(symbol, PERIOD_D1 , 0) - (PeriodSeconds(PERIOD_H1) * 7); datetime time_5pm = iTime(symbol, PERIOD_D1 , 0) - (PeriodSeconds(PERIOD_H1) * 7);
datetime ny_close_in_brokers_time = timezone_conversions("NY", time_5pm, "Broker"); datetime ny_close_in_brokers_time = timezone_conversions("NY", time_5pm, "Broker");
datetime ny_close_time = ny_close_in_brokers_time + PeriodSeconds(PERIOD_D1); // ny close tomorrow datetime ny_close_time = ny_close_in_brokers_time + PeriodSeconds(PERIOD_D1); // ny close tomorrow
if(TimeCurrent()<ny_close_time){ if(TimeCurrent()<ny_close_time){
ny_close_time = ny_close_time - PeriodSeconds(PERIOD_D1); // ny close today ny_close_time = ny_close_time - PeriodSeconds(PERIOD_D1); // ny close today
} }
// Get the number of hours since NY closed: // Get the number of hours since NY closed:
int shift = iBarShift(symbol, PERIOD_H1, ny_close_time, false) + 1; int shift = iBarShift(symbol, PERIOD_H1, ny_close_time, false) + 1;
shift = shift + (24 * (shift_days - 1)); // shift days if required: shift = shift + (24 * (shift_days - 1)); // shift days if required:
double ny_close = iClose(symbol,PERIOD_H1, shift); double ny_close = iClose(symbol,PERIOD_H1, shift);
double br_close = iClose(symbol,PERIOD_H1, 1); double br_close = iClose(symbol,PERIOD_H1, 1);
if(print_data==true){ if(print_data==true){
Print("shift ",shift); Print("shift ",shift);
Print("time_5pm ",time_5pm); Print("time_5pm ",time_5pm);
Print("ny_close_in_brokers_time ",ny_close_in_brokers_time); Print("ny_close_in_brokers_time ",ny_close_in_brokers_time);
Print("ny_close_time ",ny_close_time); Print("ny_close_time ",ny_close_time);
Print("ny_close ", ny_close); Print("ny_close ", ny_close);
Print("br_close ",br_close); Print("br_close ",br_close);
} }
return ny_close; return ny_close;
} }
+67
View File
@@ -0,0 +1,67 @@
#include <MyLibs/Utils/TimeZones.mqh>
class TradeSessionUtils {
protected:
TimeZones tz; // For handling timezone conversion
bool in_window; // Whether the current time is in the allowed window
datetime start_time; // Session start time (converted to Broker time)
datetime end_time; // Session end time (converted to Broker time)
public:
bool trade_window(string t1, string t2, string time_zone = "Broker", bool plot_range_inp = true);
};
bool TradeSessionUtils::trade_window(string t1, string t2, string time_zone, bool plot_range_inp) {
datetime _t1 = StringToTime(t1); // Convert string to datetime
datetime _t2 = StringToTime(t2); // Convert string to datetime
// Handle overnight windows (e.g. 22:0001:00)
if (_t1 > _t2) {
_t2 = _t2 + PeriodSeconds(PERIOD_D1);
}
int w_duration = (int)(_t2 - _t1); // Duration of the session in seconds
// Check if we're currently within the window
if (TimeCurrent() >= start_time && TimeCurrent() <= end_time) {
in_window = true;
}
// If we've moved beyond the previous window, define a new one
if (TimeCurrent() >= end_time) {
in_window = false;
// Convert start time to broker time based on user timezone input
start_time = tz.timezone_conversions(time_zone, StringToTime(t1), "Broker");
// If we've already passed today's start time, push it to tomorrow
if (TimeCurrent() >= start_time) {
start_time += PeriodSeconds(PERIOD_D1);
}
// End time is relative to updated start time
end_time = start_time + w_duration;
// Plot vertical lines if requested
if (plot_range_inp) {
string name = "Start Time" + (string)start_time;
if (start_time > 0) {
ObjectCreate(NULL, name, OBJ_VLINE, 0, start_time, 0);
ObjectSetInteger(NULL, name, OBJPROP_COLOR, clrBlue);
ObjectSetInteger(NULL, name, OBJPROP_BACK, true);
}
name = "End Time" + (string)end_time;
if (end_time > 0) {
ObjectCreate(NULL, name, OBJ_VLINE, 0, end_time, 0);
ObjectSetInteger(NULL, name, OBJPROP_COLOR, C'56,108,26');
ObjectSetInteger(NULL, name, OBJPROP_BACK, true);
}
ChartRedraw();
}
}
return in_window;
}
+35
View File
@@ -0,0 +1,35 @@
class BarUtils {
protected:
datetime previousTime; // Stores the previous bar open time to detect new bars
datetime bar_open_time; // Current bar open time
public:
bool is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, string daily_start_time = "00:10");
};
bool BarUtils::is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, string daily_start_time) {
// Get the open time of the current bar
bar_open_time = iTime(symbol, time_frame, 0);
// Check if it's different from the last seen time — this implies a new bar has formed
if (previousTime != bar_open_time) {
// Special logic for daily bars: wait until a specific time-of-day threshold
if (PeriodSeconds(time_frame) == PeriodSeconds(PERIOD_D1)) {
// Don't trigger on midnight, wait until configured daily_start_time (e.g., "00:10")
if (TimeCurrent() > StringToTime(daily_start_time)) {
previousTime = bar_open_time; // Update the marker
return true;
}
} else {
// For all non-daily timeframes, treat any change in bar time as new bar
previousTime = bar_open_time;
return true;
}
}
// No new bar detected
return false;
}
+43
View File
@@ -0,0 +1,43 @@
class BufferUtils {
public:
double get_latest_buffer_value(int handle);
double get_buffer_value(int handle, int shift);
};
// ---- IMPLEMENTATION BELOW ----
/**
* Get the most recent value from the specified indicator buffer.
*
* param handle: Indicator handle (must be valid and previously created)
* return: Most recent buffer value (shift 0), or 0.0 if retrieval fails
*/
double BufferUtils::get_latest_buffer_value(int handle) {
double val[];
ArraySetAsSeries(val, true);
if (CopyBuffer(handle, 0, 0, 1, val) == 1)
return val[0];
return 0.0;
}
/**
* Get a specific historical value from the specified indicator buffer.
*
* param handle: Indicator handle
* param shift: Bar index (0 = current, 1 = previous, etc.)
* return: Buffer value at shift, or 0.0 if retrieval fails
*/
double BufferUtils::get_buffer_value(int handle, int shift) {
double val[];
ArraySetAsSeries(val, true);
if (CopyBuffer(handle, 0, shift, 1, val) == 1)
return val[0];
return 0.0;
}
@@ -1,278 +1,278 @@
#property library #property library
#include <Trade/Trade.mqh> #include <Trade/Trade.mqh>
#include <MyLibs/TimeZones.mqh> #include <MyLibs/TimeZones.mqh>
#include <MyLibs/Myfunctions.mqh> #include <MyLibs/Myfunctions.mqh>
class CalculatePositionData : public CObject{ class CalculatePositionData : public CObject{
protected: protected:
CTrade trade; CTrade trade;
TimeZones tz; TimeZones tz;
CPositionInfo position; CPositionInfo position;
MyFunctions mf; MyFunctions mf;
bool check_lots(double &lots, string symbol); bool check_lots(double &lots, string symbol);
bool normalise_price(double price, double &normalizedPrice, string symbol); bool normalise_price(double price, double &normalizedPrice, string symbol);
// double adjusted_point(string symbol); // double adjusted_point(string symbol);
public: public:
double calculate_stoploss(string symbol, double price, int order_side, string _sl_mode, double sl_var, ENUM_TIMEFRAMES atr_period); double calculate_stoploss(string symbol, double price, int order_side, string _sl_mode, double sl_var, ENUM_TIMEFRAMES atr_period);
double calculate_take_profit(string symbol, double price, double stoploss, int order_side, string mode_tp, double tp_var, ENUM_TIMEFRAMES atr_period); double calculate_take_profit(string symbol, double price, double stoploss, int order_side, string mode_tp, double tp_var, ENUM_TIMEFRAMES atr_period);
double calculate_lots(string symbol, double sl_distance, double price, string mode_lot, double lot_var); double calculate_lots(string symbol, double sl_distance, double price, string mode_lot, double lot_var);
double calculate_trading_cost(string symbol, ulong position_ticket); double calculate_trading_cost(string symbol, ulong position_ticket);
}; };
double CalculatePositionData::calculate_stoploss(string symbol, double price, int order_side, string mode_sl, double sl_var, ENUM_TIMEFRAMES atr_period){ double CalculatePositionData::calculate_stoploss(string symbol, double price, int order_side, string mode_sl, double sl_var, ENUM_TIMEFRAMES atr_period){
// order_side int must be 1 for BUY or 2 for // order_side int must be 1 for BUY or 2 for
double sl=0; double sl=0;
if(mode_sl=="NO_STOPLOSS"){ if(mode_sl=="NO_STOPLOSS"){
sl=0; sl=0;
} }
if(mode_sl=="SL_BREAKEVEN"){ if(mode_sl=="SL_BREAKEVEN"){
// https://www.youtube.com/watch?v=idPulZ3_iR0 // https://www.youtube.com/watch?v=idPulZ3_iR0
Alert("Not implemented yet yet"); Alert("Not implemented yet yet");
} }
if(mode_sl=="SL_FIXED_PIPS"){ if(mode_sl=="SL_FIXED_PIPS"){
// pips/poins = https://www.mql5.com/en/forum/187757 // pips/poins = https://www.mql5.com/en/forum/187757
double adj_point = mf.adjusted_point(symbol); double adj_point = mf.adjusted_point(symbol);
if(order_side == 1){ if(order_side == 1){
sl = price - sl_var * adj_point; sl = price - sl_var * adj_point;
if(!normalise_price(sl,sl,symbol)){return false;} if(!normalise_price(sl,sl,symbol)){return false;}
} }
if(order_side == 2){ if(order_side == 2){
sl = price + sl_var * adj_point; sl = price + sl_var * adj_point;
if(!normalise_price(sl,sl,symbol)){return false;} if(!normalise_price(sl,sl,symbol)){return false;}
} }
} }
if(mode_sl=="SL_FIXED_PERCENT"){ if(mode_sl=="SL_FIXED_PERCENT"){
if(order_side == 1){ if(order_side == 1){
sl = (-1.0 * sl_var * price / 100.00) + price; sl = (-1.0 * sl_var * price / 100.00) + price;
if(!normalise_price(sl,sl,symbol)){return false;} if(!normalise_price(sl,sl,symbol)){return false;}
} }
if(order_side == 2){ if(order_side == 2){
sl = sl_var * price / 100.00 + price; sl = sl_var * price / 100.00 + price;
if(!normalise_price(sl,sl,symbol)){return false;} if(!normalise_price(sl,sl,symbol)){return false;}
} }
} }
if(mode_sl=="SL_ATR_MULTIPLE"){ if(mode_sl=="SL_ATR_MULTIPLE"){
int atr_handle = iATR(symbol,atr_period,14); int atr_handle = iATR(symbol,atr_period,14);
double atr[]; double atr[];
ArraySetAsSeries(atr,true); ArraySetAsSeries(atr,true);
CopyBuffer(atr_handle,MAIN_LINE,1,1,atr); CopyBuffer(atr_handle,MAIN_LINE,1,1,atr);
if(order_side == 1){ if(order_side == 1){
sl = price - (atr[0] * sl_var); sl = price - (atr[0] * sl_var);
if(!normalise_price(sl,sl,symbol)){return false;} if(!normalise_price(sl,sl,symbol)){return false;}
} }
if(order_side == 2){ if(order_side == 2){
sl = price + (atr[0] * sl_var); sl = price + (atr[0] * sl_var);
if(!normalise_price(sl,sl,symbol)){return false;} if(!normalise_price(sl,sl,symbol)){return false;}
} }
} }
if(mode_sl=="SL_SPECIFIED_VALUE"){ if(mode_sl=="SL_SPECIFIED_VALUE"){
double adj_point = mf.adjusted_point(symbol); double adj_point = mf.adjusted_point(symbol);
if(order_side == 1){ if(order_side == 1){
double pip_50_sl = price - 10 * adj_point; double pip_50_sl = price - 10 * adj_point;
if(sl_var >= pip_50_sl){ if(sl_var >= pip_50_sl){
sl = pip_50_sl; sl = pip_50_sl;
} }
else sl = sl_var; else sl = sl_var;
if(!normalise_price(sl,sl,symbol)){return false;} if(!normalise_price(sl,sl,symbol)){return false;}
} }
if(order_side == 2){ if(order_side == 2){
double pip_50_sl = price + 10 * adj_point; double pip_50_sl = price + 10 * adj_point;
if(sl_var <= pip_50_sl){ if(sl_var <= pip_50_sl){
sl = pip_50_sl; sl = pip_50_sl;
} }
else sl = sl_var; else sl = sl_var;
sl = sl = sl_var; sl = sl = sl_var;
if(!normalise_price(sl,sl,symbol)){return false;} if(!normalise_price(sl,sl,symbol)){return false;}
} }
} }
return sl; return sl;
} }
double CalculatePositionData::calculate_take_profit(string symbol, double price, double stoploss, int order_side, string mode_tp, double _tp_var, ENUM_TIMEFRAMES atr_period){ double CalculatePositionData::calculate_take_profit(string symbol, double price, double stoploss, int order_side, string mode_tp, double _tp_var, ENUM_TIMEFRAMES atr_period){
// order_side int must be 1 for BUY or 2 for SELL // order_side int must be 1 for BUY or 2 for SELL
double tp=0; double tp=0;
if(mode_tp=="NO_TAKE_PROFIT"){ if(mode_tp=="NO_TAKE_PROFIT"){
tp=0; tp=0;
} }
if(mode_tp=="TP_FIXED_PIPS"){ if(mode_tp=="TP_FIXED_PIPS"){
double adj_point = mf.adjusted_point(symbol); double adj_point = mf.adjusted_point(symbol);
if(order_side == 1){ if(order_side == 1){
tp = price + _tp_var * adj_point; tp = price + _tp_var * adj_point;
if(!normalise_price(tp,tp,symbol)){return false;} if(!normalise_price(tp,tp,symbol)){return false;}
} }
if(order_side == 2){ if(order_side == 2){
tp = price - _tp_var * adj_point; tp = price - _tp_var * adj_point;
if(!normalise_price(tp,tp,symbol)){return false;} if(!normalise_price(tp,tp,symbol)){return false;}
} }
} }
if(mode_tp=="TP_FIXED_PERCENT"){ if(mode_tp=="TP_FIXED_PERCENT"){
if(order_side == 1){ if(order_side == 1){
tp = _tp_var * price / 100.00 + price; tp = _tp_var * price / 100.00 + price;
if(!normalise_price(tp,tp,symbol)){return false;} if(!normalise_price(tp,tp,symbol)){return false;}
} }
if(order_side == 2){ if(order_side == 2){
tp = (-1 * _tp_var * price / 100.00) + price; tp = (-1 * _tp_var * price / 100.00) + price;
if(!normalise_price(tp,tp,symbol)){return false;} if(!normalise_price(tp,tp,symbol)){return false;}
} }
} }
if(mode_tp=="TP_ATR_MULTIPLE"){ if(mode_tp=="TP_ATR_MULTIPLE"){
int atr_handle = iATR(symbol,atr_period,14); int atr_handle = iATR(symbol,atr_period,14);
double atr[]; double atr[];
ArraySetAsSeries(atr,true); ArraySetAsSeries(atr,true);
CopyBuffer(atr_handle,MAIN_LINE,1,1,atr); CopyBuffer(atr_handle,MAIN_LINE,1,1,atr);
if(order_side == 1){ if(order_side == 1){
tp = price + (atr[0] * _tp_var); tp = price + (atr[0] * _tp_var);
if(!normalise_price(tp,tp,symbol)){return false;} if(!normalise_price(tp,tp,symbol)){return false;}
} }
if(order_side == 2){ if(order_side == 2){
tp = price - (atr[0] * _tp_var); tp = price - (atr[0] * _tp_var);
if(!normalise_price(tp,tp,symbol)){return false;} if(!normalise_price(tp,tp,symbol)){return false;}
} }
} }
if(mode_tp=="TP_SL_MULTIPLE"){ if(mode_tp=="TP_SL_MULTIPLE"){
if(order_side == 1){ if(order_side == 1){
double sl_size = price - stoploss; double sl_size = price - stoploss;
tp = price + (_tp_var * sl_size); tp = price + (_tp_var * sl_size);
if(!normalise_price(tp,tp,symbol)){return false;} if(!normalise_price(tp,tp,symbol)){return false;}
} }
if(order_side == 2){ if(order_side == 2){
double sl_size = stoploss - price; double sl_size = stoploss - price;
tp = price - (_tp_var * sl_size); tp = price - (_tp_var * sl_size);
if(!normalise_price(tp,tp,symbol)){return false;} if(!normalise_price(tp,tp,symbol)){return false;}
} }
} }
if(mode_tp=="TP_SPECIFIED_VALUE"){ if(mode_tp=="TP_SPECIFIED_VALUE"){
if(_tp_var!=0){ if(_tp_var!=0){
double adj_point = mf.adjusted_point(symbol); double adj_point = mf.adjusted_point(symbol);
if(order_side == 1){ if(order_side == 1){
double pip_limit = price + 10 * adj_point; double pip_limit = price + 10 * adj_point;
if(_tp_var <= pip_limit){ if(_tp_var <= pip_limit){
tp = pip_limit; tp = pip_limit;
} }
else tp = _tp_var; else tp = _tp_var;
if(!normalise_price(tp,tp,symbol)){return false;} if(!normalise_price(tp,tp,symbol)){return false;}
} }
if(order_side == 2){ if(order_side == 2){
double pip_limit = price - 10 * adj_point; double pip_limit = price - 10 * adj_point;
if(_tp_var >= pip_limit){ if(_tp_var >= pip_limit){
tp = pip_limit; tp = pip_limit;
} }
else tp = _tp_var; else tp = _tp_var;
tp = tp = _tp_var; tp = tp = _tp_var;
if(!normalise_price(tp,tp,symbol)){return false;} if(!normalise_price(tp,tp,symbol)){return false;}
} }
} }
} }
return tp; return tp;
} }
double CalculatePositionData::calculate_lots(string symbol, double sl_distance, double price, string mode_lot, double lot_var){ double CalculatePositionData::calculate_lots(string symbol, double sl_distance, double price, string mode_lot, double lot_var){
double lots = 0; double lots = 0;
double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE); double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE); double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double volume_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); double volume_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
double account_value = fmin(fmin(AccountInfoDouble(ACCOUNT_EQUITY),AccountInfoDouble(ACCOUNT_BALANCE)),AccountInfoDouble(ACCOUNT_MARGIN_FREE)); double account_value = fmin(fmin(AccountInfoDouble(ACCOUNT_EQUITY),AccountInfoDouble(ACCOUNT_BALANCE)),AccountInfoDouble(ACCOUNT_MARGIN_FREE));
double risk_money = account_value * lot_var / 100; double risk_money = account_value * lot_var / 100;
if(mode_lot=="LOT_MODE_FIXED"){ if(mode_lot=="LOT_MODE_FIXED"){
lots = lot_var; lots = lot_var;
} }
if(mode_lot=="LOT_MODE_PCT_RISK"){ if(mode_lot=="LOT_MODE_PCT_RISK"){
double money_lot_step = (sl_distance / tick_size) * tick_value * volume_step; double money_lot_step = (sl_distance / tick_size) * tick_value * volume_step;
lots = MathFloor(risk_money/money_lot_step) * volume_step; lots = MathFloor(risk_money/money_lot_step) * volume_step;
} }
if(mode_lot=="LOT_MODE_PCT_ACCOUNT"){ if(mode_lot=="LOT_MODE_PCT_ACCOUNT"){
double money_lot_step = (price / tick_size) * tick_value * volume_step; double money_lot_step = (price / tick_size) * tick_value * volume_step;
lots = MathFloor(risk_money/money_lot_step) * volume_step; lots = MathFloor(risk_money/money_lot_step) * volume_step;
} }
if(!check_lots(lots, symbol)){return false;} if(!check_lots(lots, symbol)){return false;}
return lots; return lots;
} }
bool CalculatePositionData::check_lots(double &lots, string symbol){ bool CalculatePositionData::check_lots(double &lots, string symbol){
double min = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); double min = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double max = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX); double max = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); double step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
if(lots<min){ if(lots<min){
Print("Lot size will be set to minimum allowed volume"); Print("Lot size will be set to minimum allowed volume");
lots = min; lots = min;
return true; return true;
} }
if(lots>max){ if(lots>max){
Print("Lot size greater than maximum allowed volume. lots:",lots,"max:",max); Print("Lot size greater than maximum allowed volume. lots:",lots,"max:",max);
return false; return false;
} }
lots = (int)MathFloor(lots/step) * step; lots = (int)MathFloor(lots/step) * step;
return true; return true;
} }
bool CalculatePositionData::normalise_price(double price, double &normalizedPrice, string symbol){ bool CalculatePositionData::normalise_price(double price, double &normalizedPrice, string symbol){
double tickSize; double tickSize;
if(!SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_SIZE,tickSize)){ if(!SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_SIZE,tickSize)){
Print("Failed to get tick size"); Print("Failed to get tick size");
return false; return false;
} }
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
normalizedPrice = NormalizeDouble(MathRound(price/tickSize)*tickSize, symbol_digits); normalizedPrice = NormalizeDouble(MathRound(price/tickSize)*tickSize, symbol_digits);
return true; return true;
} }
double CalculatePositionData::calculate_trading_cost(string symbol, ulong position_ticket){ double CalculatePositionData::calculate_trading_cost(string symbol, ulong position_ticket){
position.SelectByTicket(position_ticket); position.SelectByTicket(position_ticket);
double swap = PositionGetDouble(POSITION_SWAP); double swap = PositionGetDouble(POSITION_SWAP);
double commission = PositionGetDouble(POSITION_COMMISSION); double commission = PositionGetDouble(POSITION_COMMISSION);
double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE); double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE); double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double lot_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); double lot_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
double lots = PositionGetDouble(POSITION_VOLUME); double lots = PositionGetDouble(POSITION_VOLUME);
double trading_cost = -1 * ((commission + swap) / tick_value * tick_size / lots); double trading_cost = -1 * ((commission + swap) / tick_value * tick_size / lots);
return trading_cost; return trading_cost;
} }
+210 -210
View File
@@ -1,211 +1,211 @@
#property library #property library
#include <Trade/Trade.mqh> #include <Trade/Trade.mqh>
#include <MyLibs/TradingWindow.mqh> #include <MyLibs/TradingWindow.mqh>
#include <MyLibs/MyEnums.mqh> #include <MyLibs/MyEnums.mqh>
class MyFunctions : public CObject{ class MyFunctions : public CObject{
protected: protected:
CTrade trade; CTrade trade;
TradingWindow tw; TradingWindow tw;
datetime previousTime; datetime previousTime;
datetime bar_open_time; datetime bar_open_time;
public: public:
void draw_line(double value, string name,color clr); void draw_line(double value, string name,color clr);
bool check_indicator_handles(int &indicator_handles[]); bool check_indicator_handles(int &indicator_handles[]);
double adjusted_point(string symbol); double adjusted_point(string symbol);
double get_bid_ask_price(string symbol, int price_side); double get_bid_ask_price(string symbol, int price_side);
bool is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, string daily_start_time="00:10"); bool is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, string daily_start_time="00:10");
bool trade_window(string t1, string t2, string time_zone, bool plot_range_inp=true); bool trade_window(string t1, string t2, string time_zone, bool plot_range_inp=true);
bool in_test_period(MODE_SPLIT_DATA data_period); bool in_test_period(MODE_SPLIT_DATA data_period);
void get_white_list(MULTI_SYM_MODE mode, string& DataArray[]); void get_white_list(MULTI_SYM_MODE mode, string& DataArray[]);
}; };
void MyFunctions::get_white_list(MULTI_SYM_MODE mode, string& DataArray[]){ void MyFunctions::get_white_list(MULTI_SYM_MODE mode, string& DataArray[]){
if(mode==MULTI_SYM_CHART){ if(mode==MULTI_SYM_CHART){
string a[] = {_Symbol}; string a[] = {_Symbol};
ArrayResize(DataArray, ArraySize(a)); ArrayResize(DataArray, ArraySize(a));
for(int i = 0; i < ArraySize(DataArray); i++){ for(int i = 0; i < ArraySize(DataArray); i++){
DataArray[i]=a[i]; DataArray[i]=a[i];
} }
} }
if(mode==MULTI_SYM_FX_B5){ if(mode==MULTI_SYM_FX_B5){
string a[] = {"EURUSD", "AUDNZD", "EURGBP", "AUDCAD", "CHFJPY"}; string a[] = {"EURUSD", "AUDNZD", "EURGBP", "AUDCAD", "CHFJPY"};
ArrayResize(DataArray, ArraySize(a)); ArrayResize(DataArray, ArraySize(a));
for(int i = 0; i < ArraySize(DataArray); i++){ for(int i = 0; i < ArraySize(DataArray); i++){
DataArray[i]=a[i]; DataArray[i]=a[i];
} }
} }
if(mode==MULTI_SYM_FX_28){ if(mode==MULTI_SYM_FX_28){
string a[] = {"EURUSD","AUDNZD","AUDUSD","AUDJPY","EURCHF","EURGBP","EURJPY","GBPCHF","GBPJPY","GBPUSD","NZDUSD","USDCAD","USDCHF","USDJPY","CADJPY","EURAUD","CHFJPY","EURCAD","AUDCAD","AUDCHF","CADCHF","EURNZD","GBPAUD","GBPCAD","GBPNZD","NZDCAD","NZDCHF","NZDJPY",}; string a[] = {"EURUSD","AUDNZD","AUDUSD","AUDJPY","EURCHF","EURGBP","EURJPY","GBPCHF","GBPJPY","GBPUSD","NZDUSD","USDCAD","USDCHF","USDJPY","CADJPY","EURAUD","CHFJPY","EURCAD","AUDCAD","AUDCHF","CADCHF","EURNZD","GBPAUD","GBPCAD","GBPNZD","NZDCAD","NZDCHF","NZDJPY",};
ArrayResize(DataArray, ArraySize(a)); ArrayResize(DataArray, ArraySize(a));
for(int i = 0; i < ArraySize(DataArray); i++){ for(int i = 0; i < ArraySize(DataArray); i++){
DataArray[i]=a[i]; DataArray[i]=a[i];
} }
} }
} }
bool MyFunctions::trade_window(string t1, string t2, string time_zone="Broker", bool plot_range_inp=true){ bool MyFunctions::trade_window(string t1, string t2, string time_zone="Broker", bool plot_range_inp=true){
bool in_window = tw.define_window(t1, t2, time_zone, plot_range_inp); bool in_window = tw.define_window(t1, t2, time_zone, plot_range_inp);
return in_window; return in_window;
} }
//if(!mf.is_new_bar(symbol, PERIOD_D1, "00:06")){return;} //if(!mf.is_new_bar(symbol, PERIOD_D1, "00:06")){return;}
bool MyFunctions::is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, string daily_start_time="00:10"){ bool MyFunctions::is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, string daily_start_time="00:10"){
bar_open_time = iTime(symbol, time_frame, 0); bar_open_time = iTime(symbol, time_frame, 0);
if(previousTime!=bar_open_time){ if(previousTime!=bar_open_time){
if(PeriodSeconds(time_frame)==PeriodSeconds(PERIOD_D1)){ if(PeriodSeconds(time_frame)==PeriodSeconds(PERIOD_D1)){
if(TimeCurrent() > StringToTime(daily_start_time)){ if(TimeCurrent() > StringToTime(daily_start_time)){
previousTime=bar_open_time; previousTime=bar_open_time;
return true; return true;
} }
} }
else{ else{
previousTime=bar_open_time; previousTime=bar_open_time;
return true; return true;
} }
} }
return false; return false;
} }
//if(!mf.in_test_period(data_split_method){return;} //if(!mf.in_test_period(data_split_method){return;}
bool MyFunctions::in_test_period(MODE_SPLIT_DATA data_split_method){ bool MyFunctions::in_test_period(MODE_SPLIT_DATA data_split_method){
string result[]; string result[];
string string_tc = TimeToString(TimeCurrent()); string string_tc = TimeToString(TimeCurrent());
ushort u_sep = StringGetCharacter(".",0); ushort u_sep = StringGetCharacter(".",0);
int split_string = StringSplit(string_tc, u_sep, result); int split_string = StringSplit(string_tc, u_sep, result);
bool odd_year = int(result[0]) % 2; bool odd_year = int(result[0]) % 2;
bool odd_month = int(result[1]) % 2; bool odd_month = int(result[1]) % 2;
// get week of the year. rough estimate can be late the first week of jan: // get week of the year. rough estimate can be late the first week of jan:
MqlDateTime dt; MqlDateTime dt;
TimeToStruct(TimeCurrent(),dt); TimeToStruct(TimeCurrent(),dt);
int iDay = (dt.day_of_week + 6 ) % 7 + 1; // convert day to standard index (1=Mon,...,7=Sun) int iDay = (dt.day_of_week + 6 ) % 7 + 1; // convert day to standard index (1=Mon,...,7=Sun)
int iWeek = (dt.day_of_year - iDay + 10 ) / 7; // calculate standard week number int iWeek = (dt.day_of_year - iDay + 10 ) / 7; // calculate standard week number
bool odd_week = iWeek % 2; bool odd_week = iWeek % 2;
if(data_split_method==NO_SPLIT){ if(data_split_method==NO_SPLIT){
return true; return true;
} }
if(data_split_method==ODD_YEARS){ if(data_split_method==ODD_YEARS){
if (odd_year){ if (odd_year){
return true; return true;
} }
} }
if(data_split_method==EVEN_YEARS){ if(data_split_method==EVEN_YEARS){
if (!odd_year){ if (!odd_year){
return true; return true;
} }
} }
if(data_split_method==ODD_MONTHS){ if(data_split_method==ODD_MONTHS){
if (odd_month){ if (odd_month){
return true; return true;
} }
} }
if(data_split_method==EVEN_MONTHS){ if(data_split_method==EVEN_MONTHS){
if (!odd_month){ if (!odd_month){
return true; return true;
} }
} }
if(data_split_method==ODD_WEEKS){ if(data_split_method==ODD_WEEKS){
if (odd_week){ if (odd_week){
return true; return true;
} }
} }
if(data_split_method==EVEN_WEEKS){ if(data_split_method==EVEN_WEEKS){
if (!odd_week){ if (!odd_week){
return true; return true;
} }
} }
return false; return false;
} }
void MyFunctions::draw_line(double value, string name,color clr=clrBlack){ void MyFunctions::draw_line(double value, string name,color clr=clrBlack){
// EG: // EG:
// ArrayResize(bar,1000); // ArrayResize(bar,1000);
// ArraySetAsSeries(bar, true); // ArraySetAsSeries(bar, true);
// CopyRates(symbol,PERIOD_CURRENT,1,1000,bar); // CopyRates(symbol,PERIOD_CURRENT,1,1000,bar);
// double close = bar[0].close; // double close = bar[0].close;
// draw_line(close,"CLOSE",clrBlue); // draw_line(close,"CLOSE",clrBlue);
if(ObjectFind(0,name)<0){ if(ObjectFind(0,name)<0){
ResetLastError(); ResetLastError();
if(!ObjectCreate(0,name,OBJ_HLINE,0,0,value)){ if(!ObjectCreate(0,name,OBJ_HLINE,0,0,value)){
Print(__FUNCTION__,": failed to create a horizontal line! Error code = ",GetLastError()); Print(__FUNCTION__,": failed to create a horizontal line! Error code = ",GetLastError());
return; return;
} }
ObjectSetInteger(0,name,OBJPROP_COLOR,clr); ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID); ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
ObjectSetInteger(0,name,OBJPROP_WIDTH,1); ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
} }
ResetLastError(); ResetLastError();
if(!ObjectMove(0,name,0,0,value)){ if(!ObjectMove(0,name,0,0,value)){
Print(__FUNCTION__,": failed to move the horizontal line! Error code = ",GetLastError()); Print(__FUNCTION__,": failed to move the horizontal line! Error code = ",GetLastError());
return; return;
} }
ChartRedraw(); ChartRedraw();
} }
double MyFunctions::adjusted_point(string symbol){ double MyFunctions::adjusted_point(string symbol){
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
int digits_adjust=1; int digits_adjust=1;
if(symbol_digits==3 || symbol_digits==5){ if(symbol_digits==3 || symbol_digits==5){
digits_adjust=10; digits_adjust=10;
} }
double symbol_point_val = SymbolInfoDouble(symbol,SYMBOL_POINT); double symbol_point_val = SymbolInfoDouble(symbol,SYMBOL_POINT);
double m_adjusted_point; double m_adjusted_point;
m_adjusted_point = symbol_point_val * digits_adjust; m_adjusted_point = symbol_point_val * digits_adjust;
return m_adjusted_point; return m_adjusted_point;
} }
// price side - 1 for the ask price and 2 for the bid price // price side - 1 for the ask price and 2 for the bid price
double MyFunctions::get_bid_ask_price(string symbol, int price_side){ double MyFunctions::get_bid_ask_price(string symbol, int price_side){
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double symbol_point = SymbolInfoDouble(symbol, SYMBOL_POINT); double symbol_point = SymbolInfoDouble(symbol, SYMBOL_POINT);
double ask = SymbolInfoDouble(symbol, SYMBOL_ASK); double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
ask = NormalizeDouble(ask, symbol_digits); ask = NormalizeDouble(ask, symbol_digits);
double bid = SymbolInfoDouble(symbol, SYMBOL_BID); double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
bid = NormalizeDouble(bid, symbol_digits); bid = NormalizeDouble(bid, symbol_digits);
double price = 0; double price = 0;
if(price_side==1){ if(price_side==1){
price = ask; price = ask;
} }
else if(price_side==2){ else if(price_side==2){
price = bid; price = bid;
} }
return price; return price;
} }
File diff suppressed because it is too large Load Diff
+35
View File
@@ -0,0 +1,35 @@
class SymbolUtils {
public:
double adjusted_point(string symbol);
double get_bid_ask_price(string symbol, int price_side);
};
/**
* Adjusts the point size for symbols with 3 or 5 digits (e.g. JPY pairs or fractional pips).
* Example: if symbol has 5 digits, 1 pip = 10 points.
*/
double SymbolUtils::adjusted_point(string symbol) {
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
int digits_adjust = (symbol_digits == 3 || symbol_digits == 5) ? 10 : 1;
double point_val = SymbolInfoDouble(symbol, SYMBOL_POINT);
return point_val * digits_adjust;
}
/**
* Returns either bid or ask price for a symbol, normalised to correct digits.
*
* param price_side: 1 for ASK, 2 for BID
*/
double SymbolUtils::get_bid_ask_price(string symbol, int price_side) {
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double ask = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_ASK), digits);
double bid = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_BID), digits);
if (price_side == 1) return ask;
if (price_side == 2) return bid;
return 0.0; // fallback if invalid side passed
}
+65 -65
View File
@@ -1,65 +1,65 @@
#property library #property library
#include <Trade/Trade.mqh> #include <Trade/Trade.mqh>
#include <MyLibs/TimeZones.mqh> #include <MyLibs/TimeZones.mqh>
class TradingWindow : public CObject{ class TradingWindow : public CObject{
protected: protected:
TimeZones tz; TimeZones tz;
bool in_window; bool in_window;
datetime start_time; datetime start_time;
datetime end_time; datetime end_time;
public: public:
bool define_window(string t1, string t2, string time_zone, bool plot_range_inp=true); bool define_window(string t1, string t2, string time_zone, bool plot_range_inp=true);
}; };
bool TradingWindow::define_window(string t1, string t2, string time_zone, bool plot_range=true){ bool TradingWindow::define_window(string t1, string t2, string time_zone, bool plot_range=true){
datetime _t1 = StringToTime(t1); datetime _t1 = StringToTime(t1);
datetime _t2 = StringToTime(t2); datetime _t2 = StringToTime(t2);
if(_t1 > _t2){ if(_t1 > _t2){
_t2 = _t2 + PeriodSeconds(PERIOD_D1); _t2 = _t2 + PeriodSeconds(PERIOD_D1);
} }
int w_duration = (int)(_t2 - _t1); int w_duration = (int)(_t2 - _t1);
// window flag // window flag
if(TimeCurrent() >= start_time && TimeCurrent() <= end_time){ if(TimeCurrent() >= start_time && TimeCurrent() <= end_time){
in_window = true; in_window = true;
} }
// define new window // define new window
if(TimeCurrent() >= end_time){ if(TimeCurrent() >= end_time){
in_window = false; in_window = false;
start_time = tz.timezone_conversions(time_zone, StringToTime(t1), "Broker"); start_time = tz.timezone_conversions(time_zone, StringToTime(t1), "Broker");
if(TimeCurrent()>=start_time){ if(TimeCurrent()>=start_time){
start_time += PeriodSeconds(PERIOD_D1); start_time += PeriodSeconds(PERIOD_D1);
} }
end_time = start_time + w_duration; end_time = start_time + w_duration;
if(plot_range){ if(plot_range){
string name = "Start Time" + (string)start_time; string name = "Start Time" + (string)start_time;
if(start_time>0){ if(start_time>0){
ObjectCreate(NULL, name, OBJ_VLINE, 0, start_time, 0); ObjectCreate(NULL, name, OBJ_VLINE, 0, start_time, 0);
ObjectSetInteger(NULL, name,OBJPROP_COLOR, clrBlue); ObjectSetInteger(NULL, name,OBJPROP_COLOR, clrBlue);
ObjectSetInteger(NULL, name,OBJPROP_BACK, true); ObjectSetInteger(NULL, name,OBJPROP_BACK, true);
} }
name = "End Time" + (string)end_time; name = "End Time" + (string)end_time;
if(end_time>0){ if(end_time>0){
ObjectCreate(NULL, name, OBJ_VLINE, 0, end_time, 0); ObjectCreate(NULL, name, OBJ_VLINE, 0, end_time, 0);
ObjectSetInteger(NULL, name,OBJPROP_COLOR, C'56,108,26'); ObjectSetInteger(NULL, name,OBJPROP_COLOR, C'56,108,26');
ObjectSetInteger(NULL, name,OBJPROP_BACK, true); ObjectSetInteger(NULL, name,OBJPROP_BACK, true);
} }
ChartRedraw(); ChartRedraw();
} }
} }
return in_window; return in_window;
} }
Binary file not shown.