From 890eaf31f55fbe1450d0a6c84f3e180c8d37e03d Mon Sep 17 00:00:00 2001 From: Matt Corcoran Date: Fri, 25 Oct 2024 12:08:52 +0200 Subject: [PATCH] first push all - just my code --- CalculatePositionData.mqh | 278 ++++++++++++++++++++++++ CustomMax.mqh | 62 ++++++ DealingWithTime.mqh | Bin 0 -> 64898 bytes DrawdownControl.mqh | 205 ++++++++++++++++++ MyEnums.mqh | 38 ++++ MyFunctions.mqh | 171 +++++++++++++++ OrderManagement.mqh | 439 ++++++++++++++++++++++++++++++++++++++ RangeCalculator.mqh | 414 +++++++++++++++++++++++++++++++++++ TimeZones.mqh | 168 +++++++++++++++ TradingWindow.mqh | 65 ++++++ 10 files changed, 1840 insertions(+) create mode 100644 CalculatePositionData.mqh create mode 100644 CustomMax.mqh create mode 100644 DealingWithTime.mqh create mode 100644 DrawdownControl.mqh create mode 100644 MyEnums.mqh create mode 100644 MyFunctions.mqh create mode 100644 OrderManagement.mqh create mode 100644 RangeCalculator.mqh create mode 100644 TimeZones.mqh create mode 100644 TradingWindow.mqh diff --git a/CalculatePositionData.mqh b/CalculatePositionData.mqh new file mode 100644 index 0000000..fe1694e --- /dev/null +++ b/CalculatePositionData.mqh @@ -0,0 +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; +} \ No newline at end of file diff --git a/CustomMax.mqh b/CustomMax.mqh new file mode 100644 index 0000000..4e1fdc7 --- /dev/null +++ b/CustomMax.mqh @@ -0,0 +1,62 @@ +#property library +#include + +enum CUSTOM_MAX_TYPE{ + CM_WIN_LOSS_RATIO, + CM_WIN_PERCENT, + CM_WIN_PERCENT_200T +}; + +class CustomMax : public CObject{ + + protected: + double custom_criteria; + + double CustomMax::win_loss_ratio(); + double CustomMax::win_percent(); + double CustomMax::win_percent_min_trades_200(); + + 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(); + } + if(cm_type==CM_WIN_PERCENT_200T){ + custom_criteria = win_percent_min_trades_200(); + } + 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(){ + double wins = TesterStatistics(STAT_PROFIT_TRADES); + double total_trades = TesterStatistics(STAT_TRADES); + return wins / total_trades * 100; +} + +double CustomMax::win_percent_min_trades_200(){ + double wins = TesterStatistics(STAT_PROFIT_TRADES); + double total_trades = TesterStatistics(STAT_TRADES); + + if(total_trades<200){ + return 0; + } + + else { + return wins / total_trades * 100; + } +} \ No newline at end of file diff --git a/DealingWithTime.mqh b/DealingWithTime.mqh new file mode 100644 index 0000000000000000000000000000000000000000..0492252baaab6a3e1729aae54c8753e013109677 GIT binary patch literal 64898 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 literal 0 HcmV?d00001 diff --git a/DrawdownControl.mqh b/DrawdownControl.mqh new file mode 100644 index 0000000..bd8280c --- /dev/null +++ b/DrawdownControl.mqh @@ -0,0 +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; +} diff --git a/MyEnums.mqh b/MyEnums.mqh new file mode 100644 index 0000000..7f1f029 --- /dev/null +++ b/MyEnums.mqh @@ -0,0 +1,38 @@ +#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 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 +}; \ No newline at end of file diff --git a/MyFunctions.mqh b/MyFunctions.mqh new file mode 100644 index 0000000..9634c92 --- /dev/null +++ b/MyFunctions.mqh @@ -0,0 +1,171 @@ +#property library +#include +#include + +class MyFunctions : public CObject{ + + protected: + CTrade trade; + TradingWindow tw; + datetime previousTime; + datetime bar_open_time; + + public: + bool is_new_daily_bar(string symbol, datetime start_time); + double period_high(string symbol, int periods, int shift); + double period_low(string symbol, int periods, int shift); + 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); + bool trade_window(string t1, string t2, string time_zone, bool plot_range_inp=true); + +}; + +bool MyFunctions::trade_window(string t1, string t2, string time_zone="Broker", bool plot_range_inp=true){ + bool in_window = tw.define_window(t1, t2, time_zone, plot_range_inp); + return in_window; +} + +//if(!mf.is_new_daily_bar(symbol, PERIOD_M1)){return;} +bool MyFunctions::is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame){ + bar_open_time = iTime(symbol,time_frame,0); + if(previousTime!=bar_open_time){ + previousTime=bar_open_time; + return true; + } + return false; +} + +// e.g. if(!mf.is_new_daily_bar(symbol, StringToTime("00:06"))){return;} +bool MyFunctions::is_new_daily_bar(string symbol, datetime start_time){ + // https://www.youtube.com/watch?v=9BdnTcGrlUM (m-25:00) + bar_open_time = iTime(symbol,PERIOD_D1,0); + if(previousTime!=bar_open_time && TimeCurrent() > start_time){ + previousTime=bar_open_time; + return true; + } + return false; +} + + +double MyFunctions::period_high(string symbol, int periods, int shift){ + + double highs[]; + ArraySetAsSeries(highs,true); + CopyHigh(symbol,PERIOD_CURRENT,1,periods+1,highs); + + double high = 0; + high=highs[shift]; + for(int i=shift; ilows[i]){ + low=lows[i]; + } + } + return(low); +} + +void MyFunctions::draw_line(double value, string name,color clr){ + // 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(); +} + +bool MyFunctions::check_indicator_handles(int &indicator_handles[]){ + // TODO check if working before implementaion: + // e.g. call via: + // int indicator_handles[] = {handle1, handle2, handle..}; + // check_indicator_handles(indicator_handles); + + for(int i =0; i < ArraySize(indicator_handles); i++){ + + if(indicator_handles[i] == INVALID_HANDLE){ + Alert("Failed to create handle"); return false; + }; + } + + return true; +} + +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/OrderManagement.mqh new file mode 100644 index 0000000..46f1be4 --- /dev/null +++ b/OrderManagement.mqh @@ -0,0 +1,439 @@ +#property library +#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_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); + }; + +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; +} + +// 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){ + + 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){ + for(int i = PositionsTotal()-1; i >=0; i--){ + + 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"); + } + } + } + } + } + } + } + } +} + +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/RangeCalculator.mqh b/RangeCalculator.mqh new file mode 100644 index 0000000..aa62fc5 --- /dev/null +++ b/RangeCalculator.mqh @@ -0,0 +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(); +} + + diff --git a/TimeZones.mqh b/TimeZones.mqh new file mode 100644 index 0000000..340644c --- /dev/null +++ b/TimeZones.mqh @@ -0,0 +1,168 @@ +#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; +} + +// bool TimeZones::trading_window(string time_zone, string start_time, string end_time){ +// // https://www.youtube.com/watch?v=V_qh7sTbl80 +// // e.g: +// // bool trade_window = trading_window(x,x,x); +// // bool long_in = trade_window +// // && close < ma[0]; + +// tz_time = get_timezone_time(time_zone, false); +// Print(tz_time); + +// // Get the current date for the required time zone: +// tz_date = get_date_string_from_datetime(tz_time); + +// // Define the start and end times on correct date: +// time_start = StringToTime(tz_date + start_time); +// time_end = StringToTime(tz_date + end_time); + +// if(time_start>time_end){ +// time_start = time_start - PeriodSeconds(PERIOD_D1); +// } +// is_time = tz_time >= time_start && tz_time < time_end; + +// return is_time; +// } + +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 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; +} + +