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
#include <Trade/Trade.mqh>
#include <MyLibs/MyFunctions.mqh>
class DrawdownControl : public CObject {
protected:
CTrade trade;
MyFunctions mf;
string data_file;
double daily_max_dd_per;
string daily_reset_time;
bool print_statments;
double acc_max_dd_per;
double equaty_control_high;
double equaty_control_low;
double daily_equity_start;
double daily_max_dd_target;
bool daily_dd_limit_reached;
bool write_global_var_data();
bool print_messages();
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);
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_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) {
data_file = inp_data_file;
acc_max_dd_per = inp_acc_max_dd_per;
daily_max_dd_per = inp_daily_max_dd_per;
daily_reset_time = inp_daily_reset_time;
print_statments = inp_print_statments;
// If no data file exisits, create one and set global vairiables:
if(FileIsExist(data_file) == false) {
daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY);
daily_max_dd_target = daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100));
daily_dd_limit_reached = false;
equaty_control_high = 9999999;
equaty_control_low = 0;
write_global_var_data();
}
// If file exisits read file:
if(FileIsExist(data_file) == true) {
int file_handle = FileOpen(data_file, FILE_READ | FILE_ANSI | FILE_TXT);
if(file_handle == INVALID_HANDLE) {
Print("Error opening file: ", data_file);
}
// 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 time_delta = ((long)TimeCurrent() - modifided_date) / 60;
if(time_delta >= 1450) {
daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY);
daily_max_dd_target = daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100));
daily_dd_limit_reached = false;
equaty_control_high = equaty_control_high;
equaty_control_low = equaty_control_low;
write_global_var_data();
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:
else {
daily_equity_start = (double)FileReadString(file_handle, 0);
daily_max_dd_target = (double)FileReadString(file_handle, 1);
daily_dd_limit_reached = FileReadBool(file_handle);
equaty_control_high = (double)FileReadString(file_handle, 3);
equaty_control_low = (double)FileReadString(file_handle, 4);;
}
FileClose(file_handle);
}
print_messages();
}
bool DrawdownControl::determine_daily_dd_limit() {
// Reset max equity at the start of each day:
string ct = TimeToString(TimeCurrent(), TIME_MINUTES);
if(ct == daily_reset_time) {
daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY);
daily_max_dd_target = (daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100)));
daily_dd_limit_reached = false;
write_global_var_data();
print_messages();
}
// 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 == false) {
daily_dd_limit_reached = true;
write_global_var_data();
print_messages();
}
for(int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
trade.PositionClose(ticket);
}
for(int i = OrdersTotal() - 1; i >= 0; i--) {
ulong ticket = OrderGetTicket(i);
trade.OrderDelete(ticket);
}
}
return daily_dd_limit_reached;
}
// 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 account_value = fmin(AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE));
double lot_factor;
// Interpolate to find lot factor between given min and max values.
if (account_value < acc_equity_start){
double acc_equity_min = acc_equity_start - (acc_equity_start * (acc_max_dd_per / 100));
double y1 = min_lot_factor;
double y2 = max_lot_factor;
double x1 = acc_equity_min;
double x = account_value;
double x2 = acc_equity_start;
lot_factor = y1 + (x - x1) * ((y2 - y1) / (x2 - x1));
}
else if(account_value >= acc_equity_start) {
if(dynm_lot_factor=true){
lot_factor = lot_correction_dynamic(dlf_trail_per, min_lot_factor, max_lot_factor);
}
else {
lot_factor = 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 account_value = fmin(AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE));
double trail_point = account_value - (account_value * (acc_dd_percent / 100));
if(equaty_control_low < trail_point){
equaty_control_low = trail_point;
}
if(equaty_control_high < account_value){
equaty_control_high = account_value;
}
if(account_value < equaty_control_low){
equaty_control_low = account_value;
equaty_control_high = account_value + (account_value * (acc_dd_percent / 100));
}
// back-up to file every hour:
if(mf.is_new_bar(_Symbol, PERIOD_H1) == true){
write_global_var_data();
}
// Linear interpolation:
double y1 = min_lot_factor;
double y2 = max_lot_factor;
double x1 = equaty_control_low;
double x = account_value;
double x2 = equaty_control_high;
double y = y1 + (x - x1) * ((y2 - y1) / (x2 - x1));
return y;
}
bool DrawdownControl::write_global_var_data() {
int file_handle = FileOpen(data_file, FILE_WRITE | FILE_ANSI | FILE_TXT);
FileWrite(file_handle, daily_equity_start);
FileWrite(file_handle, daily_max_dd_target);
FileWrite(file_handle, daily_dd_limit_reached);
FileClose(file_handle);
Print(data_file, " written");
return true;
}
bool DrawdownControl::print_messages() {
if(print_statments == true) {
Print("TimeCurrent(): ", TimeToString(TimeCurrent()));
Print("Daily Equity Start: ", (int)daily_equity_start);
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 Hit: ", daily_dd_limit_reached);
}
return true;
}
#property library
#include <Trade/Trade.mqh>
#include <MyLibs/Utils/BarUtils.mqh>
class DrawdownControl : public CObject {
protected:
CTrade trade;
BarUtils bar_utils;
string data_file;
double daily_max_dd_per;
string daily_reset_time;
bool print_statments;
double acc_max_dd_per;
double equaty_control_high;
double equaty_control_low;
double daily_equity_start;
double daily_max_dd_target;
bool daily_dd_limit_reached;
bool write_global_var_data();
bool print_messages();
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);
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_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) {
data_file = inp_data_file;
acc_max_dd_per = inp_acc_max_dd_per;
daily_max_dd_per = inp_daily_max_dd_per;
daily_reset_time = inp_daily_reset_time;
print_statments = inp_print_statments;
// If no data file exisits, create one and set global vairiables:
if(FileIsExist(data_file) == false) {
daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY);
daily_max_dd_target = daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100));
daily_dd_limit_reached = false;
equaty_control_high = 9999999;
equaty_control_low = 0;
write_global_var_data();
}
// If file exisits read file:
if(FileIsExist(data_file) == true) {
int file_handle = FileOpen(data_file, FILE_READ | FILE_ANSI | FILE_TXT);
if(file_handle == INVALID_HANDLE) {
Print("Error opening file: ", data_file);
}
// 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 time_delta = ((long)TimeCurrent() - modifided_date) / 60;
if(time_delta >= 1450) {
daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY);
daily_max_dd_target = daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100));
daily_dd_limit_reached = false;
equaty_control_high = equaty_control_high;
equaty_control_low = equaty_control_low;
write_global_var_data();
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:
else {
daily_equity_start = (double)FileReadString(file_handle, 0);
daily_max_dd_target = (double)FileReadString(file_handle, 1);
daily_dd_limit_reached = FileReadBool(file_handle);
equaty_control_high = (double)FileReadString(file_handle, 3);
equaty_control_low = (double)FileReadString(file_handle, 4);;
}
FileClose(file_handle);
}
print_messages();
}
bool DrawdownControl::determine_daily_dd_limit() {
// Reset max equity at the start of each day:
string ct = TimeToString(TimeCurrent(), TIME_MINUTES);
if(ct == daily_reset_time) {
daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY);
daily_max_dd_target = (daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100)));
daily_dd_limit_reached = false;
write_global_var_data();
print_messages();
}
// 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 == false) {
daily_dd_limit_reached = true;
write_global_var_data();
print_messages();
}
for(int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
trade.PositionClose(ticket);
}
for(int i = OrdersTotal() - 1; i >= 0; i--) {
ulong ticket = OrderGetTicket(i);
trade.OrderDelete(ticket);
}
}
return daily_dd_limit_reached;
}
// 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 account_value = fmin(AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE));
double lot_factor;
// Interpolate to find lot factor between given min and max values.
if (account_value < acc_equity_start){
double acc_equity_min = acc_equity_start - (acc_equity_start * (acc_max_dd_per / 100));
double y1 = min_lot_factor;
double y2 = max_lot_factor;
double x1 = acc_equity_min;
double x = account_value;
double x2 = acc_equity_start;
lot_factor = y1 + (x - x1) * ((y2 - y1) / (x2 - x1));
}
else if(account_value >= acc_equity_start) {
if(dynm_lot_factor=true){
lot_factor = lot_correction_dynamic(dlf_trail_per, min_lot_factor, max_lot_factor);
}
else {
lot_factor = 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 account_value = fmin(AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE));
double trail_point = account_value - (account_value * (acc_dd_percent / 100));
if(equaty_control_low < trail_point){
equaty_control_low = trail_point;
}
if(equaty_control_high < account_value){
equaty_control_high = account_value;
}
if(account_value < equaty_control_low){
equaty_control_low = account_value;
equaty_control_high = account_value + (account_value * (acc_dd_percent / 100));
}
// back-up to file every hour:
if(bar_utils.is_new_bar(_Symbol, PERIOD_H1) == true){
write_global_var_data();
}
// Linear interpolation:
double y1 = min_lot_factor;
double y2 = max_lot_factor;
double x1 = equaty_control_low;
double x = account_value;
double x2 = equaty_control_high;
double y = y1 + (x - x1) * ((y2 - y1) / (x2 - x1));
return y;
}
bool DrawdownControl::write_global_var_data() {
int file_handle = FileOpen(data_file, FILE_WRITE | FILE_ANSI | FILE_TXT);
FileWrite(file_handle, daily_equity_start);
FileWrite(file_handle, daily_max_dd_target);
FileWrite(file_handle, daily_dd_limit_reached);
FileClose(file_handle);
Print(data_file, " written");
return true;
}
bool DrawdownControl::print_messages() {
if(print_statments == true) {
Print("TimeCurrent(): ", TimeToString(TimeCurrent()));
Print("Daily Equity Start: ", (int)daily_equity_start);
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 Hit: ", daily_dd_limit_reached);
}
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
#include <Trade/Trade.mqh>
#include <MyLibs/TimeZones.mqh>
class RangeCalculator : public CObject{
protected:
TimeZones tz;
bool days_initlised;
bool range_initlised;
string symbol;
ENUM_TIMEFRAMES calc_period;
string inp_r_start_string;
int r_duration;
int r_expire;
int r_close;
string inp_timezone;
bool sun;
bool mon;
bool tue;
bool wed;
bool thu;
bool fri;
bool sat;
bool plot_range;
datetime start_time; // Start of the range
datetime end_time; // end of the range
datetime order_expire_time; // end of the range
datetime close_time; // Close time
double high; // high of the range
double low; // low of the range
double mid; // mid 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_low_breakout; // flag if a low breakout occurred
bool above_last;
bool above_current;
bool below_last;
bool below_current;
// private functions
void update_objects();
void draw_objects();
void define_new_range();
bool convert_input_time_strings(string t1, string t2, string t3, string t4);
public:
void calculate_range();
double get_range_high();
double get_range_low();
double get_range_mid();
datetime get_range_start();
datetime get_range_end();
datetime get_order_expire_time();
datetime get_range_close();
bool get_range_high_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);
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){
sun = _inp_sun;
mon = _inp_mon;
tue = _inp_tue;
wed = _inp_wed;
thu = _inp_thu;
fri = _inp_fri;
sat = _inp_sat;
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){
inp_r_start_string = t1;
inp_timezone = time_zone;
symbol = inp_symbol;
calc_period =_calc_period;
plot_range = plot_range_inp;
start_time = 0;
end_time = 0;
close_time = 0;
high = 0;
low = DBL_MAX;
mid = 0;
f_entry = false;
f_high_breakout = false;
f_low_breakout = false;
above_last = false;
above_current= false;
below_last= false;
below_current= false;
if(!days_initlised){
sun = true;
mon = true;
tue = true;
wed = true;
thu = true;
fri = true;
sat = true;
}
range_initlised = true;
bool corret_inputs = convert_input_time_strings(t1, t2, t3, t4);
if(corret_inputs = false){
return false;
}
return true;
}
bool RangeCalculator::convert_input_time_strings(string t1, string t2, string t3, string t4){
datetime _t1 = StringToTime(t1);
datetime _t2 = StringToTime(t2);
datetime _t3 = StringToTime(t3);
datetime _t4 = StringToTime(t4);
if(_t1 > _t2){
_t2 = _t2 + PeriodSeconds(PERIOD_D1);
_t3 = _t3 + PeriodSeconds(PERIOD_D1);
_t4 = _t4 + PeriodSeconds(PERIOD_D1);
}
if(_t2 > _t3){
_t3 = _t3 + PeriodSeconds(PERIOD_D1);
_t4 = _t4 + PeriodSeconds(PERIOD_D1);
}
if(_t3 > _t4){
_t4 = _t4 + PeriodSeconds(PERIOD_D1);
}
r_duration = (int)(_t2 - _t1);
r_expire = (int)(_t3 - _t1);
r_close = (int)(_t4 - _t1);
if(_t4 - _t1 >= PeriodSeconds(PERIOD_D1)){
Alert("INCORRECT RANGE INPUTS!");
return false;
}
return true;
}
// high of the range
double RangeCalculator::get_range_high(){
return high;
};
// low of the range
double RangeCalculator::get_range_low(){
return low;
};
// mid of the range
double RangeCalculator::get_range_mid(){
return mid;
};
datetime RangeCalculator::get_range_start(){
return start_time;
};
datetime RangeCalculator::get_range_end(){
return end_time;
};
datetime RangeCalculator::get_order_expire_time(){
return order_expire_time;
};
datetime RangeCalculator::get_range_close(){
return close_time;
};
// flag if a high breakout occurred
bool RangeCalculator::get_range_high_breakout(){
return f_high_breakout;
};
// flag if a low breakout occurred
bool RangeCalculator::get_range_low_breakout(){
return f_low_breakout;
};
void RangeCalculator::calculate_range(){
f_high_breakout = false;
f_low_breakout = false;
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:
// range calculation
if(TimeCurrent() >= start_time && TimeCurrent() <= end_time){
// set flag
f_entry = true;
// new high
if(last_bar_high > high){
high = last_bar_high;
mid = (high + low)/2;
if(plot_range){
update_objects();
}
}
// new low
if(last_bar_low < low){
low = last_bar_low;
mid = (high + low)/2;
if(plot_range){
update_objects();
}
}
}
// calculate new reange if
if( (TimeCurrent() >= close_time) // close time reached
|| (end_time == 0) // range not calculated yet
|| (end_time !=0 && TimeCurrent() > end_time && !f_entry) // there was a range calculated but no tick inside.
){
define_new_range();
}
// check if we are after the range end
if(TimeCurrent() >= end_time && end_time > 0 && f_entry){
if(!f_high_breakout && last_bar_high >= high){
above_last = above_current;
above_current= true;
if(above_last==false && above_current == true){
f_high_breakout = true;
}
else(f_high_breakout = false);
}
if(!f_low_breakout && last_bar_low >= low){
below_last = below_current;
below_current = true;
if(below_last == false && below_current == true){
f_low_breakout = true;
}
else(f_low_breakout = false);
}
}
}
void RangeCalculator::define_new_range(){
// reset range vars
start_time = 0;
end_time = 0;
order_expire_time = 0;
close_time = 0;
high = 0;
low = INT_MAX;
mid = 0;
f_entry = false;
// calculate range start time:
datetime r_st = StringToTime(inp_r_start_string);
start_time = tz.timezone_conversions(inp_timezone, r_st, "Broker");
for(int i=0; i<8; i++){
MqlDateTime tmp;
TimeToStruct(start_time,tmp);
int dow = tmp.day_of_week;
if(TimeCurrent()>=start_time
|| (dow==0 && !sun)
|| (dow==1 && !mon)
|| (dow==2 && !tue)
|| (dow==3 && !wed)
|| (dow==4 && !thu)
|| (dow==5 && !fri)
|| (dow==6 && !sat)
){
start_time += PeriodSeconds(PERIOD_D1);
}
}
end_time = start_time + r_duration;
order_expire_time = start_time + r_expire;
close_time = start_time + r_close;
if(plot_range){
draw_objects();
}
}
void RangeCalculator::update_objects(){
string name = "Range Mid " + (string)start_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, mid);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, mid);
// ObjectSetString(NULL, name , OBJPROP_TOOLTIP, "Range Mid");
name = "Order expire " + (string)order_expire_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
name = "Range start " + (string)start_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
name = "Range end " + (string)end_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
datetime rct = r_close>=0 ? close_time : INT_MAX;
name = "Range close " + (string)rct;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
name = "Range High " + (string)rct;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, high);
name = "Range Low " + (string)rct;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, low);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
name = "range box "+ (string)start_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
ObjectSetDouble(NULL, name +" ", OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name +" ", OBJPROP_PRICE,1, low);
}
void RangeCalculator::draw_objects(){
datetime rct = r_close>=0 ? close_time : INT_MAX;
// Range mid line
string name = "Range Mid " + (string)start_time;;
ObjectCreate(NULL, name, OBJ_TREND, 0, start_time, mid, rct, mid);
ObjectSetString(NULL, name , OBJPROP_TOOLTIP, "Range Mid" + (string)mid);
ObjectSetInteger(NULL, name, OBJPROP_COLOR, clrGray);
ObjectSetInteger(NULL, name, OBJPROP_WIDTH, 1);
ObjectSetInteger(NULL, name, OBJPROP_STYLE, STYLE_DOT);
// order lines
string name2 = "Order expire " + (string)order_expire_time;
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));
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, C'139,41,41');
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range start " + (string)start_time;
ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, low, start_time, high);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range end " + (string)end_time;
ObjectCreate(NULL, name2, OBJ_TREND, 0, end_time, low, end_time, high);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range close " + (string)rct;
ObjectCreate(NULL, name2, OBJ_TREND, 0, rct, low, rct, high);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range High " + (string)rct;
ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, high, rct, high);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range Low " + (string)rct;
ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, low, rct, low);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2 ,OBJPROP_BACK, true);
// Box
name = "range box " + (string)start_time;
ObjectCreate(NULL, name, OBJ_RECTANGLE, 0, start_time, high, end_time, low);
ObjectSetString(NULL,name,OBJPROP_TOOLTIP,"\n");
ObjectSetInteger(NULL, name,OBJPROP_COLOR, C'128,177,173');
ObjectSetInteger(NULL, name,OBJPROP_FILL, true);
ObjectSetInteger(NULL, name,OBJPROP_BACK, true);
ObjectCreate(NULL, name + " ", OBJ_RECTANGLE, 0, end_time, high, rct, low);
ObjectSetString(NULL, name+ " ", OBJPROP_TOOLTIP, "\n");
ObjectSetInteger(NULL, name + " ",OBJPROP_FILL, true);
ObjectSetInteger(NULL, name + " ",OBJPROP_COLOR, C'165,220,215' );
ObjectSetInteger(NULL, name + " ",OBJPROP_BACK, true);
ChartRedraw();
}
#property library
#include <Trade/Trade.mqh>
#include <MyLibs/Utils/TimeZones.mqh>
class RangeCalculator : public CObject{
protected:
TimeZones tz;
bool days_initlised;
bool range_initlised;
string symbol;
ENUM_TIMEFRAMES calc_period;
string inp_r_start_string;
int r_duration;
int r_expire;
int r_close;
string inp_timezone;
bool sun;
bool mon;
bool tue;
bool wed;
bool thu;
bool fri;
bool sat;
bool plot_range;
datetime start_time; // Start of the range
datetime end_time; // end of the range
datetime order_expire_time; // end of the range
datetime close_time; // Close time
double high; // high of the range
double low; // low of the range
double mid; // mid 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_low_breakout; // flag if a low breakout occurred
bool above_last;
bool above_current;
bool below_last;
bool below_current;
// private functions
void update_objects();
void draw_objects();
void define_new_range();
bool convert_input_time_strings(string t1, string t2, string t3, string t4);
public:
void calculate_range();
double get_range_high();
double get_range_low();
double get_range_mid();
datetime get_range_start();
datetime get_range_end();
datetime get_order_expire_time();
datetime get_range_close();
bool get_range_high_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);
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){
sun = _inp_sun;
mon = _inp_mon;
tue = _inp_tue;
wed = _inp_wed;
thu = _inp_thu;
fri = _inp_fri;
sat = _inp_sat;
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){
inp_r_start_string = t1;
inp_timezone = time_zone;
symbol = inp_symbol;
calc_period =_calc_period;
plot_range = plot_range_inp;
start_time = 0;
end_time = 0;
close_time = 0;
high = 0;
low = DBL_MAX;
mid = 0;
f_entry = false;
f_high_breakout = false;
f_low_breakout = false;
above_last = false;
above_current= false;
below_last= false;
below_current= false;
if(!days_initlised){
sun = true;
mon = true;
tue = true;
wed = true;
thu = true;
fri = true;
sat = true;
}
range_initlised = true;
bool corret_inputs = convert_input_time_strings(t1, t2, t3, t4);
if(corret_inputs = false){
return false;
}
return true;
}
bool RangeCalculator::convert_input_time_strings(string t1, string t2, string t3, string t4){
datetime _t1 = StringToTime(t1);
datetime _t2 = StringToTime(t2);
datetime _t3 = StringToTime(t3);
datetime _t4 = StringToTime(t4);
if(_t1 > _t2){
_t2 = _t2 + PeriodSeconds(PERIOD_D1);
_t3 = _t3 + PeriodSeconds(PERIOD_D1);
_t4 = _t4 + PeriodSeconds(PERIOD_D1);
}
if(_t2 > _t3){
_t3 = _t3 + PeriodSeconds(PERIOD_D1);
_t4 = _t4 + PeriodSeconds(PERIOD_D1);
}
if(_t3 > _t4){
_t4 = _t4 + PeriodSeconds(PERIOD_D1);
}
r_duration = (int)(_t2 - _t1);
r_expire = (int)(_t3 - _t1);
r_close = (int)(_t4 - _t1);
if(_t4 - _t1 >= PeriodSeconds(PERIOD_D1)){
Alert("INCORRECT RANGE INPUTS!");
return false;
}
return true;
}
// high of the range
double RangeCalculator::get_range_high(){
return high;
};
// low of the range
double RangeCalculator::get_range_low(){
return low;
};
// mid of the range
double RangeCalculator::get_range_mid(){
return mid;
};
datetime RangeCalculator::get_range_start(){
return start_time;
};
datetime RangeCalculator::get_range_end(){
return end_time;
};
datetime RangeCalculator::get_order_expire_time(){
return order_expire_time;
};
datetime RangeCalculator::get_range_close(){
return close_time;
};
// flag if a high breakout occurred
bool RangeCalculator::get_range_high_breakout(){
return f_high_breakout;
};
// flag if a low breakout occurred
bool RangeCalculator::get_range_low_breakout(){
return f_low_breakout;
};
void RangeCalculator::calculate_range(){
f_high_breakout = false;
f_low_breakout = false;
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:
// range calculation
if(TimeCurrent() >= start_time && TimeCurrent() <= end_time){
// set flag
f_entry = true;
// new high
if(last_bar_high > high){
high = last_bar_high;
mid = (high + low)/2;
if(plot_range){
update_objects();
}
}
// new low
if(last_bar_low < low){
low = last_bar_low;
mid = (high + low)/2;
if(plot_range){
update_objects();
}
}
}
// calculate new reange if
if( (TimeCurrent() >= close_time) // close time reached
|| (end_time == 0) // range not calculated yet
|| (end_time !=0 && TimeCurrent() > end_time && !f_entry) // there was a range calculated but no tick inside.
){
define_new_range();
}
// check if we are after the range end
if(TimeCurrent() >= end_time && end_time > 0 && f_entry){
if(!f_high_breakout && last_bar_high >= high){
above_last = above_current;
above_current= true;
if(above_last==false && above_current == true){
f_high_breakout = true;
}
else(f_high_breakout = false);
}
if(!f_low_breakout && last_bar_low >= low){
below_last = below_current;
below_current = true;
if(below_last == false && below_current == true){
f_low_breakout = true;
}
else(f_low_breakout = false);
}
}
}
void RangeCalculator::define_new_range(){
// reset range vars
start_time = 0;
end_time = 0;
order_expire_time = 0;
close_time = 0;
high = 0;
low = INT_MAX;
mid = 0;
f_entry = false;
// calculate range start time:
datetime r_st = StringToTime(inp_r_start_string);
start_time = tz.timezone_conversions(inp_timezone, r_st, "Broker");
for(int i=0; i<8; i++){
MqlDateTime tmp;
TimeToStruct(start_time,tmp);
int dow = tmp.day_of_week;
if(TimeCurrent()>=start_time
|| (dow==0 && !sun)
|| (dow==1 && !mon)
|| (dow==2 && !tue)
|| (dow==3 && !wed)
|| (dow==4 && !thu)
|| (dow==5 && !fri)
|| (dow==6 && !sat)
){
start_time += PeriodSeconds(PERIOD_D1);
}
}
end_time = start_time + r_duration;
order_expire_time = start_time + r_expire;
close_time = start_time + r_close;
if(plot_range){
draw_objects();
}
}
void RangeCalculator::update_objects(){
string name = "Range Mid " + (string)start_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, mid);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, mid);
// ObjectSetString(NULL, name , OBJPROP_TOOLTIP, "Range Mid");
name = "Order expire " + (string)order_expire_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
name = "Range start " + (string)start_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
name = "Range end " + (string)end_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
datetime rct = r_close>=0 ? close_time : INT_MAX;
name = "Range close " + (string)rct;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
name = "Range High " + (string)rct;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, high);
name = "Range Low " + (string)rct;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, low);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
name = "range box "+ (string)start_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
ObjectSetDouble(NULL, name +" ", OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name +" ", OBJPROP_PRICE,1, low);
}
void RangeCalculator::draw_objects(){
datetime rct = r_close>=0 ? close_time : INT_MAX;
// Range mid line
string name = "Range Mid " + (string)start_time;;
ObjectCreate(NULL, name, OBJ_TREND, 0, start_time, mid, rct, mid);
ObjectSetString(NULL, name , OBJPROP_TOOLTIP, "Range Mid" + (string)mid);
ObjectSetInteger(NULL, name, OBJPROP_COLOR, clrGray);
ObjectSetInteger(NULL, name, OBJPROP_WIDTH, 1);
ObjectSetInteger(NULL, name, OBJPROP_STYLE, STYLE_DOT);
// order lines
string name2 = "Order expire " + (string)order_expire_time;
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));
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, C'139,41,41');
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range start " + (string)start_time;
ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, low, start_time, high);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range end " + (string)end_time;
ObjectCreate(NULL, name2, OBJ_TREND, 0, end_time, low, end_time, high);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range close " + (string)rct;
ObjectCreate(NULL, name2, OBJ_TREND, 0, rct, low, rct, high);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range High " + (string)rct;
ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, high, rct, high);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range Low " + (string)rct;
ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, low, rct, low);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2 ,OBJPROP_BACK, true);
// Box
name = "range box " + (string)start_time;
ObjectCreate(NULL, name, OBJ_RECTANGLE, 0, start_time, high, end_time, low);
ObjectSetString(NULL,name,OBJPROP_TOOLTIP,"\n");
ObjectSetInteger(NULL, name,OBJPROP_COLOR, C'128,177,173');
ObjectSetInteger(NULL, name,OBJPROP_FILL, true);
ObjectSetInteger(NULL, name,OBJPROP_BACK, true);
ObjectCreate(NULL, name + " ", OBJ_RECTANGLE, 0, end_time, high, rct, low);
ObjectSetString(NULL, name+ " ", OBJPROP_TOOLTIP, "\n");
ObjectSetInteger(NULL, name + " ",OBJPROP_FILL, true);
ObjectSetInteger(NULL, name + " ",OBJPROP_COLOR, C'165,220,215' );
ObjectSetInteger(NULL, name + " ",OBJPROP_BACK, true);
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
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
SL_FIXED_PERCENT, // Fixed Percent
SL_ATR_MULTIPLE, // ATR Multiple
SL_SPECIFIED_VALUE, // Bespoke calculation in code
NO_STOPLOSS, // No Stop-loss
SL_BREAKEVEN, // Breakeven
};
enum TP_MODE{
TP_FIXED_PIPS, // Fixed Pips
TP_FIXED_PERCENT, // Fixed Percent
TP_ATR_MULTIPLE, // ATR Multiple
TP_SL_MULTIPLE, // Multiple of Risk (from sl)
TP_SPECIFIED_VALUE, // Bespoke calculation in code
NO_TAKE_PROFIT, // No Take-Profit
};
enum TSL_MODE{
TSL_ATR_MULTIPLE, // ATR Multiple
TSL_FIXED_PIPS, // Fixed Pips
TSL_FIXED_PERCENT, // Fixed Percent
};
enum TIME_ZONES{
NY, // New York
Lon, // London
Ffm, // Frankfurt
Syd, // Sidney
Mosc, // Moscow
Tok, // Tokyo - no DST
};
enum MULTI_SYM_MODE{
MULTI_SYM_CHART, // Chart Symbol only
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
};
enum LOT_MODE{
LOT_MODE_FIXED, // Fixed Lot Size
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
SL_FIXED_PERCENT, // Fixed Percent
SL_ATR_MULTIPLE, // ATR Multiple
SL_SPECIFIED_VALUE, // Bespoke calculation in code
NO_STOPLOSS, // No Stop-loss
SL_BREAKEVEN, // Breakeven
};
enum TP_MODE{
TP_FIXED_PIPS, // Fixed Pips
TP_FIXED_PERCENT, // Fixed Percent
TP_ATR_MULTIPLE, // ATR Multiple
TP_SL_MULTIPLE, // Multiple of Risk (from sl)
TP_SPECIFIED_VALUE, // Bespoke calculation in code
NO_TAKE_PROFIT, // No Take-Profit
};
enum TSL_MODE{
TSL_ATR_MULTIPLE, // ATR Multiple
TSL_FIXED_PIPS, // Fixed Pips
TSL_FIXED_PERCENT, // Fixed Percent
};
enum TIME_ZONES{
NY, // New York
Lon, // London
Ffm, // Frankfurt
Syd, // Sidney
Mosc, // Moscow
Tok, // Tokyo - no DST
};
enum MULTI_SYM_MODE{
MULTI_SYM_CHART, // Chart Symbol only
MULTI_SYM_FX_B5, // FX Benchmark 5
MULTI_SYM_FX_28 // FX 28 Majors
};
@@ -1,28 +1,28 @@
#include <MyLibs/Myfunctions.mqh>
#include <MyLibs/OrderManagement.mqh>
#include <MyLibs/MyEnums.mqh>
#include <MyLibs/CustomMax.mqh>
CustomMax cm;
MyFunctions mf;
OrderManagment om;
//---
string SymbolsArray[];
input LOT_MODE inp_lot_mode = LOT_MODE_PCT_RISK; // Lot Size Mode
input double inp_lot_var = 2; // Lot Size Var
input SL_MODE inp_sl_mode = SL_ATR_MULTIPLE; // Stop-loss Mode
input double inp_sl_var = 1.5; // Stop-loss Var
input TP_MODE inp_tp_mode = TP_ATR_MULTIPLE; // Take-profit Mode
input double inp_tp_var = 1; // Take-Profit Var
string lot_mode = EnumToString(inp_lot_mode);
string sl_mode = EnumToString(inp_sl_mode);
string tp_mode = EnumToString(inp_tp_mode);
input CUSTOM_MAX_TYPE inp_custom_criteria = CM_WIN_PERCENT_200T;
input MULTI_SYM_MODE inp_sym_mode = MULTI_SYM_FX_B5;
input MODE_SPLIT_DATA inp_data_split_method = NO_SPLIT;
input int inp_force_opt = 1;
input group "-----------------------------------------"
#include <MyLibs/Myfunctions.mqh>
#include <MyLibs/OrderManagement.mqh>
#include <MyLibs/Utils/MyEnums.mqh>
#include <MyLibs/BacktestUtils/CustomMax.mqh>
#include <MyLibs/BacktestUtils/TestDataSplit.mqh>
CustomMax c_max;
// MyFunctions mf;
// OrderManagment om;
//---
input LOT_MODE inp_lot_mode = LOT_MODE_PCT_RISK; // Lot Size Mode
input double inp_lot_var = 2; // Lot Size Var
input SL_MODE inp_sl_mode = SL_ATR_MULTIPLE; // Stop-loss Mode
input double inp_sl_var = 1.5; // Stop-loss Var
input TP_MODE inp_tp_mode = TP_ATR_MULTIPLE; // Take-profit Mode
input double inp_tp_var = 1; // Take-Profit Var
string lot_mode = EnumToString(inp_lot_mode);
string sl_mode = EnumToString(inp_sl_mode);
string tp_mode = EnumToString(inp_tp_mode);
input CUSTOM_MAX_TYPE inp_custom_criteria = CM_WIN_PERCENT;
input int inp_opt_min_trades = 0; // 0/off
input MODE_SPLIT_DATA inp_data_split_method = NO_SPLIT;
input int inp_force_opt = 1;
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
#include <Trade/Trade.mqh>
#include <MyLibs/DealingWithTime.mqh>
class TimeZones: public CObject{
protected:
string dt_s;
int len;
string dt_string;
datetime tC, tGMT, tNY, tLon, tFfm, tMosc, tSyd, tTok;
datetime tz_time;
string tz_date;
datetime time_start;
datetime time_end;
bool is_time;
datetime tGIVEN;
datetime tREQ;
datetime tzt;
datetime tz_req;
double ny_daily_close_protected(string symbol, int shift_days, bool print_data=false);
double required_close;
public:
string get_date_string_from_datetime(datetime dt);
datetime get_timezone_time(string time_zone, bool print_time);
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);
};
string TimeZones::get_date_string_from_datetime(datetime dt){
dt_s = TimeToString(dt);
len = StringLen(dt_s);
dt_string = StringSubstr(dt_s, 0, len-5);
return dt_string;
}
datetime TimeZones::get_timezone_time(string time_zone, bool print_time){
// https://www.mql5.com/en/code/45287
// https://www.mql5.com/en/articles/9926
// https://www.mql5.com/en/articles/9929
checkTimeOffset(TimeCurrent()); // check changes of DST
// cto();
tC = TimeCurrent();
tGMT = TimeCurrent() + OffsetBroker.actOffset; // GMT
tNY = tGMT - (NYShift+DST_USD); // time in New York (EST)
tLon = tGMT - (LondonShift+DST_EUR); // time in London
tFfm = tGMT - (FfmShift+DST_EUR); // time in Frankfurt
tSyd = tGMT - (SidneyShift+DST_AUD); // time in Sidney
tMosc = tGMT - (MoskwaShift+DST_RUS); // time in Moscow
tTok = tGMT - (TokyoShift); // time in Tokyo - no DST
if(print_time==true){
Print("----------------------------------");
Print("Broker: ", tC);
Print("GMT: ", tGMT);
Print("time in New York: ", tNY);
Print("time in London: ", tLon);
Print("time in Frankfurt: ", tFfm);
Print("time in Sidney: ", tSyd);
Print("time in Moscow: ", tMosc);
Print("time in Tokyo: ", tTok);
}
if(time_zone=="NY"){return tNY;}
if(time_zone=="Lon"){return tLon;}
if(time_zone=="Ffm"){return tFfm;}
if(time_zone=="Syd"){return tSyd;}
if(time_zone=="Mosc"){return tMosc;}
if(time_zone=="Tok"){return tTok;}
return NULL;
}
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/articles/9926
// https://www.mql5.com/en/articles/9929
tGIVEN = time_given; //StringToTime(time_given);
checkTimeOffset(tGIVEN); // check changes of DST
// Get GMT:
if(time_zone_known=="GMT" ){tGMT = tGIVEN;}
if(time_zone_known=="Broker" ){tGMT = tGIVEN + OffsetBroker.actOffset;}
if(time_zone_known=="NY" ){tGMT = tGIVEN + (NYShift+DST_USD);}
if(time_zone_known=="Lon" ){tGMT = tGIVEN + (LondonShift+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=="Mosc" ){tGMT = tGIVEN + (MoskwaShift+DST_RUS);}
if(time_zone_known=="Tok" ){tGMT = tGIVEN + (TokyoShift);}
// define the required time:
tREQ = NULL;
if(time_zone_required=="GMT" ){tREQ = tGMT;}
if(time_zone_required=="Broker" ){tREQ = tGMT - OffsetBroker.actOffset;}
if(time_zone_required=="NY" ){tREQ = tGMT - (NYShift+DST_USD);}
if(time_zone_required=="Lon" ){tREQ = tGMT - (LondonShift+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=="Mosc" ){tREQ = tGMT - (MoskwaShift+DST_RUS);}
if(time_zone_required=="Tok" ){tREQ = tGMT - (TokyoShift);}
return tREQ;
}
// Calculte NY close time:
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);
return required_close;
}
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:
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_time = ny_close_in_brokers_time + PeriodSeconds(PERIOD_D1); // ny close tomorrow
if(TimeCurrent()<ny_close_time){
ny_close_time = ny_close_time - PeriodSeconds(PERIOD_D1); // ny close today
}
// Get the number of hours since NY closed:
int shift = iBarShift(symbol, PERIOD_H1, ny_close_time, false) + 1;
shift = shift + (24 * (shift_days - 1)); // shift days if required:
double ny_close = iClose(symbol,PERIOD_H1, shift);
double br_close = iClose(symbol,PERIOD_H1, 1);
if(print_data==true){
Print("shift ",shift);
Print("time_5pm ",time_5pm);
Print("ny_close_in_brokers_time ",ny_close_in_brokers_time);
Print("ny_close_time ",ny_close_time);
Print("ny_close ", ny_close);
Print("br_close ",br_close);
}
return ny_close;
#property library
#include <Trade/Trade.mqh>
#include <MyLibs/Utils/DealingWithTime.mqh>
class TimeZones: public CObject{
protected:
string dt_s;
int len;
string dt_string;
datetime tC, tGMT, tNY, tLon, tFfm, tMosc, tSyd, tTok;
datetime tz_time;
string tz_date;
datetime time_start;
datetime time_end;
bool is_time;
datetime tGIVEN;
datetime tREQ;
datetime tzt;
datetime tz_req;
double ny_daily_close_protected(string symbol, int shift_days, bool print_data=false);
double required_close;
public:
string get_date_string_from_datetime(datetime dt);
datetime get_timezone_time(string time_zone, bool print_time);
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);
};
string TimeZones::get_date_string_from_datetime(datetime dt){
dt_s = TimeToString(dt);
len = StringLen(dt_s);
dt_string = StringSubstr(dt_s, 0, len-5);
return dt_string;
}
datetime TimeZones::get_timezone_time(string time_zone, bool print_time){
// https://www.mql5.com/en/code/45287
// https://www.mql5.com/en/articles/9926
// https://www.mql5.com/en/articles/9929
checkTimeOffset(TimeCurrent()); // check changes of DST
// cto();
tC = TimeCurrent();
tGMT = TimeCurrent() + OffsetBroker.actOffset; // GMT
tNY = tGMT - (NYShift+DST_USD); // time in New York (EST)
tLon = tGMT - (LondonShift+DST_EUR); // time in London
tFfm = tGMT - (FfmShift+DST_EUR); // time in Frankfurt
tSyd = tGMT - (SidneyShift+DST_AUD); // time in Sidney
tMosc = tGMT - (MoskwaShift+DST_RUS); // time in Moscow
tTok = tGMT - (TokyoShift); // time in Tokyo - no DST
if(print_time==true){
Print("----------------------------------");
Print("Broker: ", tC);
Print("GMT: ", tGMT);
Print("time in New York: ", tNY);
Print("time in London: ", tLon);
Print("time in Frankfurt: ", tFfm);
Print("time in Sidney: ", tSyd);
Print("time in Moscow: ", tMosc);
Print("time in Tokyo: ", tTok);
}
if(time_zone=="NY"){return tNY;}
if(time_zone=="Lon"){return tLon;}
if(time_zone=="Ffm"){return tFfm;}
if(time_zone=="Syd"){return tSyd;}
if(time_zone=="Mosc"){return tMosc;}
if(time_zone=="Tok"){return tTok;}
return NULL;
}
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/articles/9926
// https://www.mql5.com/en/articles/9929
tGIVEN = time_given; //StringToTime(time_given);
checkTimeOffset(tGIVEN); // check changes of DST
// Get GMT:
if(time_zone_known=="GMT" ){tGMT = tGIVEN;}
if(time_zone_known=="Broker" ){tGMT = tGIVEN + OffsetBroker.actOffset;}
if(time_zone_known=="NY" ){tGMT = tGIVEN + (NYShift+DST_USD);}
if(time_zone_known=="Lon" ){tGMT = tGIVEN + (LondonShift+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=="Mosc" ){tGMT = tGIVEN + (MoskwaShift+DST_RUS);}
if(time_zone_known=="Tok" ){tGMT = tGIVEN + (TokyoShift);}
// define the required time:
tREQ = NULL;
if(time_zone_required=="GMT" ){tREQ = tGMT;}
if(time_zone_required=="Broker" ){tREQ = tGMT - OffsetBroker.actOffset;}
if(time_zone_required=="NY" ){tREQ = tGMT - (NYShift+DST_USD);}
if(time_zone_required=="Lon" ){tREQ = tGMT - (LondonShift+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=="Mosc" ){tREQ = tGMT - (MoskwaShift+DST_RUS);}
if(time_zone_required=="Tok" ){tREQ = tGMT - (TokyoShift);}
return tREQ;
}
// Calculte NY close time:
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);
return required_close;
}
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:
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_time = ny_close_in_brokers_time + PeriodSeconds(PERIOD_D1); // ny close tomorrow
if(TimeCurrent()<ny_close_time){
ny_close_time = ny_close_time - PeriodSeconds(PERIOD_D1); // ny close today
}
// Get the number of hours since NY closed:
int shift = iBarShift(symbol, PERIOD_H1, ny_close_time, false) + 1;
shift = shift + (24 * (shift_days - 1)); // shift days if required:
double ny_close = iClose(symbol,PERIOD_H1, shift);
double br_close = iClose(symbol,PERIOD_H1, 1);
if(print_data==true){
Print("shift ",shift);
Print("time_5pm ",time_5pm);
Print("ny_close_in_brokers_time ",ny_close_in_brokers_time);
Print("ny_close_time ",ny_close_time);
Print("ny_close ", ny_close);
Print("br_close ",br_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
#include <Trade/Trade.mqh>
#include <MyLibs/TimeZones.mqh>
#include <MyLibs/Myfunctions.mqh>
class CalculatePositionData : public CObject{
protected:
CTrade trade;
TimeZones tz;
CPositionInfo position;
MyFunctions mf;
bool check_lots(double &lots, string symbol);
bool normalise_price(double price, double &normalizedPrice, string symbol);
// double adjusted_point(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 = mf.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 = mf.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 = mf.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 = mf.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;
#property library
#include <Trade/Trade.mqh>
#include <MyLibs/TimeZones.mqh>
#include <MyLibs/Myfunctions.mqh>
class CalculatePositionData : public CObject{
protected:
CTrade trade;
TimeZones tz;
CPositionInfo position;
MyFunctions mf;
bool check_lots(double &lots, string symbol);
bool normalise_price(double price, double &normalizedPrice, string symbol);
// double adjusted_point(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 = mf.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 = mf.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 = mf.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 = mf.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;
}
+210 -210
View File
@@ -1,211 +1,211 @@
#property library
#include <Trade/Trade.mqh>
#include <MyLibs/TradingWindow.mqh>
#include <MyLibs/MyEnums.mqh>
class MyFunctions : public CObject{
protected:
CTrade trade;
TradingWindow tw;
datetime previousTime;
datetime bar_open_time;
public:
void draw_line(double value, string name,color clr);
bool check_indicator_handles(int &indicator_handles[]);
double adjusted_point(string symbol);
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 trade_window(string t1, string t2, string time_zone, bool plot_range_inp=true);
bool in_test_period(MODE_SPLIT_DATA data_period);
void 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){
string a[] = {_Symbol};
ArrayResize(DataArray, ArraySize(a));
for(int i = 0; i < ArraySize(DataArray); i++){
DataArray[i]=a[i];
}
}
if(mode==MULTI_SYM_FX_B5){
string a[] = {"EURUSD", "AUDNZD", "EURGBP", "AUDCAD", "CHFJPY"};
ArrayResize(DataArray, ArraySize(a));
for(int i = 0; i < ArraySize(DataArray); i++){
DataArray[i]=a[i];
}
}
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",};
ArrayResize(DataArray, ArraySize(a));
for(int i = 0; i < ArraySize(DataArray); i++){
DataArray[i]=a[i];
}
}
}
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);
return in_window;
}
//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"){
bar_open_time = iTime(symbol, time_frame, 0);
if(previousTime!=bar_open_time){
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;
}
//if(!mf.in_test_period(data_split_method){return;}
bool MyFunctions::in_test_period(MODE_SPLIT_DATA data_split_method){
string result[];
string string_tc = TimeToString(TimeCurrent());
ushort u_sep = StringGetCharacter(".",0);
int split_string = StringSplit(string_tc, u_sep, result);
bool odd_year = int(result[0]) % 2;
bool odd_month = int(result[1]) % 2;
// get week of the year. rough estimate can be late the first week of jan:
MqlDateTime dt;
TimeToStruct(TimeCurrent(),dt);
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
bool odd_week = iWeek % 2;
if(data_split_method==NO_SPLIT){
return true;
}
if(data_split_method==ODD_YEARS){
if (odd_year){
return true;
}
}
if(data_split_method==EVEN_YEARS){
if (!odd_year){
return true;
}
}
if(data_split_method==ODD_MONTHS){
if (odd_month){
return true;
}
}
if(data_split_method==EVEN_MONTHS){
if (!odd_month){
return true;
}
}
if(data_split_method==ODD_WEEKS){
if (odd_week){
return true;
}
}
if(data_split_method==EVEN_WEEKS){
if (!odd_week){
return true;
}
}
return false;
}
void MyFunctions::draw_line(double value, string name,color clr=clrBlack){
// EG:
// ArrayResize(bar,1000);
// ArraySetAsSeries(bar, true);
// CopyRates(symbol,PERIOD_CURRENT,1,1000,bar);
// double close = bar[0].close;
// draw_line(close,"CLOSE",clrBlue);
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();
}
double MyFunctions::adjusted_point(string symbol){
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
int digits_adjust=1;
if(symbol_digits==3 || symbol_digits==5){
digits_adjust=10;
}
double symbol_point_val = SymbolInfoDouble(symbol,SYMBOL_POINT);
double m_adjusted_point;
m_adjusted_point = symbol_point_val * digits_adjust;
return m_adjusted_point;
}
// price side - 1 for the ask price and 2 for the bid price
double MyFunctions::get_bid_ask_price(string symbol, int price_side){
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double symbol_point = SymbolInfoDouble(symbol, SYMBOL_POINT);
double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
ask = NormalizeDouble(ask, symbol_digits);
double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
bid = NormalizeDouble(bid, symbol_digits);
double price = 0;
if(price_side==1){
price = ask;
}
else if(price_side==2){
price = bid;
}
return price;
#property library
#include <Trade/Trade.mqh>
#include <MyLibs/TradingWindow.mqh>
#include <MyLibs/MyEnums.mqh>
class MyFunctions : public CObject{
protected:
CTrade trade;
TradingWindow tw;
datetime previousTime;
datetime bar_open_time;
public:
void draw_line(double value, string name,color clr);
bool check_indicator_handles(int &indicator_handles[]);
double adjusted_point(string symbol);
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 trade_window(string t1, string t2, string time_zone, bool plot_range_inp=true);
bool in_test_period(MODE_SPLIT_DATA data_period);
void 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){
string a[] = {_Symbol};
ArrayResize(DataArray, ArraySize(a));
for(int i = 0; i < ArraySize(DataArray); i++){
DataArray[i]=a[i];
}
}
if(mode==MULTI_SYM_FX_B5){
string a[] = {"EURUSD", "AUDNZD", "EURGBP", "AUDCAD", "CHFJPY"};
ArrayResize(DataArray, ArraySize(a));
for(int i = 0; i < ArraySize(DataArray); i++){
DataArray[i]=a[i];
}
}
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",};
ArrayResize(DataArray, ArraySize(a));
for(int i = 0; i < ArraySize(DataArray); i++){
DataArray[i]=a[i];
}
}
}
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);
return in_window;
}
//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"){
bar_open_time = iTime(symbol, time_frame, 0);
if(previousTime!=bar_open_time){
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;
}
//if(!mf.in_test_period(data_split_method){return;}
bool MyFunctions::in_test_period(MODE_SPLIT_DATA data_split_method){
string result[];
string string_tc = TimeToString(TimeCurrent());
ushort u_sep = StringGetCharacter(".",0);
int split_string = StringSplit(string_tc, u_sep, result);
bool odd_year = int(result[0]) % 2;
bool odd_month = int(result[1]) % 2;
// get week of the year. rough estimate can be late the first week of jan:
MqlDateTime dt;
TimeToStruct(TimeCurrent(),dt);
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
bool odd_week = iWeek % 2;
if(data_split_method==NO_SPLIT){
return true;
}
if(data_split_method==ODD_YEARS){
if (odd_year){
return true;
}
}
if(data_split_method==EVEN_YEARS){
if (!odd_year){
return true;
}
}
if(data_split_method==ODD_MONTHS){
if (odd_month){
return true;
}
}
if(data_split_method==EVEN_MONTHS){
if (!odd_month){
return true;
}
}
if(data_split_method==ODD_WEEKS){
if (odd_week){
return true;
}
}
if(data_split_method==EVEN_WEEKS){
if (!odd_week){
return true;
}
}
return false;
}
void MyFunctions::draw_line(double value, string name,color clr=clrBlack){
// EG:
// ArrayResize(bar,1000);
// ArraySetAsSeries(bar, true);
// CopyRates(symbol,PERIOD_CURRENT,1,1000,bar);
// double close = bar[0].close;
// draw_line(close,"CLOSE",clrBlue);
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();
}
double MyFunctions::adjusted_point(string symbol){
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
int digits_adjust=1;
if(symbol_digits==3 || symbol_digits==5){
digits_adjust=10;
}
double symbol_point_val = SymbolInfoDouble(symbol,SYMBOL_POINT);
double m_adjusted_point;
m_adjusted_point = symbol_point_val * digits_adjust;
return m_adjusted_point;
}
// price side - 1 for the ask price and 2 for the bid price
double MyFunctions::get_bid_ask_price(string symbol, int price_side){
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double symbol_point = SymbolInfoDouble(symbol, SYMBOL_POINT);
double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
ask = NormalizeDouble(ask, symbol_digits);
double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
bid = NormalizeDouble(bid, symbol_digits);
double price = 0;
if(price_side==1){
price = ask;
}
else if(price_side==2){
price = bid;
}
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
#include <Trade/Trade.mqh>
#include <MyLibs/TimeZones.mqh>
class TradingWindow : public CObject{
protected:
TimeZones tz;
bool in_window;
datetime start_time;
datetime end_time;
public:
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){
datetime _t1 = StringToTime(t1);
datetime _t2 = StringToTime(t2);
if(_t1 > _t2){
_t2 = _t2 + PeriodSeconds(PERIOD_D1);
}
int w_duration = (int)(_t2 - _t1);
// window flag
if(TimeCurrent() >= start_time && TimeCurrent() <= end_time){
in_window = true;
}
// define new window
if(TimeCurrent() >= end_time){
in_window = false;
start_time = tz.timezone_conversions(time_zone, StringToTime(t1), "Broker");
if(TimeCurrent()>=start_time){
start_time += PeriodSeconds(PERIOD_D1);
}
end_time = start_time + w_duration;
if(plot_range){
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;
}
#property library
#include <Trade/Trade.mqh>
#include <MyLibs/TimeZones.mqh>
class TradingWindow : public CObject{
protected:
TimeZones tz;
bool in_window;
datetime start_time;
datetime end_time;
public:
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){
datetime _t1 = StringToTime(t1);
datetime _t2 = StringToTime(t2);
if(_t1 > _t2){
_t2 = _t2 + PeriodSeconds(PERIOD_D1);
}
int w_duration = (int)(_t2 - _t1);
// window flag
if(TimeCurrent() >= start_time && TimeCurrent() <= end_time){
in_window = true;
}
// define new window
if(TimeCurrent() >= end_time){
in_window = false;
start_time = tz.timezone_conversions(time_zone, StringToTime(t1), "Broker");
if(TimeCurrent()>=start_time){
start_time += PeriodSeconds(PERIOD_D1);
}
end_time = start_time + w_duration;
if(plot_range){
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;
}
Binary file not shown.