//+------------------------------------------------------------------+ //| Custom Logic For Trading MT5 Mini.mq5 | //| Jollie Roger | //| SPDX-License-Identifier: MIT | //+------------------------------------------------------------------+ #property copyright "Jollie Roger" #property link "https://www.mql5.com/en/market/product/121666" #property version "2.00" #property description "Automated trading script designed to execute trades based on custom indicator signals." #property strict #include // Define the enum for Signal Types enum SignalType { STCrossOverPrice, // Cross over price STCrossUnderPrice, // Cross under price STCrossOverLevel, // Cross over level STCrossUnderLevel, // Cross under level STSymbol // Symbol/arrow }; // Define the enum for Logic Direction enum LogicDirection { DirectLogic, // Direct ReversalLogic // Reversal }; // Define the enum for Trading Side enum TradingSide { LongSideOnly, // Long ShortSideOnly, // Short BothSides // Both }; // Input parameters input string indicator_setting = "=== Indicator Setting ===";// Indicator Setting input string indicator_name = ""; // Indicator name input int buy_stream_index = 0; // Buy stream index input double buy_level = 0; // Buy level input SignalType buy_signal = STCrossUnderPrice; // Buy signal input int sell_stream_index = 0; // Sell stream index input double sell_level = 0; // Sell level input SignalType sell_signal = STCrossOverPrice; // Sell signal input string position_sizing = "=== Position Sizing ===";// Position Sizing input double lots_value = 0.01; // Position size input double take_profit = 0; // Take Profit in points input double stop_loss = 0; // Stop Loss in points input int slippage_points = 3; // Slippage, points input int magic_number = 42; // Magic number input string trade_comment = ""; // Comment for orders input string trading_parameters = "=== Trading Parameters ==="; // Trading Parameters input bool close_on_opposite = true; // Close on opposite signal input LogicDirection logic_direction = DirectLogic; // Logic direction input TradingSide trading_side = BothSides; // Trading side input int shift = 0; // Shift input int maximum_order = 0; // Maximum order allowed (0 = not used) input string breakeven_feature = "=== Breakeven ===";// Breakeven feature input bool use_break_even = false; // Use break even feature input double break_even_trigger = 0; // Break even trigger in points input double break_even_target = 0; // Break even target in points // Global variables CTrade trade; int indicator_handle; bool prev_buy_signal = false; bool prev_sell_signal = false; bool initial_buy_signal_ignored = false; bool initial_sell_signal_ignored = false; bool ready_to_buy = false; bool ready_to_sell = false; int max_position = 1; bool long_signal_history[]; bool short_signal_history[]; int max_history_size = 1000; // Function to get signal from the indicator double GetSignal(string symbol, ENUM_TIMEFRAMES tf, int period, int stream) { double buffer[]; ArraySetAsSeries(buffer, true); int copied = CopyBuffer(indicator_handle, stream, period, 1, buffer); if(copied == 1) return buffer[0]; return EMPTY_VALUE; } // Function to check long condition bool IsLongCondition(int period, bool &signalAvailable) { double value = GetSignal(Symbol(), PERIOD_CURRENT, period, buy_stream_index); signalAvailable = (value != EMPTY_VALUE && MathIsValidNumber(value)); if(!signalAvailable) return false; bool conditionMet = false; // Initialize conditionMet to false switch(buy_signal) { case STCrossOverPrice: conditionMet = value > iClose(Symbol(), PERIOD_CURRENT, period); break; case STCrossUnderPrice: conditionMet = value < iClose(Symbol(), PERIOD_CURRENT, period); break; case STCrossOverLevel: conditionMet = value > buy_level; break; case STCrossUnderLevel: conditionMet = value < buy_level; break; case STSymbol: conditionMet = value != 0 && value != EMPTY_VALUE; break; } return logic_direction == DirectLogic ? conditionMet : !conditionMet; } // Function to check short condition bool IsShortCondition(int period, bool &signalAvailable) { double value = GetSignal(Symbol(), PERIOD_CURRENT, period, sell_stream_index); signalAvailable = (value != EMPTY_VALUE && MathIsValidNumber(value)); if(!signalAvailable) return false; bool conditionMet = false; // Initialize conditionMet to false switch(sell_signal) { case STCrossOverPrice: conditionMet = value > iClose(Symbol(), PERIOD_CURRENT, period); break; case STCrossUnderPrice: conditionMet = value < iClose(Symbol(), PERIOD_CURRENT, period); break; case STCrossOverLevel: conditionMet = value > sell_level; break; case STCrossUnderLevel: conditionMet = value < sell_level; break; case STSymbol: conditionMet = value != 0 && value != EMPTY_VALUE; break; } return logic_direction == DirectLogic ? conditionMet : !conditionMet; } // IsLongCondition and IsShortCondition functions remain mostly the same // Replace iClose with: // double close = iClose(Symbol(), PERIOD_CURRENT, period); //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int PositionTotal() { int count = 0; // Loop through all positions for(int i = PositionsTotal() - 1; i >= 0; i--) { // Select the position by index ulong ticket = PositionGetTicket(i); if(ticket <= 0) continue; // Check if position matches current symbol and magic number string position_symbol = PositionGetString(POSITION_SYMBOL); long position_magic = PositionGetInteger(POSITION_MAGIC); if(position_symbol == Symbol() && position_magic == magic_number) { count++; } } return count; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void ClosePosition(ENUM_POSITION_TYPE type) { for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(PositionSelectByTicket(ticket)) { if(PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_TYPE) == type && PositionGetInteger(POSITION_MAGIC) == magic_number) { ResetLastError(); bool closed = trade.PositionClose(ticket); uint retcode = trade.ResultRetcode(); if(!closed || (retcode != TRADE_RETCODE_DONE && retcode != TRADE_RETCODE_DONE_PARTIAL && retcode != TRADE_RETCODE_PLACED)) PrintFormat("Error closing position: retcode=%u (%s), error=%d", retcode, trade.ResultRetcodeDescription(), GetLastError()); } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void ExecutionTrade() { int currentPeriod = shift; bool longSignalAvailable = false; bool shortSignalAvailable = false; bool longCondition = IsLongCondition(currentPeriod, longSignalAvailable); bool shortCondition = IsShortCondition(currentPeriod, shortSignalAvailable); if(close_on_opposite) { if(shortSignalAvailable && (trading_side == LongSideOnly || trading_side == BothSides) && shortCondition) { ClosePosition(POSITION_TYPE_BUY); } if(longSignalAvailable && (trading_side == ShortSideOnly || trading_side == BothSides) && longCondition) { ClosePosition(POSITION_TYPE_SELL); } } bool isAvailableQuota = (maximum_order - PositionTotal() > 0 || maximum_order == 0); // Update long signal history array only after a valid indicator read. if(longSignalAvailable) { if(ArraySize(long_signal_history) >= max_history_size) { for(int i = 1; i < ArraySize(long_signal_history); i++) long_signal_history[i - 1] = long_signal_history[i]; ArrayResize(long_signal_history, ArraySize(long_signal_history) - 1); } ArrayResize(long_signal_history, ArraySize(long_signal_history) + 1); long_signal_history[ArraySize(long_signal_history) - 1] = longCondition; } // Update short signal history array only after a valid indicator read. if(shortSignalAvailable) { if(ArraySize(short_signal_history) >= max_history_size) { for(int i = 1; i < ArraySize(short_signal_history); i++) short_signal_history[i - 1] = short_signal_history[i]; ArrayResize(short_signal_history, ArraySize(short_signal_history) - 1); } ArrayResize(short_signal_history, ArraySize(short_signal_history) + 1); short_signal_history[ArraySize(short_signal_history) - 1] = shortCondition; } // Check if the current long signal is valid (changed from false to true if(longSignalAvailable && ArraySize(long_signal_history) >= 2 && long_signal_history[ArraySize(long_signal_history) - 1] && !long_signal_history[ArraySize(long_signal_history) - 2]) { if((trading_side == LongSideOnly || trading_side == BothSides) && longCondition && isAvailableQuota) { Print("Valid Long Signal Detected"); double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); double tpPrice = take_profit > 0 ? ask + take_profit * SymbolInfoDouble(Symbol(), SYMBOL_POINT) : 0; double slPrice = stop_loss > 0 ? ask - stop_loss * SymbolInfoDouble(Symbol(), SYMBOL_POINT) : 0; ResetLastError(); bool opened = trade.Buy(lots_value, Symbol(), ask, slPrice, tpPrice, trade_comment); uint retcode = trade.ResultRetcode(); if(!opened || (retcode != TRADE_RETCODE_DONE && retcode != TRADE_RETCODE_DONE_PARTIAL && retcode != TRADE_RETCODE_PLACED)) PrintFormat("Error opening buy position: retcode=%u (%s), error=%d", retcode, trade.ResultRetcodeDescription(), GetLastError()); } } if(shortSignalAvailable && ArraySize(short_signal_history) >= 2 && short_signal_history[ArraySize(short_signal_history) - 1] && !short_signal_history[ArraySize(short_signal_history) - 2]) { if((trading_side == ShortSideOnly || trading_side == BothSides) && shortCondition && isAvailableQuota) { Print("Valid Short Signal Detected"); double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); double tpPrice = take_profit > 0 ? bid - take_profit * SymbolInfoDouble(Symbol(), SYMBOL_POINT) : 0; double slPrice = stop_loss > 0 ? bid + stop_loss * SymbolInfoDouble(Symbol(), SYMBOL_POINT) : 0; ResetLastError(); bool opened = trade.Sell(lots_value, Symbol(), bid, slPrice, tpPrice, trade_comment); uint retcode = trade.ResultRetcode(); if(!opened || (retcode != TRADE_RETCODE_DONE && retcode != TRADE_RETCODE_DONE_PARTIAL && retcode != TRADE_RETCODE_PLACED)) PrintFormat("Error opening sell position: retcode=%u (%s), error=%d", retcode, trade.ResultRetcodeDescription(), GetLastError()); } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CheckBreakEven() { 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) { double breakEvenLevel; if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { breakEvenLevel = PositionGetDouble(POSITION_PRICE_OPEN) + break_even_target * SymbolInfoDouble(Symbol(), SYMBOL_POINT); if(SymbolInfoDouble(Symbol(), SYMBOL_BID) - PositionGetDouble(POSITION_PRICE_OPEN) >= break_even_trigger * SymbolInfoDouble(Symbol(), SYMBOL_POINT) && (PositionGetDouble(POSITION_SL) < PositionGetDouble(POSITION_PRICE_OPEN) || PositionGetDouble(POSITION_SL) == 0)) { ResetLastError(); bool modified = trade.PositionModify(ticket, breakEvenLevel, PositionGetDouble(POSITION_TP)); uint retcode = trade.ResultRetcode(); if(!modified || (retcode != TRADE_RETCODE_DONE && retcode != TRADE_RETCODE_DONE_PARTIAL && retcode != TRADE_RETCODE_PLACED)) PrintFormat("Error modifying buy position for break-even: retcode=%u (%s), error=%d", retcode, trade.ResultRetcodeDescription(), GetLastError()); } } else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { breakEvenLevel = PositionGetDouble(POSITION_PRICE_OPEN) - break_even_target * SymbolInfoDouble(Symbol(), SYMBOL_POINT); if(PositionGetDouble(POSITION_PRICE_OPEN) - SymbolInfoDouble(Symbol(), SYMBOL_ASK) >= break_even_trigger * SymbolInfoDouble(Symbol(), SYMBOL_POINT) && (PositionGetDouble(POSITION_SL) > PositionGetDouble(POSITION_PRICE_OPEN) || PositionGetDouble(POSITION_SL) == 0)) { ResetLastError(); bool modified = trade.PositionModify(ticket, breakEvenLevel, PositionGetDouble(POSITION_TP)); uint retcode = trade.ResultRetcode(); if(!modified || (retcode != TRADE_RETCODE_DONE && retcode != TRADE_RETCODE_DONE_PARTIAL && retcode != TRADE_RETCODE_PLACED)) PrintFormat("Error modifying sell position for break-even: retcode=%u (%s), error=%d", retcode, trade.ResultRetcodeDescription(), GetLastError()); } } } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnTick() { ExecutionTrade(); if(use_break_even) CheckBreakEven(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int OnInit() { if(indicator_name == "") { Print("Initialization failed: indicator_name is empty."); return(INIT_PARAMETERS_INCORRECT); } if(lots_value <= 0 || shift < 0 || maximum_order < 0 || slippage_points < 0 || buy_stream_index < 0 || sell_stream_index < 0 || magic_number < 0 || take_profit < 0 || stop_loss < 0 || break_even_target < 0) { Print("Initialization failed: one or more numeric inputs are outside their valid range."); return(INIT_PARAMETERS_INCORRECT); } if(use_break_even && break_even_trigger <= 0) { Print("Initialization failed: break_even_trigger must be greater than zero when break-even is enabled."); return(INIT_PARAMETERS_INCORRECT); } trade.SetExpertMagicNumber(magic_number); trade.SetDeviationInPoints(slippage_points); trade.SetTypeFillingBySymbol(Symbol()); indicator_handle = iCustom(Symbol(), PERIOD_CURRENT, indicator_name); if(indicator_handle == INVALID_HANDLE) { Print("Failed to create handle of the indicator"); return INIT_FAILED; } initial_buy_signal_ignored = false; initial_sell_signal_ignored = false; ArrayResize(long_signal_history, 0); ArrayResize(short_signal_history, 0); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(indicator_handle != INVALID_HANDLE) IndicatorRelease(indicator_handle); ArrayResize(long_signal_history, 0); ArrayResize(short_signal_history, 0); Print("EA deinitialized"); } //+------------------------------------------------------------------+