mql5/Custom-Logic-Trading-Mini/Custom Logic For Trading MT5 Mini.mq5
Jetaroon Sriboonruang c17d37b7d7 Add Custom Logic Trading Mini source framework
Publish the preserved MT4/MT5 rolling signal-history framework with documentation, license, changelog, and flow image.
2026-09-04 19:58:50 +00:00

399 lines
17 KiB
MQL5

//+------------------------------------------------------------------+
//| 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 <Trade\Trade.mqh>
// 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");
}
//+------------------------------------------------------------------+