370 lines
14 KiB
MQL4
370 lines
14 KiB
MQL4
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 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");
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//+------------------------------------------------------------------+
|