diff --git a/BacktestUtils/CustomMax.mqh b/BacktestUtils/CustomMax.mqh new file mode 100644 index 0000000..2d411dd --- /dev/null +++ b/BacktestUtils/CustomMax.mqh @@ -0,0 +1,52 @@ +#include + +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; +} diff --git a/BacktestUtils/TestDataSplit.mqh b/BacktestUtils/TestDataSplit.mqh new file mode 100644 index 0000000..9458589 --- /dev/null +++ b/BacktestUtils/TestDataSplit.mqh @@ -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; +} diff --git a/CustomMax.mqh b/CustomMax.mqh deleted file mode 100644 index fb6e8d1..0000000 --- a/CustomMax.mqh +++ /dev/null @@ -1,90 +0,0 @@ -#property library -#include - -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 +#include +#include + +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(lotsmax){ + 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; +} \ No newline at end of file diff --git a/Orders/EntryOrders.mqh b/Orders/EntryOrders.mqh new file mode 100644 index 0000000..e38c2d9 --- /dev/null +++ b/Orders/EntryOrders.mqh @@ -0,0 +1,112 @@ +#include +#include + +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; +} diff --git a/Orders/ExitOrders.mqh b/Orders/ExitOrders.mqh new file mode 100644 index 0000000..e60bc35 --- /dev/null +++ b/Orders/ExitOrders.mqh @@ -0,0 +1,153 @@ +#include +#include +#include + +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; +} diff --git a/Orders/OrderTracker.mqh b/Orders/OrderTracker.mqh new file mode 100644 index 0000000..8ab8eb1 --- /dev/null +++ b/Orders/OrderTracker.mqh @@ -0,0 +1,69 @@ +#include +#include + +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; +} diff --git a/Orders/StopLogic.mqh b/Orders/StopLogic.mqh new file mode 100644 index 0000000..e39e661 --- /dev/null +++ b/Orders/StopLogic.mqh @@ -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; + } +} diff --git a/Orders/TrailingLogic.mqh b/Orders/TrailingLogic.mqh new file mode 100644 index 0000000..88a73c4 --- /dev/null +++ b/Orders/TrailingLogic.mqh @@ -0,0 +1,95 @@ +#include + +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); + } + } + } + } + } +} diff --git a/DrawdownControl.mqh b/RiskManagement/DrawdownControl.mqh similarity index 96% rename from DrawdownControl.mqh rename to RiskManagement/DrawdownControl.mqh index bd8280c..862e27a 100644 --- a/DrawdownControl.mqh +++ b/RiskManagement/DrawdownControl.mqh @@ -1,205 +1,205 @@ -#property library -#include -#include - -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 +#include + +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; +} diff --git a/Strategy/EntryLogic_nnfx.mqh b/Strategy/EntryLogic_nnfx.mqh new file mode 100644 index 0000000..6592bd4 --- /dev/null +++ b/Strategy/EntryLogic_nnfx.mqh @@ -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; + } +}; diff --git a/RangeCalculator.mqh b/Strategy/Range/RangeCalculator.mqh similarity index 96% rename from RangeCalculator.mqh rename to Strategy/Range/RangeCalculator.mqh index aa62fc5..97b71d3 100644 --- a/RangeCalculator.mqh +++ b/Strategy/Range/RangeCalculator.mqh @@ -1,414 +1,414 @@ -#property library -#include -#include - -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 +#include + +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(); +} + + diff --git a/Utils/ChartUtils.mqh b/Utils/ChartUtils.mqh new file mode 100644 index 0000000..79eeaa9 --- /dev/null +++ b/Utils/ChartUtils.mqh @@ -0,0 +1,31 @@ +#include + +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(); +} diff --git a/DealingWithTime.mqh b/Utils/DealingWithTime.mqh similarity index 100% rename from DealingWithTime.mqh rename to Utils/DealingWithTime.mqh diff --git a/MyEnums.mqh b/Utils/Enums.mqh similarity index 82% rename from MyEnums.mqh rename to Utils/Enums.mqh index 015c9b4..71624e3 100644 --- a/MyEnums.mqh +++ b/Utils/Enums.mqh @@ -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 -}; \ No newline at end of file +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 +}; diff --git a/NNFX_TestFileHeader.mqh b/Utils/ExpertFileHeader.mqh similarity index 73% rename from NNFX_TestFileHeader.mqh rename to Utils/ExpertFileHeader.mqh index b630dec..79a697d 100644 --- a/NNFX_TestFileHeader.mqh +++ b/Utils/ExpertFileHeader.mqh @@ -1,28 +1,28 @@ -#include -#include -#include -#include -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 +#include +#include +#include +#include +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 "-----------------------------------------" + + + + + diff --git a/Utils/MarketDataUtils.mqh b/Utils/MarketDataUtils.mqh new file mode 100644 index 0000000..4551312 --- /dev/null +++ b/Utils/MarketDataUtils.mqh @@ -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 +} diff --git a/TimeZones.mqh b/Utils/TimeZones.mqh similarity index 96% rename from TimeZones.mqh rename to Utils/TimeZones.mqh index 1be9dbf..fd41c7a 100644 --- a/TimeZones.mqh +++ b/Utils/TimeZones.mqh @@ -1,144 +1,144 @@ -#property library -#include -#include - -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() +#include + +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() + +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:00–01: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; +} diff --git a/_old/BarUtils.mqh b/_old/BarUtils.mqh new file mode 100644 index 0000000..4900d3c --- /dev/null +++ b/_old/BarUtils.mqh @@ -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; +} + diff --git a/_old/BufferUtils.mqh b/_old/BufferUtils.mqh new file mode 100644 index 0000000..fb3164e --- /dev/null +++ b/_old/BufferUtils.mqh @@ -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; +} diff --git a/CalculatePositionData.mqh b/_old/CalculatePositionData.mqh similarity index 97% rename from CalculatePositionData.mqh rename to _old/CalculatePositionData.mqh index fe1694e..254eda0 100644 --- a/CalculatePositionData.mqh +++ b/_old/CalculatePositionData.mqh @@ -1,278 +1,278 @@ -#property library -#include -#include -#include - -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(lotsmax){ - 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 +#include +#include + +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(lotsmax){ + 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; } \ No newline at end of file diff --git a/MyFunctions.mqh b/_old/MyFunctions.mqh similarity index 96% rename from MyFunctions.mqh rename to _old/MyFunctions.mqh index ee71dc1..f48cbf0 100644 --- a/MyFunctions.mqh +++ b/_old/MyFunctions.mqh @@ -1,211 +1,211 @@ -#property library -#include -#include -#include - -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 +#include +#include + +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; + } \ No newline at end of file diff --git a/OrderManagement.mqh b/_old/OrderManagement.mqh similarity index 97% rename from OrderManagement.mqh rename to _old/OrderManagement.mqh index d3ed70e..5f5b35a 100644 --- a/OrderManagement.mqh +++ b/_old/OrderManagement.mqh @@ -1,556 +1,556 @@ -#property library -#include -#include -#include -#include -#include -#include -#include - -class OrderManagment : public CObject{ - - protected: - CTrade trade; - TimeZones tz; - CalculatePositionData cpd; - CPositionInfo m_position; - COrderInfo m_order; - - double stop_loss; - double take_profit; - ulong posTicket; - int time_difference; - int total_open_buy_orders; - int total_open_sell_orders; - double current_price; - int total_pos; - long position_open_time; - long first_allowed_close_time; - datetime current_bar_open_time; - - 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_nnfx_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_nnfx_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); - bool close_buy_orders(string symbol, bool buy_out, int close_bars, ENUM_TIMEFRAMES close_bar_period, long magic_number); - bool close_sell_orders(string symbol, bool sell_out, int close_bars, ENUM_TIMEFRAMES close_bar_period, long magic_number); - bool first_profitable_close_exit(string symbol, 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 tz, int delay_days, long magic_number); - int count_all_positions(string symbol, long magic_number); - int count_pending_orders(string symbol, ENUM_ORDER_TYPE pendingType, long magic); - 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); - int count_open_positions(string symbol,int order_side, long magic_number); - 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); - }; - -bool OrderManagment::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 == true){ - current_price = SymbolInfoDouble(symbol, SYMBOL_ASK); // ask for buy side - - total_open_buy_orders = count_open_positions(symbol, 1, magic_number); - if(total_open_buy_orders == 0){ - - stop_loss = cpd.calculate_stoploss(symbol, current_price, 1, _sl_mode, sl_var, atr_period); - take_profit = cpd.calculate_take_profit(symbol, current_price, stop_loss, 1, _tp_mode, tp_var, atr_period); - - double sl_distance = current_price-stop_loss; - double lots = cpd.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 OrderManagment::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 == true){ - - // if(!SymbolInfoTick(symbol,currentTick)){Print("FAILED TO GET TICK:", symbol);return false;} - current_price = SymbolInfoDouble(symbol, SYMBOL_BID); // bid for sell side - - total_open_sell_orders = count_open_positions(symbol, 2, magic_number); - if(total_open_sell_orders == 0){ - - stop_loss = cpd.calculate_stoploss(symbol, current_price, 2, _sl_mode, sl_var, atr_period); - take_profit = cpd.calculate_take_profit(symbol, current_price, stop_loss, 2, _tp_mode, tp_var, atr_period); - - double sl_distance = stop_loss-current_price; - double lots = cpd.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 OrderManagment::open_nnfx_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 == true){ - current_price = SymbolInfoDouble(symbol, SYMBOL_ASK); // ask for buy side - - total_open_buy_orders = count_open_positions(symbol, 1, magic_number); - if(total_open_buy_orders == 0){ - - stop_loss = cpd.calculate_stoploss(symbol, current_price, 1, _sl_mode, sl_var, atr_period); - take_profit = cpd.calculate_take_profit(symbol, current_price, stop_loss, 1, _tp_mode, tp_var, atr_period); - - double sl_distance = current_price-stop_loss; - double lots = cpd.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var/2); - - 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); - trade.PositionOpen(symbol,ORDER_TYPE_BUY,lots,current_price,stop_loss,0,comment); - } - } - return true; -} - - -bool OrderManagment::open_nnfx_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 == true){ - - // if(!SymbolInfoTick(symbol,currentTick)){Print("FAILED TO GET TICK:", symbol);return false;} - current_price = SymbolInfoDouble(symbol, SYMBOL_BID); // bid for sell side - - total_open_sell_orders = count_open_positions(symbol, 2, magic_number); - if(total_open_sell_orders == 0){ - - stop_loss = cpd.calculate_stoploss(symbol, current_price, 2, _sl_mode, sl_var, atr_period); - take_profit = cpd.calculate_take_profit(symbol, current_price, stop_loss, 2, _tp_mode, tp_var, atr_period); - - double sl_distance = stop_loss-current_price; - double lots = cpd.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); - trade.PositionOpen(symbol,ORDER_TYPE_SELL,lots,current_price,stop_loss,0,comment); - } - } - return true; -} -// some usfull comment here -bool OrderManagment::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 == true){ - - total_open_buy_orders = count_open_positions(symbol, 1, magic_number); - if(total_open_buy_orders == 0){ - - stop_loss = cpd.calculate_stoploss(symbol, entry_price, 1, _sl_mode, sl_var, atr_period); - take_profit = cpd.calculate_take_profit(symbol, entry_price, stop_loss, 1, _tp_mode, tp_var, atr_period); - - double sl_distance = entry_price-stop_loss; - double lots = cpd.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 OrderManagment::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 == true){ - - total_open_sell_orders = count_open_positions(symbol, 2, magic_number); - if(total_open_sell_orders == 0){ - - stop_loss = cpd.calculate_stoploss(symbol, entry_price, 2, _sl_mode, sl_var, atr_period); - take_profit = cpd.calculate_take_profit(symbol, entry_price, stop_loss, 2, _tp_mode, tp_var, atr_period); - - double sl_distance = stop_loss-entry_price; - double lots = cpd.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; -} - -bool OrderManagment::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){ - - time_difference = Bars(symbol, close_bar_period, PositionGetInteger(POSITION_TIME), TimeCurrent()) - 1; - - if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){ - - if(condition){ - trade.PositionClose(posTicket); - } - - if(close_bars > 0){ - if(time_difference >= close_bars){ - trade.PositionClose(posTicket); - } - } - } - } - } - return true; -} - -bool OrderManagment::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){ - - time_difference = Bars(symbol, close_bar_period, PositionGetInteger(POSITION_TIME), TimeCurrent()) - 1; - - if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){ - - if(condition){trade.PositionClose(posTicket);} - - if(close_bars > 0){ - if(time_difference >= close_bars){ - trade.PositionClose(posTicket); - } - } - } - } - } - return true; -} - -// order_side int must be 1 for BUY or 2 for SELL -int OrderManagment::count_open_positions(string symbol,int order_side, long magic_number){ - - - int count = 0; - bool match = (PositionGetInteger(POSITION_MAGIC)==magic_number); - - for(int i = PositionsTotal()-1; i >=0; i--){ - ulong ticket = PositionGetTicket(i); - - if(PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC)==magic_number){ - - // Count only Buy orders: - if(order_side == 1){ - if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){ - count = count + 1; - } - } - - // Count only Sell orders: - if(order_side == 2){ - if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){ - count = count + 1; - } - } - } - } - return count; -} - -int OrderManagment::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 = count + 1; - } - } - return count; -} - -bool OrderManagment::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){ - - // datetime broker_close_time = tz.timezone_conversions(cw_tzone, StringToTime(exit_time), "Broker"); - if(TimeCurrent()>= exit_time){ - - if(PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number){ - - if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){ - trade.PositionClose(posTicket); - } - - // Sell orders: - if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){ - trade.PositionClose(posTicket); - } - } - } - } - } - } -return true; -} - -bool OrderManagment::daily_timed_profit_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, string exit_time, string cw_tzone, int delay_days, long magic_number){ - - // om.daily_timed_profit_exit(_Symbol, PERIOD_CURRENT, "16:45", "17:00", "NY", 1, inp_magic); - - 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){ - - 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); // shift 1 because 0 = live candle. - double trading_cost = cpd.calculate_trading_cost(symbol, posTicket); - - - if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){ - if(bar_close > (position_open_price + spread + trading_cost)){ - trade.PositionClose(posTicket); - - } - } - - // Sell orders: - if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){ - if(bar_close < position_open_price - spread - trading_cost){ - trade.PositionClose(posTicket); - } - } - } - } - } - } - } -return true; -} - - -bool OrderManagment::first_profitable_close_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, long magic_number){ - // om.first_profitable_close_exit(_Symbol, PERIOD_CURRENT, inp_magic); - - position_open_time = PositionGetInteger(POSITION_TIME); - first_allowed_close_time = position_open_time + PeriodSeconds(close_bar_period); - - if((int)position_open_time>0){ - - if(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); // shift 1 because 0 = live candle. - double trading_cost = cpd.calculate_trading_cost(symbol, posTicket); - - - if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){ - if(bar_close > (position_open_price + spread + trading_cost)){ - trade.PositionClose(posTicket); - - } - } - - // Sell orders: - if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){ - if(bar_close < position_open_price - spread - trading_cost){ - trade.PositionClose(posTicket); - } - } - } - } - } - } -return true; -} - -// e.g. int buy_stop_count = om.count_pending_orders(symbol, ORDER_TYPE_BUY_STOP, inp_magic); -// order types: ORDER_TYPE_BUY_LIMIT, ORDER_TYPE_SELL_LIMIT, ORDER_TYPE_BUY_STOP, ORDER_TYPE_SELL_STOP -int OrderManagment::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); -} - -void OrderManagment::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 = SymbolInfoDouble(symbol, SYMBOL_ASK); - ask = NormalizeDouble(ask, symbol_digits); - - double bid = SymbolInfoDouble(symbol, SYMBOL_BID); - bid = NormalizeDouble(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){ - - if(bid > position_open_price + be_trigger_points * symbol_point){ - - double sl = position_open_price + be_puffer * symbol_point; - sl = NormalizeDouble(sl, symbol_digits); - if(sl > position_sl){ - - if(trade.PositionModify(ticket, sl, position_tp)){ - Print("-----------------------------------Stop moved to break even"); - } - } - } - } - else if(position_type == POSITION_TYPE_SELL){ - - if(ask < position_open_price - be_trigger_points * symbol_point){ - - double sl = position_open_price - be_puffer * symbol_point; - sl = NormalizeDouble(sl, symbol_digits); - if(sl < position_sl){ - - if(trade.PositionModify(ticket, sl, position_tp)){ - Print("-----------------------------------Stop moved to break even"); - } - } - } - } - } - } - } - } -} - - -void OrderManagment::nnfx_trailing_stop(string symbol, double sl_var, double tp_var, double atr_value, ulong magic_number){ - - MyFunctions mf3; - - 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 = SymbolInfoDouble(symbol, SYMBOL_ASK); - ask = NormalizeDouble(ask, symbol_digits); - - double bid = SymbolInfoDouble(symbol, SYMBOL_BID); - bid = NormalizeDouble(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){ - - if(bid > position_open_price + (atr_value * tp_var)){ - - double sl = bid - (atr_value * sl_var); - sl = NormalizeDouble(sl, symbol_digits); - if(sl > (position_sl + (atr_value * 0.5))){ - - if(trade.PositionModify(ticket, sl, position_tp)){ - - } - } - } - } - - - else if(position_type == POSITION_TYPE_SELL){ - - if(ask < position_open_price - (atr_value * tp_var)){ - - double sl = ask + (atr_value * sl_var); - sl = NormalizeDouble(sl, symbol_digits); - if(sl < (position_sl + (atr_value * 0.5))){ - - if(trade.PositionModify(ticket, sl, position_tp)){ - } - } - } - } - } - } - - - } -} - -double OrderManagment::sl_specified_value_switch(string _sl_mode, double _inp_sl_var, double value){ - double sl = 0; - if(_sl_mode=="SL_SPECIFIED_VALUE"){sl = value;} - if(_sl_mode!="SL_SPECIFIED_VALUE"){sl = _inp_sl_var;} - return sl; -} -double OrderManagment::tp_specified_value_switch(string _tp_mode, double _inp_tp_var, double value){ - double tp = 0; - if(_tp_mode=="SL_SPECIFIED_VALUE"){tp = value;} - if(_tp_mode!="SL_SPECIFIED_VALUE"){tp = _inp_tp_var;} - return tp; +#property library +#include +#include +#include +#include +#include +#include +#include + +class OrderManagment : public CObject{ + + protected: + CTrade trade; + TimeZones tz; + CalculatePositionData cpd; + CPositionInfo m_position; + COrderInfo m_order; + + double stop_loss; + double take_profit; + ulong posTicket; + int time_difference; + int total_open_buy_orders; + int total_open_sell_orders; + double current_price; + int total_pos; + long position_open_time; + long first_allowed_close_time; + datetime current_bar_open_time; + + 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_nnfx_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_nnfx_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); + bool close_buy_orders(string symbol, bool buy_out, int close_bars, ENUM_TIMEFRAMES close_bar_period, long magic_number); + bool close_sell_orders(string symbol, bool sell_out, int close_bars, ENUM_TIMEFRAMES close_bar_period, long magic_number); + bool first_profitable_close_exit(string symbol, 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 tz, int delay_days, long magic_number); + int count_all_positions(string symbol, long magic_number); + int count_pending_orders(string symbol, ENUM_ORDER_TYPE pendingType, long magic); + 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); + int count_open_positions(string symbol,int order_side, long magic_number); + 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); + }; + +bool OrderManagment::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 == true){ + current_price = SymbolInfoDouble(symbol, SYMBOL_ASK); // ask for buy side + + total_open_buy_orders = count_open_positions(symbol, 1, magic_number); + if(total_open_buy_orders == 0){ + + stop_loss = cpd.calculate_stoploss(symbol, current_price, 1, _sl_mode, sl_var, atr_period); + take_profit = cpd.calculate_take_profit(symbol, current_price, stop_loss, 1, _tp_mode, tp_var, atr_period); + + double sl_distance = current_price-stop_loss; + double lots = cpd.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 OrderManagment::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 == true){ + + // if(!SymbolInfoTick(symbol,currentTick)){Print("FAILED TO GET TICK:", symbol);return false;} + current_price = SymbolInfoDouble(symbol, SYMBOL_BID); // bid for sell side + + total_open_sell_orders = count_open_positions(symbol, 2, magic_number); + if(total_open_sell_orders == 0){ + + stop_loss = cpd.calculate_stoploss(symbol, current_price, 2, _sl_mode, sl_var, atr_period); + take_profit = cpd.calculate_take_profit(symbol, current_price, stop_loss, 2, _tp_mode, tp_var, atr_period); + + double sl_distance = stop_loss-current_price; + double lots = cpd.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 OrderManagment::open_nnfx_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 == true){ + current_price = SymbolInfoDouble(symbol, SYMBOL_ASK); // ask for buy side + + total_open_buy_orders = count_open_positions(symbol, 1, magic_number); + if(total_open_buy_orders == 0){ + + stop_loss = cpd.calculate_stoploss(symbol, current_price, 1, _sl_mode, sl_var, atr_period); + take_profit = cpd.calculate_take_profit(symbol, current_price, stop_loss, 1, _tp_mode, tp_var, atr_period); + + double sl_distance = current_price-stop_loss; + double lots = cpd.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var/2); + + 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); + trade.PositionOpen(symbol,ORDER_TYPE_BUY,lots,current_price,stop_loss,0,comment); + } + } + return true; +} + + +bool OrderManagment::open_nnfx_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 == true){ + + // if(!SymbolInfoTick(symbol,currentTick)){Print("FAILED TO GET TICK:", symbol);return false;} + current_price = SymbolInfoDouble(symbol, SYMBOL_BID); // bid for sell side + + total_open_sell_orders = count_open_positions(symbol, 2, magic_number); + if(total_open_sell_orders == 0){ + + stop_loss = cpd.calculate_stoploss(symbol, current_price, 2, _sl_mode, sl_var, atr_period); + take_profit = cpd.calculate_take_profit(symbol, current_price, stop_loss, 2, _tp_mode, tp_var, atr_period); + + double sl_distance = stop_loss-current_price; + double lots = cpd.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); + trade.PositionOpen(symbol,ORDER_TYPE_SELL,lots,current_price,stop_loss,0,comment); + } + } + return true; +} +// some usfull comment here +bool OrderManagment::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 == true){ + + total_open_buy_orders = count_open_positions(symbol, 1, magic_number); + if(total_open_buy_orders == 0){ + + stop_loss = cpd.calculate_stoploss(symbol, entry_price, 1, _sl_mode, sl_var, atr_period); + take_profit = cpd.calculate_take_profit(symbol, entry_price, stop_loss, 1, _tp_mode, tp_var, atr_period); + + double sl_distance = entry_price-stop_loss; + double lots = cpd.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 OrderManagment::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 == true){ + + total_open_sell_orders = count_open_positions(symbol, 2, magic_number); + if(total_open_sell_orders == 0){ + + stop_loss = cpd.calculate_stoploss(symbol, entry_price, 2, _sl_mode, sl_var, atr_period); + take_profit = cpd.calculate_take_profit(symbol, entry_price, stop_loss, 2, _tp_mode, tp_var, atr_period); + + double sl_distance = stop_loss-entry_price; + double lots = cpd.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; +} + +bool OrderManagment::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){ + + time_difference = Bars(symbol, close_bar_period, PositionGetInteger(POSITION_TIME), TimeCurrent()) - 1; + + if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){ + + if(condition){ + trade.PositionClose(posTicket); + } + + if(close_bars > 0){ + if(time_difference >= close_bars){ + trade.PositionClose(posTicket); + } + } + } + } + } + return true; +} + +bool OrderManagment::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){ + + time_difference = Bars(symbol, close_bar_period, PositionGetInteger(POSITION_TIME), TimeCurrent()) - 1; + + if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){ + + if(condition){trade.PositionClose(posTicket);} + + if(close_bars > 0){ + if(time_difference >= close_bars){ + trade.PositionClose(posTicket); + } + } + } + } + } + return true; +} + +// order_side int must be 1 for BUY or 2 for SELL +int OrderManagment::count_open_positions(string symbol,int order_side, long magic_number){ + + + int count = 0; + bool match = (PositionGetInteger(POSITION_MAGIC)==magic_number); + + for(int i = PositionsTotal()-1; i >=0; i--){ + ulong ticket = PositionGetTicket(i); + + if(PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC)==magic_number){ + + // Count only Buy orders: + if(order_side == 1){ + if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){ + count = count + 1; + } + } + + // Count only Sell orders: + if(order_side == 2){ + if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){ + count = count + 1; + } + } + } + } + return count; +} + +int OrderManagment::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 = count + 1; + } + } + return count; +} + +bool OrderManagment::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){ + + // datetime broker_close_time = tz.timezone_conversions(cw_tzone, StringToTime(exit_time), "Broker"); + if(TimeCurrent()>= exit_time){ + + if(PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number){ + + if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){ + trade.PositionClose(posTicket); + } + + // Sell orders: + if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){ + trade.PositionClose(posTicket); + } + } + } + } + } + } +return true; +} + +bool OrderManagment::daily_timed_profit_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, string exit_time, string cw_tzone, int delay_days, long magic_number){ + + // om.daily_timed_profit_exit(_Symbol, PERIOD_CURRENT, "16:45", "17:00", "NY", 1, inp_magic); + + 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){ + + 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); // shift 1 because 0 = live candle. + double trading_cost = cpd.calculate_trading_cost(symbol, posTicket); + + + if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){ + if(bar_close > (position_open_price + spread + trading_cost)){ + trade.PositionClose(posTicket); + + } + } + + // Sell orders: + if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){ + if(bar_close < position_open_price - spread - trading_cost){ + trade.PositionClose(posTicket); + } + } + } + } + } + } + } +return true; +} + + +bool OrderManagment::first_profitable_close_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, long magic_number){ + // om.first_profitable_close_exit(_Symbol, PERIOD_CURRENT, inp_magic); + + position_open_time = PositionGetInteger(POSITION_TIME); + first_allowed_close_time = position_open_time + PeriodSeconds(close_bar_period); + + if((int)position_open_time>0){ + + if(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); // shift 1 because 0 = live candle. + double trading_cost = cpd.calculate_trading_cost(symbol, posTicket); + + + if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){ + if(bar_close > (position_open_price + spread + trading_cost)){ + trade.PositionClose(posTicket); + + } + } + + // Sell orders: + if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){ + if(bar_close < position_open_price - spread - trading_cost){ + trade.PositionClose(posTicket); + } + } + } + } + } + } +return true; +} + +// e.g. int buy_stop_count = om.count_pending_orders(symbol, ORDER_TYPE_BUY_STOP, inp_magic); +// order types: ORDER_TYPE_BUY_LIMIT, ORDER_TYPE_SELL_LIMIT, ORDER_TYPE_BUY_STOP, ORDER_TYPE_SELL_STOP +int OrderManagment::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); +} + +void OrderManagment::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 = SymbolInfoDouble(symbol, SYMBOL_ASK); + ask = NormalizeDouble(ask, symbol_digits); + + double bid = SymbolInfoDouble(symbol, SYMBOL_BID); + bid = NormalizeDouble(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){ + + if(bid > position_open_price + be_trigger_points * symbol_point){ + + double sl = position_open_price + be_puffer * symbol_point; + sl = NormalizeDouble(sl, symbol_digits); + if(sl > position_sl){ + + if(trade.PositionModify(ticket, sl, position_tp)){ + Print("-----------------------------------Stop moved to break even"); + } + } + } + } + else if(position_type == POSITION_TYPE_SELL){ + + if(ask < position_open_price - be_trigger_points * symbol_point){ + + double sl = position_open_price - be_puffer * symbol_point; + sl = NormalizeDouble(sl, symbol_digits); + if(sl < position_sl){ + + if(trade.PositionModify(ticket, sl, position_tp)){ + Print("-----------------------------------Stop moved to break even"); + } + } + } + } + } + } + } + } +} + + +void OrderManagment::nnfx_trailing_stop(string symbol, double sl_var, double tp_var, double atr_value, ulong magic_number){ + + MyFunctions mf3; + + 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 = SymbolInfoDouble(symbol, SYMBOL_ASK); + ask = NormalizeDouble(ask, symbol_digits); + + double bid = SymbolInfoDouble(symbol, SYMBOL_BID); + bid = NormalizeDouble(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){ + + if(bid > position_open_price + (atr_value * tp_var)){ + + double sl = bid - (atr_value * sl_var); + sl = NormalizeDouble(sl, symbol_digits); + if(sl > (position_sl + (atr_value * 0.5))){ + + if(trade.PositionModify(ticket, sl, position_tp)){ + + } + } + } + } + + + else if(position_type == POSITION_TYPE_SELL){ + + if(ask < position_open_price - (atr_value * tp_var)){ + + double sl = ask + (atr_value * sl_var); + sl = NormalizeDouble(sl, symbol_digits); + if(sl < (position_sl + (atr_value * 0.5))){ + + if(trade.PositionModify(ticket, sl, position_tp)){ + } + } + } + } + } + } + + + } +} + +double OrderManagment::sl_specified_value_switch(string _sl_mode, double _inp_sl_var, double value){ + double sl = 0; + if(_sl_mode=="SL_SPECIFIED_VALUE"){sl = value;} + if(_sl_mode!="SL_SPECIFIED_VALUE"){sl = _inp_sl_var;} + return sl; +} +double OrderManagment::tp_specified_value_switch(string _tp_mode, double _inp_tp_var, double value){ + double tp = 0; + if(_tp_mode=="SL_SPECIFIED_VALUE"){tp = value;} + if(_tp_mode!="SL_SPECIFIED_VALUE"){tp = _inp_tp_var;} + return tp; } \ No newline at end of file diff --git a/_old/SymbolUtils.mqh b/_old/SymbolUtils.mqh new file mode 100644 index 0000000..3212e8a --- /dev/null +++ b/_old/SymbolUtils.mqh @@ -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 +} diff --git a/TradingWindow.mqh b/_old/TradingWindow.mqh similarity index 96% rename from TradingWindow.mqh rename to _old/TradingWindow.mqh index 4c37219..4c72a94 100644 --- a/TradingWindow.mqh +++ b/_old/TradingWindow.mqh @@ -1,65 +1,65 @@ -#property library -#include -#include - -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 +#include + +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; +} + + diff --git a/_old/_MyLibs_old.zip b/_old/_MyLibs_old.zip new file mode 100644 index 0000000..a1c1ea6 Binary files /dev/null and b/_old/_MyLibs_old.zip differ