//+------------------------------------------------------------------+ //| Custom Logic For Trading Mini.mq4 | //| Jollie Roger | //| SPDX-License-Identifier: MIT | //+------------------------------------------------------------------+ #property copyright "Jollie Roger" #property link "https://www.mql5.com/en/market/product/119607" #property version "3.00" #property description "Automated trading script designed to execute trades based on custom indicator signals." #property strict // 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 // Variables to track last signals and trade flags bool prev_buy_signal = false; // Previous state of buy signal bool prev_sell_signal = false; // Previous state of sell signal bool initial_buy_signal_ignored = false; // Flag to ignore the first buy signal bool initial_sell_signal_ignored = false; // Flag to ignore the first sell signal bool ready_to_buy = false; bool ready_to_sell = false; int max_position = 1; // Arrays to store recent history of long and short signals bool long_signal_history[]; bool short_signal_history[]; // Maximum size of signal history arrays int max_history_size = 1000; // Function to get signal from the indicator double GetSignal(string symbol, ENUM_TIMEFRAMES tf, int period, int stream) { return iCustom(symbol, tf, indicator_name, stream, period); } // 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); 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); 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; } // Function to get the total number of positions for the current symbol and magic number int positionTotal() { int count = 0; int totalOrders = OrdersTotal(); for(int i = 0; i < totalOrders; i++) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderSymbol() == Symbol() && OrderMagicNumber() == magic_number) { count++; } } } return count; } // Function to close positions of a certain type void ClosePosition(int type) { for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderSymbol() == Symbol() && OrderType() == type && OrderMagicNumber() == magic_number) { if(!OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), slippage_points, clrNONE)) { Print("Error closing position: ", GetLastError()); } } } } } // Execution trade void ExecutionTrade() { if(shift < 0 || shift >= Bars) return; int currentPeriod = iBarShift(Symbol(), PERIOD_CURRENT, Time[shift]); if(currentPeriod < 0) return; bool longSignalAvailable = false; bool shortSignalAvailable = false; bool longCondition = IsLongCondition(currentPeriod, longSignalAvailable); bool shortCondition = IsShortCondition(currentPeriod, shortSignalAvailable); // Check for opposite signals and close positions if needed if(close_on_opposite) { if(shortSignalAvailable && (trading_side == LongSideOnly || trading_side == BothSides) && shortCondition) { ClosePosition(OP_BUY); } if(longSignalAvailable && (trading_side == ShortSideOnly || trading_side == BothSides) && longCondition) { ClosePosition(OP_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 the current long signal is valid, open a long position if((trading_side == LongSideOnly || trading_side == BothSides) && longCondition && isAvailableQuota) { Print("Valid Long Signal Detected"); // Debug print for valid long signal // Calculate TP and SL prices for buy order double tpPrice = take_profit > 0 ? Ask + take_profit * Point : 0; double slPrice = stop_loss > 0 ? Ask - stop_loss * Point : 0; // Open a new buy position int ticket = OrderSend(Symbol(), OP_BUY, lots_value, Ask, slippage_points, slPrice, tpPrice, trade_comment, magic_number, 0, Blue); if(ticket < 0) Print("Error opening buy order: ", GetLastError()); } } // Check if the current short signal is valid (changed from false to true) if(shortSignalAvailable && ArraySize(short_signal_history) >= 2 && short_signal_history[ArraySize(short_signal_history) - 1] && !short_signal_history[ArraySize(short_signal_history) - 2]) { // If the current short signal is valid, open a short position if((trading_side == ShortSideOnly || trading_side == BothSides) && shortCondition && isAvailableQuota) { Print("Valid Short Signal Detected"); // Debug print for valid short signal // Calculate TP and SL prices for sell order double tpPrice = take_profit > 0 ? Bid - take_profit * Point : 0; double slPrice = stop_loss > 0 ? Bid + stop_loss * Point : 0; // Open a new sell position int ticket = OrderSend(Symbol(), OP_SELL, lots_value, Bid, slippage_points, slPrice, tpPrice, trade_comment, magic_number, 0, Red); if(ticket < 0) Print("Error opening sell order: ", GetLastError()); } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CheckBreakEven() { for(int i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderSymbol() == Symbol() && OrderMagicNumber() == magic_number) { // Calculate the break-even level double breakEvenLevel; if(OrderType() == OP_BUY) { breakEvenLevel = OrderOpenPrice() + break_even_target * Point; // Check if the price has reached the trigger point if(Bid - OrderOpenPrice() >= break_even_trigger * Point && (OrderStopLoss() < OrderOpenPrice() || OrderStopLoss() == 0)) { // Modify the stop loss to the break-even level if(!OrderModify(OrderTicket(), OrderOpenPrice(), breakEvenLevel, OrderTakeProfit(), 0, clrNONE)) { Print("Error modifying buy order for break-even: ", GetLastError()); } } } else if(OrderType() == OP_SELL) { breakEvenLevel = OrderOpenPrice() - break_even_target * Point; // Check if the price has reached the trigger point if(OrderOpenPrice() - Ask >= break_even_trigger * Point && (OrderStopLoss() > OrderOpenPrice() || OrderStopLoss() == 0)) { // Modify the stop loss to the break-even level if(!OrderModify(OrderTicket(), OrderOpenPrice(), breakEvenLevel, OrderTakeProfit(), 0, clrNONE)) { Print("Error modifying sell order for break-even: ", GetLastError()); } } } } } } } // OnTick function to handle trading logic void OnTick() { ExecutionTrade(); // Check for break-even conditions if(use_break_even) CheckBreakEven(); } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ 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 || 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); } initial_buy_signal_ignored = false; // Reset the flag for buy signal initial_sell_signal_ignored = false; // Reset the flag for sell signal ArrayResize(long_signal_history, 0); // Initialize the long signal history array ArrayResize(short_signal_history, 0); // Initialize the short signal history array return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ArrayResize(long_signal_history, 0); // Clear the long signal history array ArrayResize(short_signal_history, 0); // Clear the short signal history array Print("EA deinitialized"); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+