diff --git a/BacktestUtils/CustomMax.mqh b/BacktestUtils/CustomMax.mqh index 2d411dd..3544ae9 100644 --- a/BacktestUtils/CustomMax.mqh +++ b/BacktestUtils/CustomMax.mqh @@ -1,10 +1,25 @@ #include +// --------------------------------------------------------------------- +// ENUM: CUSTOM_MAX_TYPE +// --------------------------------------------------------------------- +// Defines the types of custom performance criteria that can be used +// for calculating max optimization targets. +// +// Values: +// - CM_WIN_LOSS_RATIO : Use win/loss ratio. +// - CM_WIN_PERCENT : Use win percentage. +// --------------------------------------------------------------------- enum CUSTOM_MAX_TYPE { CM_WIN_LOSS_RATIO, CM_WIN_PERCENT }; +// --------------------------------------------------------------------- +// CLASS: CustomMax +// --------------------------------------------------------------------- +// Calculates custom performance criteria for use in optimizations. +// --------------------------------------------------------------------- class CustomMax : public CObject { protected: double custom_criteria; @@ -16,6 +31,17 @@ public: double calculate_custom_criteria(CUSTOM_MAX_TYPE cm_type, int min_trades = 0); }; +// --------------------------------------------------------------------- +// Calculates the selected custom criteria metric. +// +// Parameters: +// - cm_type : The selected criteria type to calculate. +// - min_trades : Optional minimum number of trades (default 0). +// +// Logic: +// - Calls the appropriate private method based on cm_type. +// - Returns the resulting metric (or 0 if invalid). +// --------------------------------------------------------------------- double CustomMax::calculate_custom_criteria(CUSTOM_MAX_TYPE cm_type, int min_trades) { switch(cm_type) { case CM_WIN_LOSS_RATIO: @@ -31,22 +57,49 @@ double CustomMax::calculate_custom_criteria(CUSTOM_MAX_TYPE cm_type, int min_tra return custom_criteria; } -// Returns the win/loss ratio, with min trades check +// --------------------------------------------------------------------- +// Calculates win/loss ratio with a minimum trade count check. +// +// Parameters: +// - min_required_trades : Minimum number of trades required. +// +// Logic: +// - Returns wins / losses if minimum is met. +// - Prevents division by zero. +// --------------------------------------------------------------------- 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 + + if ((min_required_trades > 0 && total_trades < min_required_trades) || total_trades == 0) + return 0; + if (losses == 0) + return 0; + return wins / losses; } -// Returns the win percentage, with min trades check +// --------------------------------------------------------------------- +// Calculates win percentage with a minimum trade count check. +// +// Parameters: +// - min_required_trades : Minimum number of trades required. +// +// Logic: +// - Returns (wins / total) * 100 if minimum is met. +// - Filters out invalid math results. +// --------------------------------------------------------------------- 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; + + 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 index 9458589..6efa791 100644 --- a/BacktestUtils/TestDataSplit.mqh +++ b/BacktestUtils/TestDataSplit.mqh @@ -1,4 +1,18 @@ -enum MODE_SPLIT_DATA{ +// --------------------------------------------------------------------- +// ENUM: MODE_SPLIT_DATA +// --------------------------------------------------------------------- +// Defines how to split data during testing based on time attributes. +// +// Values: +// - NO_SPLIT : Do not split, always return true. +// - ODD_YEARS : Include only odd-numbered years. +// - EVEN_YEARS : Include only even-numbered years. +// - ODD_MONTHS : Include only odd-numbered months. +// - EVEN_MONTHS : Include only even-numbered months. +// - ODD_WEEKS : Include only odd-numbered weeks. +// - EVEN_WEEKS : Include only even-numbered weeks. +// --------------------------------------------------------------------- +enum MODE_SPLIT_DATA { NO_SPLIT, ODD_YEARS, EVEN_YEARS, @@ -8,44 +22,60 @@ enum MODE_SPLIT_DATA{ EVEN_WEEKS }; +// --------------------------------------------------------------------- +// CLASS: TestDataSplit +// --------------------------------------------------------------------- +// Provides logic to determine whether the current date falls within +// a selected split group for testing or optimization purposes. +// --------------------------------------------------------------------- class TestDataSplit { public: - bool in_test_period(MODE_SPLIT_DATA data_split_method); + bool in_test_period(MODE_SPLIT_DATA data_split_method); }; +// --------------------------------------------------------------------- +// Determines whether the current time falls in the selected group. +// +// Parameters: +// - data_split_method : Enum value defining the split strategy. +// +// Logic: +// - Extracts current date components (year, month, ISO week). +// - Returns true if current time matches the given split rule. +// --------------------------------------------------------------------- bool TestDataSplit::in_test_period(MODE_SPLIT_DATA data_split_method) { - string result[]; - string string_tc = TimeToString(TimeCurrent()); + 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); + // 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; + 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; + // Calculate week of the year (approximate ISO week) + MqlDateTime dt; + TimeToStruct(TimeCurrent(), dt); + int i_day = (dt.day_of_week + 6) % 7 + 1; // Convert to 1=Mon,...,7=Sun + int i_week = (dt.day_of_year - i_day + 10) / 7; // Approximate ISO week number + bool odd_week = i_week % 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; + // 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; + return false; } diff --git a/Orders/AdjustPosition.mqh b/Orders/AdjustPosition.mqh index 1209aea..b2d9520 100644 --- a/Orders/AdjustPosition.mqh +++ b/Orders/AdjustPosition.mqh @@ -1,24 +1,35 @@ #include #include +// --------------------------------------------------------------------- +// GLOBALS +// --------------------------------------------------------------------- CTrade trade; AtrHandleManager atr_manager; +// --------------------------------------------------------------------- +// CLASS: AdjustPosition +// --------------------------------------------------------------------- +// Provides methods to manage stop-loss logic for runner trades. +// Includes breakeven, trailing stop (fixed and ATR), and virtual TP SLs. +// --------------------------------------------------------------------- class AdjustPosition { - public: +public: void set_breakeven_sl(string symbol, int runner_magic_no, double buffer_points = 5); void set_breakeven_if_profit_target_hit(string symbol, int runner_magic_no, double buffer_points = 5); void set_fixed_sl(string symbol, int runner_magic_no, double fixed_sl_price); void set_trailing_sl(string symbol, int runner_magic_no, double sl_offset_points = 5); - void trailing_stop_atr(string symbol, int magic_number, ENUM_TIMEFRAMES tf = PERIOD_CURRENT, double activation_mult = 1.0, - double trail_mult = 1.0, int atr_period = 14, bool use_bar_close = false); + void trailing_stop_atr(string symbol, int magic_number, ENUM_TIMEFRAMES tf = PERIOD_CURRENT, double activation_mult = 1.0, + double trail_mult = 1.0, int atr_period = 14, bool use_bar_close = false); - private: - void set_breakeven_sl_for_ticket(string symbol, ulong ticket, long order_type, double entry_price, double current_sl, double current_tp, int digits, double buffer_price, bool remove_tp); +private: + void set_breakeven_sl_for_ticket(string symbol, ulong ticket, long order_type, double entry_price, + double current_sl, double current_tp, int digits, double buffer_price, bool remove_tp); }; -// --------------------------------------------------------- - +// --------------------------------------------------------------------- +// Sets SL to breakeven for all matching runner trades. +// --------------------------------------------------------------------- void AdjustPosition::set_breakeven_sl(string symbol, int runner_magic_no, double buffer_points) { int digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS); double buffer_price = buffer_points * _Point; @@ -38,37 +49,33 @@ void AdjustPosition::set_breakeven_sl(string symbol, int runner_magic_no, double } } -// --------------------------------------------------------- -// Check runner trades for virtual TP hits and set SL to breakeven if crossed. -// Optimized to avoid unnecessary processing on every OnTimer()/OnTick() call. +// --------------------------------------------------------------------- +// Sets SL to breakeven if virtual TP (in comment) was hit. +// --------------------------------------------------------------------- void AdjustPosition::set_breakeven_if_profit_target_hit(string symbol, int runner_magic_no, double buffer_points) { - - // --- Get current bid/ask and symbol precision double ask = SymbolInfoDouble(symbol, SYMBOL_ASK); double bid = SymbolInfoDouble(symbol, SYMBOL_BID); - int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); + int digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS); double buffer_price = buffer_points * _Point; - double price_margin = 50 * _Point; // avoid premature checking if far from TP + double price_margin = 50 * _Point; - // --- Fast check: skip if no runner trades exist for this symbol bool has_runner = false; for (int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if (!PositionSelectByTicket(ticket)) continue; if (PositionGetString(POSITION_SYMBOL) != symbol) continue; - if ((int)PositionGetInteger(POSITION_MAGIC) == runner_magic_no) { + if ((int) PositionGetInteger(POSITION_MAGIC) == runner_magic_no) { has_runner = true; break; } } if (!has_runner) return; - // --- Loop through positions for breakeven SL for (int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if (!PositionSelectByTicket(ticket)) continue; if (PositionGetString(POSITION_SYMBOL) != symbol) continue; - if ((int)PositionGetInteger(POSITION_MAGIC) != runner_magic_no) continue; + if ((int) PositionGetInteger(POSITION_MAGIC) != runner_magic_no) continue; long order_type = PositionGetInteger(POSITION_TYPE); double entry = PositionGetDouble(POSITION_PRICE_OPEN); @@ -76,51 +83,135 @@ void AdjustPosition::set_breakeven_if_profit_target_hit(string symbol, int runne double tp = PositionGetDouble(POSITION_TP); string comment = PositionGetString(POSITION_COMMENT); - - // --- Parse virtual TP from comment: expected format "runner_tp:1.10500" double virtual_tp = 0.0; if (StringFind(comment, "runner_tp:") == 0) { string tp_str = StringSubstr(comment, StringLen("runner_tp:")); virtual_tp = StringToDouble(tp_str); } - if (virtual_tp <= 0.0) continue; // no valid virtual TP - if (tp > 0.0) { + if (virtual_tp <= 0.0) continue; + if (tp > 0.0) PrintFormat("Warning: Runner trade on %s (ticket %d) has TP set: %.5f", symbol, ticket, tp); - } - // --- Skip early if price not near virtual TP if (order_type == POSITION_TYPE_BUY && bid < virtual_tp - price_margin) continue; if (order_type == POSITION_TYPE_SELL && ask > virtual_tp + price_margin) continue; - // --- Check if virtual TP was hit - bool tp_hit = false; - if (order_type == POSITION_TYPE_BUY && bid >= virtual_tp) tp_hit = true; - if (order_type == POSITION_TYPE_SELL && ask <= virtual_tp) tp_hit = true; + bool tp_hit = (order_type == POSITION_TYPE_BUY && bid >= virtual_tp) || + (order_type == POSITION_TYPE_SELL && ask <= virtual_tp); if (!tp_hit) continue; - // Calculate expected breakeven SL double expected_sl = (order_type == POSITION_TYPE_BUY) ? entry + buffer_price : entry - buffer_price; - // Skip if SL already set to breakeven if (NormalizeDouble(sl, digits) == NormalizeDouble(expected_sl, digits)) continue; - - // --- Set breakeven SL set_breakeven_sl_for_ticket(symbol, ticket, order_type, entry, sl, tp, digits, buffer_price, true); } } // --------------------------------------------------------------------- -// TRAILING STOP ATR LOGIC +// Sets SL for a single trade to breakeven, optionally removes TP. // --------------------------------------------------------------------- -// This function updates the stop-loss of runner trades based on ATR. -// It only applies to trades with the given magic number and symbol. +void AdjustPosition::set_breakeven_sl_for_ticket(string symbol, ulong ticket, long order_type, double entry_price, + double current_sl, double current_tp, int digits, + double buffer_price, bool remove_tp) { + double breakeven_sl = (order_type == POSITION_TYPE_BUY) + ? entry_price + buffer_price + : entry_price - buffer_price; + + if ((order_type == POSITION_TYPE_BUY && current_sl >= breakeven_sl) || + (order_type == POSITION_TYPE_SELL && current_sl <= breakeven_sl)) return; + + MqlTradeRequest request = {}; + MqlTradeResult result; + + request.action = TRADE_ACTION_SLTP; + request.symbol = symbol; + request.position = ticket; + request.sl = NormalizeDouble(breakeven_sl, digits); + request.tp = remove_tp ? 0.0 : current_tp; + request.magic = (int) PositionGetInteger(POSITION_MAGIC); + + if (!OrderSend(request, result)) + Print("Failed to adjust runner: ", symbol, ". Error: ", result.retcode); + else if (remove_tp) + Print("Runner upgraded to trailing: SL at breakeven, TP removed for ", symbol); +} + +// --------------------------------------------------------------------- +// Sets a fixed SL price for all runner trades. +// --------------------------------------------------------------------- +void AdjustPosition::set_fixed_sl(string symbol, int runner_magic_no, double fixed_sl_price) { + for (int i = PositionsTotal() - 1; i >= 0; i--) { + ulong ticket = PositionGetTicket(i); + if (!PositionSelectByTicket(ticket)) continue; + if (PositionGetString(POSITION_SYMBOL) != symbol) continue; + if ((int) PositionGetInteger(POSITION_MAGIC) != runner_magic_no) continue; + + double current_sl = PositionGetDouble(POSITION_SL); + double current_tp = PositionGetDouble(POSITION_TP); + if (current_sl == fixed_sl_price) continue; + + MqlTradeRequest request = {}; + MqlTradeResult result; + + request.action = TRADE_ACTION_SLTP; + request.symbol = symbol; + request.position = ticket; + request.sl = fixed_sl_price; + request.tp = current_tp; + request.magic = runner_magic_no; + + if (!OrderSend(request, result)) + Print("Failed to set fixed SL for runner on ", symbol, ". Error: ", result.retcode); + } +} + +// --------------------------------------------------------------------- +// Applies a fixed-point trailing stop to runner trades. +// --------------------------------------------------------------------- +void AdjustPosition::set_trailing_sl(string symbol, int runner_magic_no, double sl_offset_points) { + int digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS); + double offset = sl_offset_points * _Point; + + for (int i = PositionsTotal() - 1; i >= 0; i--) { + ulong ticket = PositionGetTicket(i); + if (!PositionSelectByTicket(ticket)) continue; + if (PositionGetString(POSITION_SYMBOL) != symbol) continue; + if ((int) PositionGetInteger(POSITION_MAGIC) != runner_magic_no) continue; + + long type = PositionGetInteger(POSITION_TYPE); + double current_sl = PositionGetDouble(POSITION_SL); + double current_tp = PositionGetDouble(POSITION_TP); + double price = (type == POSITION_TYPE_BUY) + ? SymbolInfoDouble(symbol, SYMBOL_BID) + : SymbolInfoDouble(symbol, SYMBOL_ASK); + + double sl = (type == POSITION_TYPE_BUY) ? price - offset : price + offset; + if ((type == POSITION_TYPE_BUY && sl <= current_sl) || + (type == POSITION_TYPE_SELL && sl >= current_sl)) continue; + + MqlTradeRequest request = {}; + MqlTradeResult result; + + request.action = TRADE_ACTION_SLTP; + request.symbol = symbol; + request.position = ticket; + request.sl = NormalizeDouble(sl, digits); + request.tp = current_tp; + request.magic = runner_magic_no; + + if (!OrderSend(request, result)) + Print("Failed to update trailing SL for runner on ", symbol, ". Error: ", result.retcode); + } +} + +// --------------------------------------------------------------------- +// Applies an ATR-based trailing stop to runner trades. // // Parameters: -// - symbol : The trading symbol. +// - symbol : Trading symbol. // - _magic_number : Magic number to identify trades. // - tf : Timeframe used for ATR calculation. // - activation_mult: Multiplier to determine when to activate trailing. @@ -132,8 +223,8 @@ void AdjustPosition::set_breakeven_if_profit_target_hit(string symbol, int runne // - Trailing starts only after activation distance is reached. // - SL is only updated if it moves closer to price (i.e., improves). // --------------------------------------------------------------------- -void AdjustPosition::trailing_stop_atr(string symbol, int _magic_number, ENUM_TIMEFRAMES tf, double activation_mult, double trail_mult, int atr_period, bool use_bar_close) { - +void AdjustPosition::trailing_stop_atr(string symbol, int _magic_number, ENUM_TIMEFRAMES tf, double activation_mult, + double trail_mult, int atr_period, bool use_bar_close) { double atr = atr_manager.get_atr_value(symbol, tf, atr_period); if (atr == EMPTY_VALUE) return; @@ -151,16 +242,19 @@ void AdjustPosition::trailing_stop_atr(string symbol, int _magic_number, ENUM_TI double entry = PositionGetDouble(POSITION_PRICE_OPEN); double sl = PositionGetDouble(POSITION_SL); double price = use_bar_close ? iClose(symbol, tf, 1) : (type == POSITION_TYPE_BUY ? bid : ask); + double trail_distance = atr * trail_mult; double activation_distance = atr * activation_mult; - bool should_trail = (type == POSITION_TYPE_BUY && price >= entry + activation_distance) || (type == POSITION_TYPE_SELL && price <= entry - activation_distance); + bool should_trail = (type == POSITION_TYPE_BUY && price >= entry + activation_distance) || + (type == POSITION_TYPE_SELL && price <= entry - activation_distance); if (!should_trail) continue; double new_sl = (type == POSITION_TYPE_BUY) ? price - trail_distance : price + trail_distance; new_sl = NormalizeDouble(new_sl, digits); - if ((type == POSITION_TYPE_BUY && sl >= new_sl) || (type == POSITION_TYPE_SELL && sl <= new_sl)) continue; + if ((type == POSITION_TYPE_BUY && sl >= new_sl) || + (type == POSITION_TYPE_SELL && sl <= new_sl)) continue; if (!trade.PositionModify(ticket, new_sl, PositionGetDouble(POSITION_TP))) PrintFormat("Trailing SL update failed for %s ticket=%d", symbol, ticket); @@ -168,98 +262,3 @@ void AdjustPosition::trailing_stop_atr(string symbol, int _magic_number, ENUM_TI PrintFormat("Trailing SL updated: %s ticket=%d new SL=%.5f", symbol, ticket, new_sl); } } - -// --------------------------------------------------------- -void AdjustPosition::set_breakeven_sl_for_ticket(string symbol, ulong ticket, long order_type, double entry_price, double current_sl, - double current_tp, int digits, double buffer_price, bool remove_tp) { - double breakeven_sl = (order_type == POSITION_TYPE_BUY) ? entry_price + buffer_price : entry_price - buffer_price; - - if ((order_type == POSITION_TYPE_BUY && current_sl >= breakeven_sl) || (order_type == POSITION_TYPE_SELL && current_sl <= breakeven_sl)) - return; - - MqlTradeRequest request = {}; - MqlTradeResult result; - - request.action = TRADE_ACTION_SLTP; - request.symbol = symbol; - request.position = ticket; - request.sl = NormalizeDouble(breakeven_sl, digits); - request.tp = remove_tp ? 0.0 : current_tp; - request.magic = (int) PositionGetInteger(POSITION_MAGIC); - - if (!OrderSend(request, result)) { - Print("Failed to adjust runner: ", symbol, ". Error: ", result.retcode); - } else if (remove_tp) { - Print("Runner upgraded to trailing: SL at breakeven, TP removed for ", symbol); - } -} - -// --------------------------------------------------------- - -void AdjustPosition::set_fixed_sl(string symbol, int runner_magic_no, double fixed_sl_price) { - for (int i = PositionsTotal() - 1; i >= 0; i--) { - ulong ticket = PositionGetTicket(i); - if (!PositionSelectByTicket(ticket)) continue; - if (PositionGetString(POSITION_SYMBOL) != symbol) continue; - if ((int) PositionGetInteger(POSITION_MAGIC) != runner_magic_no) continue; - - double current_sl = PositionGetDouble(POSITION_SL); - double current_tp = PositionGetDouble(POSITION_TP); - - if (current_sl == fixed_sl_price) continue; - - MqlTradeRequest request = {}; - MqlTradeResult result; - - request.action = TRADE_ACTION_SLTP; - request.symbol = symbol; - request.position = ticket; - request.sl = fixed_sl_price; - request.tp = current_tp; - request.magic = runner_magic_no; - - if (!OrderSend(request, result)) { - Print("Failed to set fixed SL for runner on ", symbol, ". Error: ", result.retcode); - } - } -} - -// --------------------------------------------------------- - -void AdjustPosition::set_trailing_sl(string symbol, int runner_magic_no, double sl_offset_points) { - int digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS); - double price = 0; - double sl = 0; - double offset = sl_offset_points * _Point; - - for (int i = PositionsTotal() - 1; i >= 0; i--) { - ulong ticket = PositionGetTicket(i); - if (!PositionSelectByTicket(ticket)) continue; - if (PositionGetString(POSITION_SYMBOL) != symbol) continue; - if ((int) PositionGetInteger(POSITION_MAGIC) != runner_magic_no) continue; - - long type = PositionGetInteger(POSITION_TYPE); - double current_sl = PositionGetDouble(POSITION_SL); - double current_tp = PositionGetDouble(POSITION_TP); - - price = (type == POSITION_TYPE_BUY) ? SymbolInfoDouble(symbol, SYMBOL_BID) : SymbolInfoDouble(symbol, SYMBOL_ASK); - - sl = (type == POSITION_TYPE_BUY) ? price - offset : price + offset; - - if ((type == POSITION_TYPE_BUY && sl <= current_sl) || (type == POSITION_TYPE_SELL && sl >= current_sl)) continue; - - MqlTradeRequest request = {}; - MqlTradeResult result; - - request.action = TRADE_ACTION_SLTP; - request.symbol = symbol; - request.position = ticket; - request.sl = NormalizeDouble(sl, digits); - request.tp = current_tp; - request.magic = runner_magic_no; - - if (!OrderSend(request, result)) { - Print("Failed to update trailing SL for runner on ", symbol, ". Error: ", result.retcode); - } - } -} diff --git a/Orders/CalculatePositionData.mqh b/Orders/CalculatePositionData.mqh index 8e6bc67..cca904d 100644 --- a/Orders/CalculatePositionData.mqh +++ b/Orders/CalculatePositionData.mqh @@ -3,6 +3,12 @@ #include #include +// --------------------------------------------------------------------- +// CLASS: CalculatePositionData +// --------------------------------------------------------------------- +// Provides core logic for computing stop loss, take profit, lot size, +// and trading costs based on symbol, price, and risk parameters. +// --------------------------------------------------------------------- class CalculatePositionData : public CObject { protected: CTrade trade; @@ -20,8 +26,20 @@ public: double calculate_trading_cost(string symbol, ulong ticket); }; -//+------------------------------------------------------------------+ - +// --------------------------------------------------------------------- +// Calculates stop loss based on selected method. +// +// Parameters: +// - symbol : Symbol for the trade. +// - price : Entry price. +// - order_side: 1 = Buy, 2 = Sell. +// - mode_sl : SL method ("NO_STOPLOSS", "SL_FIXED_PIPS", etc). +// - sl_var : SL parameter (pips, %, ATR multiplier, or absolute). +// - atr_tf : Timeframe for ATR. +// +// Returns: +// - Calculated SL price, or 0 if invalid. +// --------------------------------------------------------------------- double CalculatePositionData::calculate_stoploss(string symbol, double price, int order_side, string mode_sl, double sl_var, ENUM_TIMEFRAMES atr_tf) { double sl = 0; @@ -55,8 +73,21 @@ double CalculatePositionData::calculate_stoploss(string symbol, double price, in return sl; } -//+------------------------------------------------------------------+ - +// --------------------------------------------------------------------- +// Calculates take profit based on selected method. +// +// Parameters: +// - symbol : Symbol for the trade. +// - price : Entry price. +// - stoploss : SL value (used in TP/SL ratio mode). +// - order_side: 1 = Buy, -1 = Sell. +// - mode_tp : TP method ("NO_TAKE_PROFIT", "TP_FIXED_PIPS", etc). +// - tp_var : TP parameter (pips, %, ATR multiplier, SL multiple). +// - atr_tf : Timeframe for ATR. +// +// Returns: +// - Calculated TP price, or 0 if invalid. +// --------------------------------------------------------------------- double CalculatePositionData::calculate_take_profit(string symbol, double price, double stoploss, int order_side, string mode_tp, double tp_var, ENUM_TIMEFRAMES atr_tf) { double tp = 0; @@ -96,8 +127,19 @@ double CalculatePositionData::calculate_take_profit(string symbol, double price, return tp; } -//+------------------------------------------------------------------+ - +// --------------------------------------------------------------------- +// Calculates lot size based on selected lot mode. +// +// Parameters: +// - symbol : Symbol for the trade. +// - sl_distance: SL distance in points. +// - price : Current price. +// - mode_lot : Lot mode ("LOT_MODE_FIXED", "LOT_MODE_PCT_RISK", etc). +// - lot_var : Value for lot calculation. +// +// Returns: +// - Computed lot size (rounded and validated). +// --------------------------------------------------------------------- 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); @@ -125,8 +167,16 @@ double CalculatePositionData::calculate_lots(string symbol, double sl_distance, return lots; } -//+------------------------------------------------------------------+ - +// --------------------------------------------------------------------- +// Validates and adjusts lot size to symbol constraints. +// +// Parameters: +// - lots : Input/output lot size. +// - symbol : Trading symbol. +// +// Returns: +// - true if lots are valid after correction. +// --------------------------------------------------------------------- bool CalculatePositionData::check_lots(double& lots, string symbol) { double min = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); double max = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX); @@ -146,8 +196,17 @@ bool CalculatePositionData::check_lots(double& lots, string symbol) { return true; } -//+------------------------------------------------------------------+ - +// --------------------------------------------------------------------- +// Normalizes price to the nearest valid tick size. +// +// Parameters: +// - price : Raw price. +// - normalizedPrice: Output normalized price. +// - symbol : Trading symbol. +// +// Returns: +// - true if successful, false if tick size lookup failed. +// --------------------------------------------------------------------- bool CalculatePositionData::normalise_price(double price, double& normalizedPrice, string symbol) { double tick_size; if (!SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE, tick_size)) { @@ -159,17 +218,3 @@ bool CalculatePositionData::normalise_price(double price, double& normalizedPric normalizedPrice = NormalizeDouble(MathRound(price / tick_size) * tick_size, digits); return true; } - -//+------------------------------------------------------------------+ - -// double CalculatePositionData::calculate_trading_cost(string symbol, ulong ticket) { -// position.SelectByTicket(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 lots = PositionGetDouble(POSITION_VOLUME); - -// return -1.0 * ((commission + swap) / tick_value * tick_size / lots); -// } diff --git a/Orders/EntryOrders.mqh b/Orders/EntryOrders.mqh index e877b40..742fe49 100644 --- a/Orders/EntryOrders.mqh +++ b/Orders/EntryOrders.mqh @@ -28,9 +28,19 @@ public: bool open_runner_sell_order_with_virtual_tp(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); - }; +// --------------------------------------------------------------------- +// Counts open positions by symbol, side, and magic number. +// +// Parameters: +// - symbol : Symbol to check. +// - order_side : 1 = Buy, 2 = Sell, 0 = Any. +// - _magic_number : Magic number to filter. +// +// Returns: +// - Number of matching open positions. +// --------------------------------------------------------------------- int EntryOrders::count_open_positions(string symbol, int order_side, long _magic_number) { int count = 0; for (int i = PositionsTotal() - 1; i >= 0; i--) { @@ -45,6 +55,24 @@ int EntryOrders::count_open_positions(string symbol, int order_side, long _magic return count; } +// --------------------------------------------------------------------- +// Opens a market BUY position. +// +// Parameters: +// - symbol : Symbol to trade. +// - condition : If false, trade will not execute. +// - atr_period : Timeframe for ATR-based SL/TP. +// - _sl_mode : SL calculation method. +// - sl_var : SL variable (e.g., pips or ATR multiplier). +// - _tp_mode : TP calculation method. +// - tp_var : TP variable. +// - _lot_mode : Lot calculation method. +// - lot_var : Lot sizing variable. +// - _magic_number : Magic number for trade. +// +// Returns: +// - True if trade was placed successfully. +// --------------------------------------------------------------------- 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) return false; @@ -68,6 +96,24 @@ bool EntryOrders::open_buy_orders(string symbol, bool condition, ENUM_TIMEFRAMES return result; } +// --------------------------------------------------------------------- +// Opens a market SELL position. +// +// Parameters: +// - symbol : Symbol to trade. +// - condition : If false, trade will not execute. +// - atr_period : Timeframe for ATR-based SL/TP. +// - _sl_mode : SL calculation method. +// - sl_var : SL variable (e.g., pips or ATR multiplier). +// - _tp_mode : TP calculation method. +// - tp_var : TP variable. +// - _lot_mode : Lot calculation method. +// - lot_var : Lot sizing variable. +// - _magic_number : Magic number for trade. +// +// Returns: +// - True if trade was placed successfully. +// --------------------------------------------------------------------- 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) return false; @@ -91,6 +137,26 @@ bool EntryOrders::open_sell_orders(string symbol, bool condition, ENUM_TIMEFRAME return result; } +// --------------------------------------------------------------------- +// Opens a pending BUY STOP order. +// +// Parameters: +// - symbol : Symbol to trade. +// - condition : If false, order will not be placed. +// - entry_price : Trigger price for Buy Stop. +// - expiration : Expiration time for pending order. +// - atr_period : Timeframe for ATR-based SL/TP. +// - _sl_mode : SL calculation method. +// - sl_var : SL variable. +// - _tp_mode : TP calculation method. +// - tp_var : TP variable. +// - _lot_mode : Lot calculation method. +// - lot_var : Lot sizing variable. +// - _magic_number : Magic number for order. +// +// Returns: +// - True if order was placed successfully. +// --------------------------------------------------------------------- bool EntryOrders::open_buy_stop_order(string symbol, bool condition, double entry_price, datetime expiration, 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) { @@ -114,6 +180,26 @@ bool EntryOrders::open_buy_stop_order(string symbol, bool condition, double entr return result; } +// --------------------------------------------------------------------- +// Opens a pending SELL STOP order. +// +// Parameters: +// - symbol : Symbol to trade. +// - condition : If false, order will not be placed. +// - entry_price : Trigger price for Sell Stop. +// - expiration : Expiration time for pending order. +// - atr_period : Timeframe for ATR-based SL/TP. +// - _sl_mode : SL calculation method. +// - sl_var : SL variable. +// - _tp_mode : TP calculation method. +// - tp_var : TP variable. +// - _lot_mode : Lot calculation method. +// - lot_var : Lot sizing variable. +// - _magic_number : Magic number for order. +// +// Returns: +// - True if order was placed successfully. +// --------------------------------------------------------------------- bool EntryOrders::open_sell_stop_order(string symbol, bool condition, double entry_price, datetime expiration, 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) { @@ -137,6 +223,24 @@ bool EntryOrders::open_sell_stop_order(string symbol, bool condition, double ent return result; } +// --------------------------------------------------------------------- +// Opens a market BUY runner with virtual TP in comment. +// +// Parameters: +// - symbol : Symbol to trade. +// - condition : If false, trade will not execute. +// - atr_period : Timeframe for ATR-based SL. +// - _sl_mode : SL calculation method. +// - sl_var : SL variable. +// - _tp_mode : TP calculation method. +// - tp_var : TP variable (used for virtual TP). +// - _lot_mode : Lot calculation method. +// - lot_var : Lot sizing variable. +// - _magic_number : Magic number for trade. +// +// Returns: +// - True if trade was placed successfully. +// --------------------------------------------------------------------- bool EntryOrders::open_runner_buy_order_with_virtual_tp(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) { @@ -161,6 +265,24 @@ bool EntryOrders::open_runner_buy_order_with_virtual_tp(string symbol, bool cond return result; } +// --------------------------------------------------------------------- +// Opens a market SELL runner with virtual TP in comment. +// +// Parameters: +// - symbol : Symbol to trade. +// - condition : If false, trade will not execute. +// - atr_period : Timeframe for ATR-based SL. +// - _sl_mode : SL calculation method. +// - sl_var : SL variable. +// - _tp_mode : TP calculation method. +// - tp_var : TP variable (used for virtual TP). +// - _lot_mode : Lot calculation method. +// - lot_var : Lot sizing variable. +// - _magic_number : Magic number for trade. +// +// Returns: +// - True if trade was placed successfully. +// --------------------------------------------------------------------- bool EntryOrders::open_runner_sell_order_with_virtual_tp(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) { diff --git a/Orders/ExitOrders.mqh b/Orders/ExitOrders.mqh index f0ed9f1..e994738 100644 --- a/Orders/ExitOrders.mqh +++ b/Orders/ExitOrders.mqh @@ -3,7 +3,7 @@ #include class ExitOrders { - protected: +protected: CTrade trade; TimeZones tz; CalculatePositionData calc; @@ -12,15 +12,27 @@ class ExitOrders { long position_open_time; long first_allowed_close_time; - public: +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 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); }; +// --------------------------------------------------------------------- +// Closes BUY positions on condition + after a number of bars (if non 0) . +// +// Parameters: +// - symbol : Symbol to evaluate positions for. +// - condition : If true, closes position immediately. +// - close_bars : Minimum number of bars before auto close. +// - close_bar_period : Timeframe to count bars on. +// - _magic_number : Magic number to identify the trade group. +// +// Returns: +// - True after evaluation and any attempted closes. +// --------------------------------------------------------------------- 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); @@ -38,6 +50,19 @@ bool ExitOrders::close_buy_orders(string symbol, bool condition, int close_bars, return true; } +// --------------------------------------------------------------------- +// Closes SELL positions on condition + after a number of bars (if non 0) . +// +// Parameters: +// - symbol : Symbol to evaluate positions for. +// - condition : If true, closes position immediately. +// - close_bars : Minimum number of bars before auto close. +// - close_bar_period : Timeframe to count bars on. +// - _magic_number : Magic number to identify the trade group. +// +// Returns: +// - True after evaluation and any attempted closes. +// --------------------------------------------------------------------- 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); @@ -55,6 +80,18 @@ bool ExitOrders::close_sell_orders(string symbol, bool condition, int close_bars return true; } +// --------------------------------------------------------------------- +// Closes position after a fixed exit time and delay in days. +// +// Parameters: +// - symbol : Symbol to evaluate. +// - exit_time : Time of day when exit is permitted. +// - delay_days : Number of full days before close allowed. +// - _magic_number : Magic number to identify the trade group. +// +// Returns: +// - True after evaluation and any attempted closes. +// --------------------------------------------------------------------- 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); @@ -73,6 +110,20 @@ bool ExitOrders::daily_timed_exit(string symbol, datetime exit_time, int delay_d return true; } +// --------------------------------------------------------------------- +// Closes a position only if it's profitable after a given time. +// +// Parameters: +// - symbol : Symbol to evaluate. +// - close_bar_period : Bar timeframe for bar-close evaluation. +// - exit_time : Time of day when profit exit is checked. +// - cw_tzone : Clockwork time zone for exit conversion. +// - delay_days : Minimum days to wait before closing. +// - _magic_number : Magic number to identify the trade group. +// +// Returns: +// - True after evaluation and any attempted closes. +// --------------------------------------------------------------------- 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--) { @@ -108,6 +159,17 @@ bool ExitOrders::daily_timed_profit_exit(string symbol, ENUM_TIMEFRAMES close_ba return true; } +// --------------------------------------------------------------------- +// Closes position on first profitable bar after one bar completes. +// +// Parameters: +// - symbol : Symbol to evaluate. +// - close_bar_period : Timeframe for bar-close evaluation. +// - _magic_number : Magic number to identify the trade group. +// +// Returns: +// - True after evaluation and any attempted closes. +// --------------------------------------------------------------------- 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); diff --git a/Orders/OrderTracker.mqh b/Orders/OrderTracker.mqh index 7e198c3..cc75fbd 100644 --- a/Orders/OrderTracker.mqh +++ b/Orders/OrderTracker.mqh @@ -12,6 +12,18 @@ class OrderTracker { int count_pending_orders(string symbol, ENUM_ORDER_TYPE order_type, long magic); }; + +// --------------------------------------------------------------------- +// Counts the number of open BUY or SELL positions for a given symbol. +// +// Parameters: +// - symbol : Trading symbol (e.g., "EURUSD"). +// - order_side : 1 = BUY, 2 = SELL. +// - magic_number : Magic number identifying strategy group. +// +// Returns: +// - Number of matching open positions. +// --------------------------------------------------------------------- int OrderTracker::count_open_positions(string symbol, int order_side, long magic_number) { int count = 0; @@ -32,6 +44,16 @@ int OrderTracker::count_open_positions(string symbol, int order_side, long magic return count; } +// --------------------------------------------------------------------- +// Counts all open positions for a symbol regardless of direction. +// +// Parameters: +// - symbol : Trading symbol. +// - magic_number : Magic number identifying strategy group. +// +// Returns: +// - Total number of matching positions. +// --------------------------------------------------------------------- int OrderTracker::count_all_positions(string symbol, long magic_number) { int count = 0; @@ -46,6 +68,17 @@ int OrderTracker::count_all_positions(string symbol, long magic_number) { return count; } +// --------------------------------------------------------------------- +// Counts pending orders of a specific type for a symbol and magic number. +// +// Parameters: +// - symbol : Trading symbol. +// - order_type : Type of pending order (e.g., ORDER_TYPE_BUY_STOP). +// - magic : Magic number identifying strategy group. +// +// Returns: +// - Number of matching pending orders. +// --------------------------------------------------------------------- int OrderTracker::count_pending_orders(string symbol, ENUM_ORDER_TYPE order_type, long magic) { int count = 0; diff --git a/Orders/StopLogic.mqh b/Orders/StopLogic.mqh index 4be92fb..4225f0b 100644 --- a/Orders/StopLogic.mqh +++ b/Orders/StopLogic.mqh @@ -1,9 +1,20 @@ class StopLogic { - public: +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); }; +// --------------------------------------------------------------------- +// Selects stop loss value based on SL mode. +// +// Parameters: +// - sl_mode : Stop loss mode ("SL_SPECIFIED_VALUE", etc). +// - inp_sl_var : User-input SL value (pips, percent, etc). +// - value : Directly specified SL value. +// +// Returns: +// - `value` if SL mode is "SL_SPECIFIED_VALUE", otherwise `inp_sl_var`. +// --------------------------------------------------------------------- double StopLogic::sl_specified_value_switch(string sl_mode, double inp_sl_var, double value) { if (sl_mode == "SL_SPECIFIED_VALUE") { return value; @@ -12,8 +23,19 @@ double StopLogic::sl_specified_value_switch(string sl_mode, double inp_sl_var, d } } +// --------------------------------------------------------------------- +// Selects take profit value based on TP mode. +// +// Parameters: +// - tp_mode : Take profit mode ("TP_SPECIFIED_VALUE", etc). +// - inp_tp_var : User-input TP value (pips, percent, etc). +// - value : Directly specified TP value. +// +// Returns: +// - `value` if TP mode is "TP_SPECIFIED_VALUE", otherwise `inp_tp_var`. +// --------------------------------------------------------------------- double StopLogic::tp_specified_value_switch(string tp_mode, double inp_tp_var, double value) { - if (tp_mode == "SL_SPECIFIED_VALUE") { + if (tp_mode == "TP_SPECIFIED_VALUE") { return value; } else { return inp_tp_var; diff --git a/Orders/TrailingLogic.mqh b/Orders/TrailingLogic.mqh deleted file mode 100644 index c10afc1..0000000 --- a/Orders/TrailingLogic.mqh +++ /dev/null @@ -1,81 +0,0 @@ -#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/RiskManagement/DrawdownControl.mqh b/RiskManagement/DrawdownControl.mqh index 862e27a..4152211 100644 --- a/RiskManagement/DrawdownControl.mqh +++ b/RiskManagement/DrawdownControl.mqh @@ -1,11 +1,11 @@ #property library #include -#include +#include class DrawdownControl : public CObject { protected: CTrade trade; - BarUtils bar_utils; + MarketDataUtils m_utils; string data_file; double daily_max_dd_per; @@ -31,6 +31,17 @@ class DrawdownControl : public CObject { double lot_correction_dynamic(double acc_dd_percent, double min_lot_factor, double max_lot_factor); }; +// --------------------------------------------------------------------- +// Initializes the drawdown control with input parameters and reads +// or resets persistent drawdown state. +// +// Parameters: +// - inp_data_file : Filename for drawdown data storage. +// - inp_acc_max_dd_per : Max absolute drawdown percentage. +// - inp_daily_max_dd_per : Max daily drawdown percentage. +// - inp_daily_reset_time : Reset time (e.g., "00:00"). +// - inp_print_statments : Optional flag to print debug info. +// --------------------------------------------------------------------- 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; @@ -82,6 +93,13 @@ void DrawdownControl::init_dd_control(string inp_data_file, double inp_acc_max_d print_messages(); } +// --------------------------------------------------------------------- +// Checks if current equity has breached the daily drawdown threshold. +// If so, closes all trades and cancels orders. +// +// Returns: +// - true if daily drawdown limit has been reached. +// --------------------------------------------------------------------- bool DrawdownControl::determine_daily_dd_limit() { // Reset max equity at the start of each day: @@ -116,7 +134,19 @@ bool DrawdownControl::determine_daily_dd_limit() { return daily_dd_limit_reached; } -// Reduces lot size as account apporchaes max allowed drawdown limit. +// --------------------------------------------------------------------- +// Calculates a corrected lot multiplier based on account drawdown. +// +// Parameters: +// - acc_equity_start : Starting equity reference. +// - min_lot_factor : Minimum lot scaling factor. +// - max_lot_factor : Maximum lot scaling factor. +// - dynm_lot_factor : Enable trailing dynamic lot logic. +// - dlf_trail_per : Percent buffer for dynamic trail. +// +// Returns: +// - Scaled lot factor between min and max bounds. +// --------------------------------------------------------------------- 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)); @@ -147,7 +177,17 @@ double DrawdownControl::lot_correction_factor(double acc_equity_start, double mi return max_lot_factor; } - +// --------------------------------------------------------------------- +// Computes dynamic lot factor based on trailing equity bounds. +// +// Parameters: +// - acc_dd_percent : Dynamic trailing buffer in percent. +// - min_lot_factor : Minimum lot factor. +// - max_lot_factor : Maximum lot factor. +// +// Returns: +// - Interpolated 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)); @@ -167,7 +207,7 @@ double DrawdownControl::lot_correction_dynamic(double acc_dd_percent, double min } // back-up to file every hour: - if(bar_utils.is_new_bar(_Symbol, PERIOD_H1) == true){ + if(m_utils.is_new_bar(_Symbol, PERIOD_H1) == true){ write_global_var_data(); } diff --git a/Strategy/EntryLogic_nnfx.mqh b/Strategy/EntryLogic_nnfx.mqh deleted file mode 100644 index 1a81a31..0000000 --- a/Strategy/EntryLogic_nnfx.mqh +++ /dev/null @@ -1,105 +0,0 @@ -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/Strategy/Range/RangeCalculator.mqh b/Strategy/RangeCalculator.mqh similarity index 77% rename from Strategy/Range/RangeCalculator.mqh rename to Strategy/RangeCalculator.mqh index 97b71d3..a6d7fe5 100644 --- a/Strategy/Range/RangeCalculator.mqh +++ b/Strategy/RangeCalculator.mqh @@ -50,7 +50,6 @@ class RangeCalculator : public CObject{ public: void calculate_range(); - double get_range_high(); double get_range_low(); double get_range_mid(); @@ -65,6 +64,18 @@ class RangeCalculator : public CObject{ }; +// --------------------------------------------------------------------- +// Sets the allowed days for range calculation. +// +// Parameters: +// - _inp_sun : Allow Sunday. +// - _inp_mon : Allow Monday. +// - _inp_tue : Allow Tuesday. +// - _inp_wed : Allow Wednesday. +// - _inp_thu : Allow Thursday. +// - _inp_fri : Allow Friday. +// - _inp_sat : Allow Saturday. +// --------------------------------------------------------------------- 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; @@ -76,6 +87,22 @@ void RangeCalculator::range_days(bool _inp_sun, bool _inp_mon, bool _inp_tue, bo days_initlised = true; } +// --------------------------------------------------------------------- +// Initializes the range parameters. +// +// Parameters: +// - inp_symbol : The symbol for the range. +// - _calc_period : Timeframe for range calculation. +// - t1 : Start time string. +// - t2 : End time string. +// - t3 : Expiry time string. +// - t4 : Close time string. +// - time_zone : Timezone name. +// - plot_range_inp : Whether to plot the range. +// +// Returns: +// - true if initialization was successful; false otherwise. +// --------------------------------------------------------------------- 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; @@ -113,7 +140,18 @@ bool RangeCalculator::initilise_range(string inp_symbol, ENUM_TIMEFRAMES _calc_p return true; } - +// --------------------------------------------------------------------- +// Converts time input strings to time deltas for range definition. +// +// Parameters: +// - t1 : Start time string. +// - t2 : End time string. +// - t3 : Expiry time string. +// - t4 : Close time string. +// +// Returns: +// - true if times were converted successfully; false on error. +// --------------------------------------------------------------------- bool RangeCalculator::convert_input_time_strings(string t1, string t2, string t3, string t4){ datetime _t1 = StringToTime(t1); @@ -149,44 +187,93 @@ bool RangeCalculator::convert_input_time_strings(string t1, string t2, string t3 return true; } -// high of the range +// --------------------------------------------------------------------- +// Returns the high value of the current range. +// +// Returns: +// - High price of the range. +// --------------------------------------------------------------------- double RangeCalculator::get_range_high(){ return high; }; -// low of the range +// --------------------------------------------------------------------- +// Returns the low value of the current range. +// +// Returns: +// - Low price of the range. +// --------------------------------------------------------------------- double RangeCalculator::get_range_low(){ return low; }; -// mid of the range +// --------------------------------------------------------------------- +// Returns the mid value of the current range. +// +// Returns: +// - Mid price of the range. +// --------------------------------------------------------------------- double RangeCalculator::get_range_mid(){ return mid; }; - +// --------------------------------------------------------------------- +// Returns the start time of the current range. +// +// Returns: +// - Range start time. +// --------------------------------------------------------------------- datetime RangeCalculator::get_range_start(){ return start_time; }; +// --------------------------------------------------------------------- +// Returns the end time of the current range. +// +// Returns: +// - Range end time. +// --------------------------------------------------------------------- datetime RangeCalculator::get_range_end(){ return end_time; }; + +// --------------------------------------------------------------------- +// Returns the expiration time for range-based orders. +// +// Returns: +// - Order expiration time. +// --------------------------------------------------------------------- datetime RangeCalculator::get_order_expire_time(){ return order_expire_time; }; +// --------------------------------------------------------------------- +// Returns the close time of the current range. +// +// Returns: +// - Range close time. +// --------------------------------------------------------------------- datetime RangeCalculator::get_range_close(){ return close_time; }; -// flag if a high breakout occurred +// --------------------------------------------------------------------- +// Returns the high breakout flag of the current range. +// +// Returns: +// - true if high breakout occurred; false otherwise. +// --------------------------------------------------------------------- bool RangeCalculator::get_range_high_breakout(){ return f_high_breakout; }; -// flag if a low breakout occurred +// --------------------------------------------------------------------- +// Returns the low breakout flag of the current range. +// +// Returns: +// - true if low breakout occurred; false otherwise. +// --------------------------------------------------------------------- bool RangeCalculator::get_range_low_breakout(){ return f_low_breakout; }; diff --git a/Utils/AtrHandleManager.mqh b/Utils/AtrHandleManager.mqh index c9f904b..96307a7 100644 --- a/Utils/AtrHandleManager.mqh +++ b/Utils/AtrHandleManager.mqh @@ -12,7 +12,19 @@ private: AtrEntry cache[]; // internal cache of ATR handles public: - // Get or create an ATR handle for a specific symbol/timeframe/period + + // --------------------------------------------------------------------- + // Returns a valid ATR handle for the given symbol, timeframe, and period. + // Creates and caches the handle if not already available. + // + // Parameters: + // - symbol : Trading symbol (e.g., "EURUSD"). + // - tf : Timeframe (e.g., PERIOD_H1). + // - period : ATR period (e.g., 14). + // + // Returns: + // - The ATR indicator handle, or INVALID_HANDLE if failed. + // --------------------------------------------------------------------- int get_atr_handle(string symbol, ENUM_TIMEFRAMES tf, int period) { for (int i = 0; i < ArraySize(cache); i++) { if (cache[i].symbol == symbol && cache[i].tf == tf && cache[i].period == period) @@ -32,7 +44,18 @@ public: return handle; } - // Get ATR value from buffer (returns EMPTY_VALUE if failure) + // --------------------------------------------------------------------- + // Gets the ATR value for a given symbol, timeframe, and period. + // + // Parameters: + // - symbol : Trading symbol (e.g., "EURUSD"). + // - tf : Timeframe to use (e.g., PERIOD_H1). + // - period : ATR period to calculate. + // - shift : Bar shift to read the value from (default is 1 for last closed bar. NEVER use 0!). + // + // Returns: + // - ATR value at the given shift, or EMPTY_VALUE on failure. + // --------------------------------------------------------------------- double get_atr_value(string symbol, ENUM_TIMEFRAMES tf, int period, int shift = 1) { int handle = get_atr_handle(symbol, tf, period); if (handle == INVALID_HANDLE) { @@ -51,7 +74,13 @@ public: return buffer[0]; } - // Release all handles in the cache + // --------------------------------------------------------------------- + // Releases all cached ATR handles and clears the internal cache. + // + // Logic: + // - Calls IndicatorRelease for each handle. + // - Clears the `cache` array. + // --------------------------------------------------------------------- void release_handles() { for (int i = 0; i < ArraySize(cache); i++) { if (cache[i].handle != INVALID_HANDLE) @@ -60,4 +89,3 @@ public: ArrayResize(cache, 0); } }; - diff --git a/Utils/ChartUtils.mqh b/Utils/ChartUtils.mqh index 79eeaa9..aa5783c 100644 --- a/Utils/ChartUtils.mqh +++ b/Utils/ChartUtils.mqh @@ -1,31 +1,43 @@ #include class ChartUtils : public CObject { - -public: - void draw_line(double value, string name, color clr = clrBlack); + public: + void draw_line(double value, string name, color clr = clrBlack); }; +// --------------------------------------------------------------------- +// Draws or updates a horizontal line on the chart at the given price level. +// +// Parameters: +// - value : Price level at which to draw the line. +// - name : Unique name for the line object. +// - clr : Line color (default is black). +// +// Logic: +// - If the object doesn't exist, it creates a new horizontal line. +// - If the object exists, it moves it to the new price level. +// - Calls ChartRedraw to update the chart visually. +// --------------------------------------------------------------------- void ChartUtils::draw_line(double value, string name, color clr) { - if (ObjectFind(0, name) < 0) { - ResetLastError(); + 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; - } + 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); - } + ObjectSetInteger(0, name, OBJPROP_COLOR, clr); + ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); + ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); + } - ResetLastError(); + ResetLastError(); - if (!ObjectMove(0, name, 0, 0, value)) { - Print(__FUNCTION__, ": failed to move the horizontal line! Error code = ", GetLastError()); - return; - } + if (!ObjectMove(0, name, 0, 0, value)) { + Print(__FUNCTION__, ": failed to move the horizontal line! Error code = ", GetLastError()); + return; + } - ChartRedraw(); + ChartRedraw(); } diff --git a/Utils/ExpertFileHeader.mqh b/Utils/ExpertFileHeader.mqh deleted file mode 100644 index 79a697d..0000000 --- a/Utils/ExpertFileHeader.mqh +++ /dev/null @@ -1,28 +0,0 @@ -#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 index 5b319db..6bbc175 100644 --- a/Utils/MarketDataUtils.mqh +++ b/Utils/MarketDataUtils.mqh @@ -1,94 +1,148 @@ 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); + 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 previousTimes[]; // Stores last recorded open time per key - string bar_keys[]; // Keys are symbol+TF combinations, e.g. "EURUSD_PERIOD_H1" + protected: + datetime previousTimes[]; // Stores last recorded open time per key + string bar_keys[]; // Keys are symbol+TF combinations, e.g. "EURUSD_PERIOD_H1" }; -// Helper function to find index of a key in an array -int LinearSearch(string &arr[], string target) { - for (int i = 0; i < ArraySize(arr); i++) { - if (arr[i] == target) - return i; - } - return -1; // Not found +// --------------------------------------------------------------------- +// Performs linear search on a string array. +// +// Parameters: +// - arr : Array of strings. +// - target : Target string to find. +// +// Returns: +// - Index of the target, or -1 if not found. +// --------------------------------------------------------------------- +int LinearSearch(string& arr[], string target) { + for (int i = 0; i < ArraySize(arr); i++) { + if (arr[i] == target) return i; + } + return -1; } -// Checks if a new bar has opened on the given timeframe and symbol +// --------------------------------------------------------------------- +// Implementation of is_new_bar. Tracks the open time of the last bar. +// +// Parameters: +// - symbol : Symbol to check. +// - time_frame : Timeframe to check. +// - daily_start_time: Time string for daily bar sync. +// +// Returns: +// - true if a new bar has formed, false otherwise. +// --------------------------------------------------------------------- bool MarketDataUtils::is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, string daily_start_time) { - datetime bar_open_time = iTime(symbol, time_frame, 0); // Current open time - string key = symbol + "_" + EnumToString(time_frame); + datetime bar_open_time = iTime(symbol, time_frame, 0); // Current open time + string key = symbol + "_" + EnumToString(time_frame); - int idx = LinearSearch(bar_keys, key); - if (idx == -1) { - int new_size = ArraySize(bar_keys) + 1; - ArrayResize(bar_keys, new_size); - ArrayResize(previousTimes, new_size); + int idx = LinearSearch(bar_keys, key); + if (idx == -1) { + int new_size = ArraySize(bar_keys) + 1; + ArrayResize(bar_keys, new_size); + ArrayResize(previousTimes, new_size); - idx = new_size - 1; - bar_keys[idx] = key; - previousTimes[idx] = 0; - } + idx = new_size - 1; + bar_keys[idx] = key; + previousTimes[idx] = 0; + } - if (previousTimes[idx] != 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)) { + if (previousTimes[idx] != bar_open_time) { + if (PeriodSeconds(time_frame) == PeriodSeconds(PERIOD_D1)) { + if (TimeCurrent() > StringToTime(daily_start_time)) { + previousTimes[idx] = bar_open_time; + return true; + } + } else { previousTimes[idx] = bar_open_time; return true; - } - } else { - previousTimes[idx] = bar_open_time; - return true; - } - } + } + } - return false; // No new bar + return false; } -// shift = 0 refers to the live candle (still forming) -// shift = 1 is the most recently closed candle -// shift = 2 is the one before that, etc. +// --------------------------------------------------------------------- +// Implementation of get_buffer_value. +// +// Parameters: +// - handle : Indicator handle. +// - shift : Shift index for historical bars. +// +// Returns: +// - The buffer value, or EMPTY_VALUE if error. +// --------------------------------------------------------------------- double MarketDataUtils::get_buffer_value(int handle, int shift) { - double val[]; - ArraySetAsSeries(val, true); + double val[]; + ArraySetAsSeries(val, true); - int copied = CopyBuffer(handle, 0, shift, 1, val); - if (copied <= 0) { - Print("CopyBuffer failed: handle=", handle, " shift=", shift); - return EMPTY_VALUE; - } + int copied = CopyBuffer(handle, 0, shift, 1, val); + if (copied <= 0) { + Print("CopyBuffer failed: handle=", handle, " shift=", shift); + return EMPTY_VALUE; + } - if (val[0] == EMPTY_VALUE) { - Print("EMPTY_VALUE returned for buffer at shift=", shift); - return EMPTY_VALUE; - } + if (val[0] == EMPTY_VALUE) { + Print("EMPTY_VALUE returned for buffer at shift=", shift); + return EMPTY_VALUE; + } - return val[0]; + return val[0]; } -// Adjusts the point value for symbol to account for fractional pips (e.g., 5-digit brokers) +// --------------------------------------------------------------------- +// Gets the latest (live) value from buffer (shift = 0). +// +// Parameters: +// - handle : Indicator handle. +// +// Returns: +// - Buffer value at shift 0 or EMPTY_VALUE if failed. +// --------------------------------------------------------------------- +double MarketDataUtils::get_latest_buffer_value(int handle) { + return get_buffer_value(handle, 0); +} + +// --------------------------------------------------------------------- +// Computes adjusted point value considering fractional pip brokers. +// +// Parameters: +// - symbol : Symbol name. +// +// Returns: +// - Adjusted point multiplier. +// --------------------------------------------------------------------- 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 + 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 current Bid or Ask price for a symbol based on side (1 = Ask, 2 = Bid) +// --------------------------------------------------------------------- +// Returns bid or ask price for a given symbol. +// +// Parameters: +// - symbol : Symbol name. +// - price_side : 1 = Ask, 2 = Bid. +// +// Returns: +// - Price value or 0.0 if input is invalid. +// --------------------------------------------------------------------- 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); + 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; + if (price_side == 1) return ask; + if (price_side == 2) return bid; - return 0.0; // Invalid input + return 0.0; } diff --git a/Utils/MultiSymbolSignalTracker.mqh b/Utils/MultiSymbolSignalTracker.mqh index 17d35b3..2bce10f 100644 --- a/Utils/MultiSymbolSignalTracker.mqh +++ b/Utils/MultiSymbolSignalTracker.mqh @@ -1,86 +1,82 @@ #include +// --------------------------------------------------------------------- // This class manages a collection of SignalStateTracker instances — one per symbol. // It allows you to track signals separately for each symbol in a multi-symbol EA. +// --------------------------------------------------------------------- class MultiSymbolSignalTracker { - private: - // Struct to hold one (symbol → tracker) mapping +private: + + // --------------------------------------------------------------------- + // Internal struct to associate a symbol with a SignalStateTracker. + // --------------------------------------------------------------------- struct SymbolTracker { - string symbol; // Symbol name (e.g. "EURUSD", "HSI") - SignalStateTracker* tracker; // Pointer to the signal tracker for that symbol + string symbol; + SignalStateTracker* tracker; }; - SymbolTracker trackers[]; // Dynamic array of symbol-tracker pairs + SymbolTracker trackers[]; // Dynamic array of symbol-tracker mappings - // Helper function: Find the index of the given symbol in the array + // --------------------------------------------------------------------- + // Finds the index of the symbol in the tracker array. + // --------------------------------------------------------------------- int find_index(const string& symbol) { for (int i = 0; i < ArraySize(trackers); i++) { - if (trackers[i].symbol == symbol) return i; // Found symbol, return its index + if (trackers[i].symbol == symbol) + return i; } - return -1; // Not found + return -1; } - public: - // Destructor: automatically called when this object is destroyed (e.g. at EA shutdown) +public: + + // --------------------------------------------------------------------- + // Destructor. Cleans up allocated memory when the object is destroyed. + // --------------------------------------------------------------------- ~MultiSymbolSignalTracker() { - clear(); // Clean up memory when done + clear(); } - // Returns the SignalStateTracker for the given symbol. - // If it doesn't exist yet, it creates one and stores it. + // --------------------------------------------------------------------- + // Retrieves the SignalStateTracker instance for a given symbol. + // Creates and stores a new one if it doesn't exist yet. + // + // Parameters: + // - symbol : Symbol for which to retrieve the signal tracker. + // + // Returns: + // - Pointer to the SignalStateTracker instance. + // --------------------------------------------------------------------- SignalStateTracker* get_tracker(const string& symbol) { int idx = find_index(symbol); - if (idx != -1) return trackers[idx].tracker; // Tracker already exists — return it + if (idx != -1) + return trackers[idx].tracker; - // If not found, create a new tracker for this symbol + // Create new tracker SignalStateTracker* tracker = new SignalStateTracker(); SymbolTracker item; item.symbol = symbol; item.tracker = tracker; - // Add new item to dynamic array ArrayResize(trackers, ArraySize(trackers) + 1); trackers[ArraySize(trackers) - 1] = item; return tracker; } - // Deletes all trackers and resets the array. - // Should be called in OnDeinit() to free memory. + // --------------------------------------------------------------------- + // Clears all SignalStateTracker instances and resets the internal array. + // Should be called in `OnDeinit()` to avoid memory leaks. + // + // Logic: + // - Deletes each dynamically allocated SignalStateTracker. + // - Resets array size to 0. + // --------------------------------------------------------------------- void clear() { for (int i = 0; i < ArraySize(trackers); i++) { - delete trackers[i].tracker; // Manually free each dynamically created tracker + delete trackers[i].tracker; } - ArrayResize(trackers, 0); // Reset array to empty + ArrayResize(trackers, 0); } }; - - -/* --------------------------------------------------------------------------- -Example usage in an EA: ---------------------------------------------------------------------------- - -// Declare the tracker globally (outside OnTick/OnTimer) -MultiSymbolSignalTracker track_trigger; - -// Inside your strategy() or OnTick(): -void strategy(string symbol, ...) { - bool trig_long = ...; // Your signal logic - bool trig_short = ...; - - // Update the signal tracker for this symbol - track_trigger.get_tracker(symbol).update_signal(trig_long, trig_short); - - // Example: Check if a recent long signal occurred - if (track_trigger.get_tracker(symbol).long_signal_recent(5)) { - // Do something like open a trade - } -} - -// In OnDeinit(): -void OnDeinit(const int reason) { - track_trigger.clear(); // Clean up memory -} - -*/ \ No newline at end of file diff --git a/Utils/ResourceManager.mqh b/Utils/ResourceManager.mqh index c59f52f..6144fa0 100644 --- a/Utils/ResourceManager.mqh +++ b/Utils/ResourceManager.mqh @@ -1,44 +1,61 @@ #include -//+------------------------------------------------------------------+ -//| ResourceManager | -//| | -//| Tracks and releases indicator handles (iMA, iRSI, etc.) | -//| Also delegates ATR handle cleanup to an external ATR manager | -//+------------------------------------------------------------------+ +// --------------------------------------------------------------------- +// ResourceManager +// +// Tracks and releases indicator handles (e.g., iMA, iRSI, iCCI). +// Delegates ATR handle management to an external AtrHandleManager instance. +// +// Usage: +// - Register indicator handles via `register_handle()`. +// - Call `release_all_handles()` to release all cached handles. +// --------------------------------------------------------------------- class ResourceManager { public: - // --- Pointer to external ATR manager (set externally) - // Used for releasing any internally cached ATR handles + AtrHandleManager* atr_manager; - // --- Register a new indicator handle to be released later - // Only valid (non-INVALID_HANDLE) handles are stored + // --------------------------------------------------------------------- + // Registers a generic indicator handle to be released later. + // + // Parameters: + // - handle : A valid (non-INVALID_HANDLE) indicator handle. + // + // Logic: + // - If the handle is valid, it is added to an internal list. + // --------------------------------------------------------------------- void register_handle(int handle) { if (handle != INVALID_HANDLE) add_handle(handle); } - // --- Release all tracked resources: + // --------------------------------------------------------------------- + // Releases all tracked indicator resources. + // + // Logic: + // - Releases generic handles tracked via `register_handle()`. + // - Also invokes `atr_manager.release_handles()` if assigned. + // --------------------------------------------------------------------- void release_all_handles() { release_internal_handles(); // ATR manager release_tracked_handles(); // Generic indicator handles } private: - // --- Dynamic array of general indicator handles int handles[]; - // --- Append a valid indicator handle to the internal array - // Used by register_handle() + // --------------------------------------------------------------------- + // Adds a handle to the internal tracking list. + // --------------------------------------------------------------------- void add_handle(int handle) { int size = ArraySize(handles); ArrayResize(handles, size + 1); handles[size] = handle; } - // --- Release all generic indicator handles tracked internally - // This covers iMA, iRSI, iCCI, etc. + // --------------------------------------------------------------------- + // Releases all tracked indicator handles (iMA, iRSI, etc.). + // --------------------------------------------------------------------- void release_tracked_handles() { for (int i = 0; i < ArraySize(handles); i++) { if (handles[i] != INVALID_HANDLE) @@ -47,8 +64,9 @@ private: ArrayFree(handles); } - // --- Release ATR handles via external AtrHandleManager - // No-op if atr_manager is not assigned + // --------------------------------------------------------------------- + // Releases any cached ATR handles using the external AtrHandleManager. + // --------------------------------------------------------------------- void release_internal_handles() { if (atr_manager != NULL) atr_manager.release_handles(); diff --git a/Utils/SignalStateTracker.mqh b/Utils/SignalStateTracker.mqh index 9aca440..602e5c9 100644 --- a/Utils/SignalStateTracker.mqh +++ b/Utils/SignalStateTracker.mqh @@ -1,22 +1,51 @@ class SignalStateTracker { - private: +private: + + // --------------------------------------------------------------------- + // Index of the last long signal detected (default -1000 when unset). + // --------------------------------------------------------------------- int last_signal_long; + + // --------------------------------------------------------------------- + // Index of the last short signal detected (default -1000 when unset). + // --------------------------------------------------------------------- int last_signal_short; - public: - // Constructor initializes both directions to a default "unset" state. +public: + + // --------------------------------------------------------------------- + // Constructor initializes the signal tracker to a reset state. + // + // Logic: + // - Sets both long and short signal indices to -1000. + // --------------------------------------------------------------------- SignalStateTracker() { reset(); } - // Resets the tracked signal bar indexes to an invalid default value (-1000). + // --------------------------------------------------------------------- + // Resets the signal tracker. + // + // Logic: + // - Sets `last_signal_long` and `last_signal_short` to -1000, + // representing no signal recorded. + // --------------------------------------------------------------------- void reset() { last_signal_long = -1000; last_signal_short = -1000; } - // Updates the signal tracker based on trigger presence per bar. - // If signal is detected, sets to 1. If not, increments previous value. + // --------------------------------------------------------------------- + // Updates internal state based on whether long/short signals occurred. + // + // Parameters: + // - signal_long : True if a long signal occurred this bar. + // - signal_short : True if a short signal occurred this bar. + // + // Logic: + // - If a signal is detected, sets the index to 1 (bar 1). + // - Otherwise, increments the previous value if it was positive. + // --------------------------------------------------------------------- void update_signal_tracker(bool signal_long, bool signal_short) { if (signal_long) { last_signal_long = 1; @@ -31,34 +60,68 @@ class SignalStateTracker { } } - // Returns true if a long signal occurred within the last `max_bars` bars. + // --------------------------------------------------------------------- + // Checks if a long signal occurred within the last N bars. + // + // Parameters: + // - max_bars : Number of bars to look back for the signal. + // + // Returns: + // - True if a long signal occurred within `max_bars` bars. + // --------------------------------------------------------------------- bool long_signal_recent(int max_bars) const { - // Assumes current bar is always bar index 1 (last closed bar). return has_long_signal() && (1 - last_signal_long <= max_bars); } - // Returns true if a short signal occurred within the last `max_bars` bars. + // --------------------------------------------------------------------- + // Checks if a short signal occurred within the last N bars. + // + // Parameters: + // - max_bars : Number of bars to look back for the signal. + // + // Returns: + // - True if a short signal occurred within `max_bars` bars. + // --------------------------------------------------------------------- bool short_signal_recent(int max_bars) const { - // Assumes current bar is always bar index 1 (last closed bar). return has_short_signal() && (1 - last_signal_short <= max_bars); } - // Returns true if a signal has been recorded for the long direction. + // --------------------------------------------------------------------- + // Indicates if any long signal has ever been recorded. + // + // Returns: + // - True if a long signal index is not equal to -1000. + // --------------------------------------------------------------------- bool has_long_signal() const { return last_signal_long != -1000; } - // Returns true if a signal has been recorded for the short direction. + // --------------------------------------------------------------------- + // Indicates if any short signal has ever been recorded. + // + // Returns: + // - True if a short signal index is not equal to -1000. + // --------------------------------------------------------------------- bool has_short_signal() const { return last_signal_short != -1000; } - // Returns the last recorded signal bar index for the long direction. + // --------------------------------------------------------------------- + // Returns the last bar index at which a long signal occurred. + // + // Returns: + // - Integer index representing bars since long signal. + // --------------------------------------------------------------------- int get_long_signal() const { return last_signal_long; } - // Returns the last recorded signal bar index for the short direction. + // --------------------------------------------------------------------- + // Returns the last bar index at which a short signal occurred. + // + // Returns: + // - Integer index representing bars since short signal. + // --------------------------------------------------------------------- int get_short_signal() const { return last_signal_short; } diff --git a/Utils/TimeZones.mqh b/Utils/TimeZones.mqh index fd41c7a..4d1f228 100644 --- a/Utils/TimeZones.mqh +++ b/Utils/TimeZones.mqh @@ -1,144 +1,176 @@ #property library -#include #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; +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 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); + double ny_daily_close_protected(string symbol, int shift_days, bool print_data = false); + + 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; +// --------------------------------------------------------------------- +// Converts a datetime to a string excluding seconds. +// +// Parameters: +// - dt : Datetime object. +// +// Returns: +// - A string in the format "yyyy.mm.dd hh:mi". +// --------------------------------------------------------------------- +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; } +// --------------------------------------------------------------------- +// Gets the current time in the specified time zone. +// +// Parameters: +// - time_zone : One of "NY", "Lon", "Ffm", "Syd", "Mosc", "Tok". +// - print_time : If true, logs various times for debugging. +// +// Returns: +// - Current time in the specified time zone. +// --------------------------------------------------------------------- +datetime TimeZones::get_timezone_time(string time_zone, bool print_time) { + checkTimeOffset(TimeCurrent()); // Adjust DST -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 + tC = TimeCurrent(); + tGMT = TimeCurrent() + OffsetBroker.actOffset; + tNY = tGMT - (NYShift + DST_USD); + tLon = tGMT - (LondonShift + DST_EUR); + tFfm = tGMT - (FfmShift + DST_EUR); + tSyd = tGMT - (SidneyShift + DST_AUD); + tMosc = tGMT - (MoskwaShift + DST_RUS); + tTok = tGMT - (TokyoShift); - checkTimeOffset(TimeCurrent()); // check changes of DST - // cto(); + if (print_time) { + 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); + } - 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 (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; - 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; + return NULL; } +// --------------------------------------------------------------------- +// Converts a datetime from one timezone to another. +// +// Parameters: +// - time_zone_known : Original timezone of the datetime. +// - time_given : The datetime to convert. +// - time_zone_required : Desired output timezone. +// +// Returns: +// - The equivalent datetime in the target timezone. +// --------------------------------------------------------------------- +datetime TimeZones::timezone_conversions(string time_zone_known, datetime time_given, string time_zone_required) { + tGIVEN = time_given; + checkTimeOffset(tGIVEN); // Adjust DST -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 + // Step 1: Convert known timezone to 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); - tGIVEN = time_given; //StringToTime(time_given); - - checkTimeOffset(tGIVEN); // check changes of DST + // Step 2: Convert GMT to required timezone + 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); - // 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; + 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; +// --------------------------------------------------------------------- +// Returns the most recent NY daily close price. +// +// Parameters: +// - symbol : Trading symbol. +// - shift_days : How many NY daily closes back to return. +// - print_data : If true, logs debug information. +// +// Returns: +// - The NY close price. +// --------------------------------------------------------------------- +double TimeZones::ny_daily_close(string symbol, int shift_days, bool print_data) { + 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 +// --------------------------------------------------------------------- +// Internal implementation to compute NY daily close price. +// +// Logic: +// - Defines NY close as 5pm NY time = 00:00 broker + 17H back. +// - Adjusts for day shifts if required. +// - Returns the close price of the NY daily session. +// --------------------------------------------------------------------- +double TimeZones::ny_daily_close_protected(string symbol, int shift_days, bool print_data) { + 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); - 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 deleted file mode 100644 index fb3164e..0000000 --- a/_old/BufferUtils.mqh +++ /dev/null @@ -1,43 +0,0 @@ -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/_old/CalculatePositionData.mqh b/_old/CalculatePositionData.mqh deleted file mode 100644 index 254eda0..0000000 --- a/_old/CalculatePositionData.mqh +++ /dev/null @@ -1,278 +0,0 @@ -#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/_old/MyFunctions.mqh b/_old/MyFunctions.mqh deleted file mode 100644 index f48cbf0..0000000 --- a/_old/MyFunctions.mqh +++ /dev/null @@ -1,211 +0,0 @@ -#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/_old/OrderManagement.mqh b/_old/OrderManagement.mqh deleted file mode 100644 index 5f5b35a..0000000 --- a/_old/OrderManagement.mqh +++ /dev/null @@ -1,556 +0,0 @@ -#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 deleted file mode 100644 index 3212e8a..0000000 --- a/_old/SymbolUtils.mqh +++ /dev/null @@ -1,35 +0,0 @@ -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/_old/TradingWindow.mqh b/_old/TradingWindow.mqh deleted file mode 100644 index 4c72a94..0000000 --- a/_old/TradingWindow.mqh +++ /dev/null @@ -1,65 +0,0 @@ -#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 deleted file mode 100644 index a1c1ea6..0000000 Binary files a/_old/_MyLibs_old.zip and /dev/null differ diff --git a/indicators/AtrBands.mqh b/indicators/AtrBands.mqh index 1d94d30..8234cba 100644 --- a/indicators/AtrBands.mqh +++ b/indicators/AtrBands.mqh @@ -1,113 +1,173 @@ #include #include +// --------------------------------------------------------------------- +// CLASS: AtrBands +// --------------------------------------------------------------------- +// Provides ATR-based upper/lower/middle band calculations and checks. +// Useful for volatility-based stop placement, trend filters, or overlays. +// --------------------------------------------------------------------- class AtrBands { private: MarketDataUtils market_data_utils; AtrHandleManager atr_manager; + double get_atr(string symbol, int atr_period, ENUM_TIMEFRAMES tf, int shift); + public: - // Core band accessors double upper_band(string symbol, double trendline_var, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int shift = 1); double lower_band(string symbol, double trendline_var, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int shift = 1); double middle_band(string symbol, double trendline_var, ENUM_TIMEFRAMES tf, int shift = 1); - - // Band checkers bool inside_upper_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int shift = 1); bool inside_lower_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int shift = 1); bool crossed_below_upper_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int shift = 1); bool crossed_above_lower_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int shift = 1); - - // Plotting void plot_bands(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int bars = 100, color clr = clrSkyBlue, int width = 1); - - -protected: - double get_atr(string symbol, int atr_period, ENUM_TIMEFRAMES tf, int shift); }; - -// ------------------- -// Public Methods -// ------------------- - +// --------------------------------------------------------------------- +// Returns the upper ATR band level. +// +// Parameters: +// - symbol : Symbol to use. +// - trendline_var : Base trendline value (e.g., MA). +// - atr_period : ATR period. +// - tf : Timeframe for ATR. +// - mult : ATR multiplier. +// - shift : Bar shift to evaluate. +// --------------------------------------------------------------------- double AtrBands::upper_band(string symbol, double trendline_var, int atr_period, ENUM_TIMEFRAMES tf, double mult, int shift) { double atr = get_atr(symbol, atr_period, tf, shift); return trendline_var + atr * mult; } +// --------------------------------------------------------------------- +// Returns the lower ATR band level. +// +// Parameters: +// - Same as upper_band, except lower band logic. +// --------------------------------------------------------------------- double AtrBands::lower_band(string symbol, double trendline_var, int atr_period, ENUM_TIMEFRAMES tf, double mult, int shift) { double atr = get_atr(symbol, atr_period, tf, shift); return trendline_var - atr * mult; } +// --------------------------------------------------------------------- +// Returns the middle band, which is simply the trendline. +// +// Parameters: +// - symbol : Symbol. +// - trendline_var : The trendline value. +// - tf : Timeframe (unused here). +// - shift : Shift index (unused here). +// --------------------------------------------------------------------- double AtrBands::middle_band(string symbol, double trendline_var, ENUM_TIMEFRAMES tf, int shift) { return trendline_var; } +// --------------------------------------------------------------------- +// Checks if price is between trendline and upper band. +// +// Logic: +// - Retrieves trendline and price. +// - Compares if price lies between trendline and upper band. +// --------------------------------------------------------------------- bool AtrBands::inside_upper_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult, int shift) { double price = iClose(symbol, tf, shift); double trendline_var = market_data_utils.get_buffer_value(handle, shift); double upper = upper_band(symbol, trendline_var, atr_period, tf, mult, shift); - if (price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || upper == EMPTY_VALUE) return false; + if (price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || upper == EMPTY_VALUE) + return false; return (price > trendline_var && price < upper); } +// --------------------------------------------------------------------- +// Checks if price is between trendline and lower band. +// +// Logic: +// - Retrieves trendline and price. +// - Compares if price lies between trendline and lower band. +// --------------------------------------------------------------------- bool AtrBands::inside_lower_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult, int shift) { double price = iClose(symbol, tf, shift); double trendline_var = market_data_utils.get_buffer_value(handle, shift); double lower = lower_band(symbol, trendline_var, atr_period, tf, mult, shift); - if (price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || lower == EMPTY_VALUE) return false; + if (price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || lower == EMPTY_VALUE) + return false; return (price < trendline_var && price > lower); } +// --------------------------------------------------------------------- +// Detects if price has crossed below the upper band. +// +// Logic: +// - Checks crossover from above to below upper band between bars [shift+1] and [shift]. +// --------------------------------------------------------------------- bool AtrBands::crossed_below_upper_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult, int shift) { double price = iClose(symbol, tf, shift); double prev_price = iClose(symbol, tf, shift + 1); - double trendline_var = market_data_utils.get_buffer_value(handle, shift); double prev_trendline_var = market_data_utils.get_buffer_value(handle, shift + 1); - double upper = upper_band(symbol, trendline_var, atr_period, tf, mult, shift); double prev_upper = upper_band(symbol, prev_trendline_var, atr_period, tf, mult, shift + 1); - if (price == EMPTY_VALUE || prev_price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || prev_trendline_var == EMPTY_VALUE) return false; + if (price == EMPTY_VALUE || prev_price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || prev_trendline_var == EMPTY_VALUE) + return false; return (prev_price > prev_upper && price < upper); } +// --------------------------------------------------------------------- +// Detects if price has crossed above the lower band. +// +// Logic: +// - Checks crossover from below to above lower band between bars [shift+1] and [shift]. +// --------------------------------------------------------------------- bool AtrBands::crossed_above_lower_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult, int shift) { double price = iClose(symbol, tf, shift); double prev_price = iClose(symbol, tf, shift + 1); - double trendline_var = market_data_utils.get_buffer_value(handle, shift); double prev_trendline_var = market_data_utils.get_buffer_value(handle, shift + 1); - double lower = lower_band(symbol, trendline_var, atr_period, tf, mult, shift); double prev_lower = lower_band(symbol, prev_trendline_var, atr_period, tf, mult, shift + 1); - if (price == EMPTY_VALUE || prev_price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || prev_trendline_var == EMPTY_VALUE) return false; + if (price == EMPTY_VALUE || prev_price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || prev_trendline_var == EMPTY_VALUE) + return false; return (prev_price < prev_lower && price > lower); } +// --------------------------------------------------------------------- +// Plots the ATR bands as OBJ_TREND lines on chart. +// +// Parameters: +// - symbol : Symbol to draw on. +// - handle : Trendline handle (e.g., MA). +// - atr_period : ATR period for bands. +// - tf : Timeframe. +// - mult : ATR multiplier. +// - bars : Number of bars to plot. +// - clr : Line color. +// - width : Line width. +// --------------------------------------------------------------------- void AtrBands::plot_bands(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult, int bars, color line_color, int width) { for (int i = bars; i >= 1; i--) { double trendline = market_data_utils.get_buffer_value(handle, i); - if (trendline == EMPTY_VALUE) continue; + if (trendline == EMPTY_VALUE) + continue; double atr = get_atr(symbol, atr_period, tf, i); - if (atr == EMPTY_VALUE) continue; + if (atr == EMPTY_VALUE) + continue; double upper = trendline + atr * mult; double lower = trendline - atr * mult; - datetime time1 = iTime(symbol, tf, i); datetime time2 = iTime(symbol, tf, i - 1); - string upper_name = "ATR_Upper_" + symbol + "_" + TimeToString(time1, TIME_DATE | TIME_MINUTES); - string lower_name = "ATR_Lower_" + symbol + "_" + TimeToString(time1, TIME_DATE | TIME_MINUTES); - string middle_name = "ATR_middle_" + symbol + "_" + TimeToString(time1, TIME_DATE | TIME_MINUTES); + string upper_name = "ATR_Upper_" + symbol + "_" + TimeToString(time1, TIME_DATE | TIME_MINUTES); + string lower_name = "ATR_Lower_" + symbol + "_" + TimeToString(time1, TIME_DATE | TIME_MINUTES); + string middle_name = "ATR_Middle_" + symbol + "_" + TimeToString(time1, TIME_DATE | TIME_MINUTES); if (ObjectFind(0, upper_name) < 0) { ObjectCreate(0, upper_name, OBJ_TREND, 0, time1, upper, time2, upper); @@ -140,6 +200,18 @@ void AtrBands::plot_bands(string symbol, int handle, int atr_period, ENUM_TIMEFR ChartRedraw(); } +// --------------------------------------------------------------------- +// Retrieves the ATR value via the shared ATR manager. +// +// Parameters: +// - symbol : Symbol to calculate ATR on. +// - atr_period : ATR calculation period. +// - tf : Timeframe of the ATR. +// - shift : Bar index to retrieve. +// +// Returns: +// - The ATR value at the given shift. +// --------------------------------------------------------------------- double AtrBands::get_atr(string symbol, int atr_period, ENUM_TIMEFRAMES tf, int shift) { return atr_manager.get_atr_value(symbol, tf, atr_period, shift); } diff --git a/indicators/TrendlineAnalyser.mqh b/indicators/TrendlineAnalyser.mqh index 71bf26a..cde0d06 100644 --- a/indicators/TrendlineAnalyser.mqh +++ b/indicators/TrendlineAnalyser.mqh @@ -1,59 +1,76 @@ #include -//+------------------------------------------------------------------+ -//| TrendlineSignal: A utility class to detect trendline crossovers | -//+------------------------------------------------------------------+ + +// --------------------------------------------------------------------- +// CLASS: TrendlineAnalyser +// --------------------------------------------------------------------- +// A utility class to detect price crossovers with a trendline buffer. +// Supports both crossover detection and trend direction checks. +// --------------------------------------------------------------------- class TrendlineAnalyser { - private: +private: MarketDataUtils market_data_utils; - public: - // Main method: detects whether price has crossed the trendline up (long) or down (short) - // Parameters: - // - symbol: the symbol we're evaluating (e.g., "EURUSD") - // - handle: the indicator handle for the trendline (e.g., iMA handle) - // - cross_long: output bool set to true if a bullish cross is detected - // - cross_short: output bool set to true if a bearish cross is detected - void detect_cross(string symbol, int handle, bool& cross_long, bool& cross_short, int shift=1) { - cross_long = false; - cross_short = false; - - // --- Retrieve prices and trendline values from last 2 closed bars - double price = iClose(symbol, PERIOD_CURRENT, shift); // most recent closed bar - double prev_price = iClose(symbol, PERIOD_CURRENT, shift + 1); // bar before that - - double trendline = market_data_utils.get_buffer_value(handle, shift); // trendline now - double prev_trendline = market_data_utils.get_buffer_value(handle, shift+1); // trendline before - - // --- Exit early if any of the data is missing or invalid - if (price == EMPTY_VALUE || prev_price == EMPTY_VALUE || trendline == EMPTY_VALUE || prev_trendline == EMPTY_VALUE) { - return; - } - - // --- Detect bullish crossover (price moves from below to above the trendline) - cross_long = (prev_price < prev_trendline && price > trendline); - - // --- Detect bearish crossover (price moves from above to below the trendline) - cross_short = (prev_price > prev_trendline && price < trendline); - } - // METHOD: determines if price is currently trending above or below the trendline - // Parameters: - // - symbol: trading symbol (e.g., "EURUSD") - // - handle: trendline indicator handle - // - is_direction_long: output bool, true if price is above trendline - // - is_direction_short: output bool, true if price is below trendline - // - shift: which candle to check (default: 1 = last closed bar) - void trend_direction(string symbol, int handle, bool& direction_long, bool& direction_short, int shift = 1) { - direction_long = false; - direction_short = false; - - double price = iClose(symbol, PERIOD_CURRENT, shift); - double trendline = market_data_utils.get_buffer_value(handle, shift); - - if (price == EMPTY_VALUE || trendline == EMPTY_VALUE) { - return; - } - - direction_long = (price > trendline); - direction_short = (price < trendline); - } +public: + void detect_cross(string symbol, int handle, bool& cross_long, bool& cross_short, int shift = 1); + void trend_direction(string symbol, int handle, bool& direction_long, bool& direction_short, int shift = 1); }; + +// --------------------------------------------------------------------- +// Detects whether price has crossed above (long) or below (short) +// a trendline between the two most recent closed bars. +// +// Parameters: +// - symbol : Trading symbol (e.g., "EURUSD"). +// - handle : Indicator handle for the trendline buffer. +// - cross_long : Output true if bullish crossover detected. +// - cross_short : Output true if bearish crossover detected. +// - shift : Bar shift to evaluate (default: 1 = last closed bar). +// +// Logic: +// - Retrieves price and trendline values for the current and previous bar. +// - Checks for a crossover by comparing price vs trendline movement. +// --------------------------------------------------------------------- +void TrendlineAnalyser::detect_cross(string symbol, int handle, bool& cross_long, bool& cross_short, int shift) { + cross_long = false; + cross_short = false; + + double price = iClose(symbol, PERIOD_CURRENT, shift); + double prev_price = iClose(symbol, PERIOD_CURRENT, shift + 1); + + double trendline = market_data_utils.get_buffer_value(handle, shift); + double prev_trendline = market_data_utils.get_buffer_value(handle, shift + 1); + + if (price == EMPTY_VALUE || prev_price == EMPTY_VALUE || trendline == EMPTY_VALUE || prev_trendline == EMPTY_VALUE) + return; + + cross_long = (prev_price < prev_trendline && price > trendline); + cross_short = (prev_price > prev_trendline && price < trendline); +} + +// --------------------------------------------------------------------- +// Checks if price is currently above or below the trendline. +// +// Parameters: +// - symbol : Trading symbol (e.g., "EURUSD"). +// - handle : Trendline indicator handle. +// - direction_long : Output true if price is above trendline. +// - direction_short : Output true if price is below trendline. +// - shift : Bar index to check (default: 1). +// +// Logic: +// - Retrieves price and trendline at the specified shift. +// - Compares relative position of price to trendline. +// --------------------------------------------------------------------- +void TrendlineAnalyser::trend_direction(string symbol, int handle, bool& direction_long, bool& direction_short, int shift) { + direction_long = false; + direction_short = false; + + double price = iClose(symbol, PERIOD_CURRENT, shift); + double trendline = market_data_utils.get_buffer_value(handle, shift); + + if (price == EMPTY_VALUE || trendline == EMPTY_VALUE) + return; + + direction_long = (price > trendline); + direction_short = (price < trendline); +}