gryps2/EA-code/handy_systems/RELEASE-CANDIDATE/84003-LULLABY for USDTRY/LULLABY for USDTRY.mq4

1648 lines
58 KiB
MQL4
Raw Permalink Normal View History

2025-05-30 14:58:21 +02:00
//+------------------------------------------------------------------+
//| LULLABY for USDTRY.mq4 |
//| Copyright © 2024, HANDY SYSTEMS, All Rights Reserved. |
//| https://www.handy-systems.com/ |
//+------------------------------------------------------------------+
#property strict
// When compiling in PRODUCTION mode, comment out the text below.
#define _DEVELOPMENT
// When compiling in PRODUCTION mode, comment out the text above.
// To restrict the account to EA-BANK, uncomment the text below.
//#define _EA_BANK
// To restrict the account to EA-BANK, uncomment the text above.
// To use GogoJungle-style comments, uncomment the part below.
//#define _GOGOJUNGLE
// To use GogoJungle-style comments, uncomment the part above.
// To use an MQL5.com format EA, uncomment the code below.
//#define _MQL5_COM
// To use an MQL5.com format EA, uncomment the code above.
// To use LET'S REAL format EA, uncomment the code below.
//#define _LETS_REAL
// To use LET'S REAL format EA, uncomment the code above.
// To disable the logo display on the properties dialog, uncomment the line below.
//#define _DO_NOT_USE_LOGO
// To disable the logo display on the properties dialog, uncomment the line above.
#include <stdlib.mqh>
//----------EA-BANK certification begin
#ifdef _EA_BANK
#include <auth.mqh>
#endif // _EA_BANK
//----------EA-BANK certification end
#property copyright "Copyright © 2024 HANDY SYSTEMS, All Rights Reserved."
#ifndef _DO_NOT_USE_LOGO
#property icon "HANDY-SYSTEMS-LOGO.ico"
#endif // _DO_NOT_USE_LOGO
#property version "1.0" // Newly released
#ifdef _DEVELOPMENT
#define dinput input
#else
#define dinput
#endif //_DEVELOPMENT_
#ifdef _GOGOJUNGLE
const string STR_EA_ID = "XXXXX";
const string STR_EA_NAME = STR_EA_ID + ":" + "LULLABY_for_USDTRY";
#else
const string STR_EA_NAME = "LULLABY_for_USDTRY";
#endif // _GOGOJUNGLE
const string STR_OP_BUY = "BUY";
const string STR_OP_SELL = "SELL";
input int MagicNumber = 84003; // MagicNumber
input double Lots = 0.08; // Lots
// Lot control parameters
input bool CompoundMode = false; // Enable compound mode
input double RiskPercent = 2.0; // Risk percent of balance (%)
input double MinLots = 0.01; // Minimum lot size
input double MaxLots = 10.0; // Maximum lot size
input double MaxSpread = 0.0; // MaxSpread (pips, 0=auto-spread)
input double MinAllowedSwap = 0.0; // MinAllowedSwap (pips)
dinput double TakeProfit_pips = 1400.0; // TakeProfit (pips)
dinput double StopLoss_pips = 7500.0; // StopLoss (pips)
dinput double Internal_TakeProfit_pips = 770.0;
dinput double Internal_StopLoss_pips = 140.0;
// Trailing stop parameters
dinput double TrailingStopStartPips = 200.0;
dinput double TrailingStopPips = 160.0;
// Limit price cut parameters
dinput double ReLimitActionPips = 200.0;
dinput double ReLimitPips = 110.0;
// Maximum acceptable slippage
int Slippage = 120;
// Number of retry times
const int MAX_ORDER_REPEAT = 5;
// Market opening waiting time (second)
const int TIME_WAIT_FOR_MARKET_OPEN = 3600;
int xBars = 0, xxBars = 0;
double BuyStop = 0, SellStop = DBL_MAX;
double BuyLimit = DBL_MAX, SellLimit = 0;
// Maximum number of positions
int maxposition = 1;
// Whether it is operated only at the candlestick price
bool CandleStartStarting = true;
// Optimization control parameter
#define OPTIMIZE_RF 0x0001
#define OPTIMIZE_TR 0x0002
#define OPTIMIZE_WR 0x0004
// Optimization type
enum OPTYPE
{
OPTYPE_RF_ONLY = OPTIMIZE_RF, // RF (Recovery Factor)
OPTYPE_TR_ONLY = OPTIMIZE_TR, // TR (Number of trades)
OPTYPE_WR_ONLY = OPTIMIZE_WR, // WR (Win rate)
OPTYPE_RF_TR = OPTIMIZE_RF |
OPTIMIZE_TR, // RF+TR
OPTYPE_RF_WR = OPTIMIZE_RF |
OPTIMIZE_WR, // RF+WR
OPTYPE_TR_WR = OPTIMIZE_TR |
OPTIMIZE_WR, // TR+WR
OPTYPE_RF_TR_WR = OPTIMIZE_RF |
OPTIMIZE_TR |
OPTIMIZE_WR, // RF+TR+WR
OPTYPE_NONE = 0 // None
};
dinput OPTYPE OP_Type = OPTYPE_RF_ONLY; // Optimization type
// Spread contol variables
dinput double AllowSpread = 0.0; // Max Spread (pips, 0=auto-spread)
dinput double Gap_Pips = 10.0; // Price gap threshold (pips)
dinput double BaseSpreadLimit = 8.0; // Initial spread threshold (pips)
dinput double VolatilityAdjustment = 1.5; // Volatility adjustment factor (multiplier)
dinput double MaxAllowableSpread = 25.0; // Maximum allowed spread (pips)
dinput int MinimumSpreadSamples = 10; // Required samples before using statistics
dinput double ExtremeSpikeMultiplier = 2.0; // Multiple of average spread to consider extreme
dinput double WeightFactor = 2.0; // Weight factor for recent values in moving averagepips)
// Entry rule variables
dinput int BB_Period = 10; // Bollinger band calculation period
dinput double BB_Angle = 85.5; // Bollinger band angle
dinput double BB_Deviation = 0.5; // Bollinger band deviation
// Entry filter variables
// Variables for exit rules
dinput int MA_Break_Period = 60; // Moving average calculation period
dinput double SL_Acceptable_Pips = 3000.0; // Acceptable unrealized loss (pips)
// Global variables for spread monitoring
double CurrentSpread, SpreadMin = DBL_MAX, SpreadMax = 0;
double SpreadTotal = 0, SpreadAvg = 0;
double DynamicSpreadLimit = 0;
int SpreadSamples = 0;
int LastMonthBar;
#ifdef _LETS_REAL
// Define LET'S REAL authentication code
int lr_product_id=000; // PRODUCT ID
#import "wininet.dll"
int InternetAttemptConnect(int x);
int InternetOpenW(string &sAgent,int lAccessType,string &sProxyName,string &sProxyBypass,int lFlags);
int InternetConnectW(int hInternet,string &szServerName,int nServerPort,string &lpszUsername,string &lpszPassword,int dwService,int dwFlags,int dwContext);
int HttpOpenRequestW(int hConnect,string &Verb,string &ObjectName,string &Version,string &Referer,string &AcceptTypes,uint dwFlags,int dwContext);
int HttpSendRequestW(int hRequest,string &lpszHeaders,int dwHeadersLength,uchar &lpOptional[],int dwOptionalLength);
int HttpQueryInfoW(int hRequest,int dwInfoLevel,int &lpvBuffer[],int &lpdwBufferLength,int &lpdwIndex);
int InternetReadFile(int hFile,uchar &sBuffer[],int lNumBytesToRead,int &lNumberOfBytesRead);
int InternetCloseHandle(int hInet);
#import
#define OPEN_TYPE_PRECONFIG 0
#define FLAG_KEEP_CONNECTION 0x00400000
#define FLAG_PRAGMA_NOCACHE 0x00000100
#define FLAG_RELOAD 0x80000000
#define SERVICE_HTTP 3
#define CloseHandle InternetCloseHandle(lr_session);InternetCloseHandle(lr_connect);InternetCloseHandle(lr_request);InternetCloseHandle(lr_send);
string
lr_is_demo="0",
lr_platform="MT4_EA",
lr_version="1.07",
lr_host="api.lets-real.com",
lr_path="index.php?route=api/register",
lr_vers="HTTP/1.1",
lr_verb="POST",
lr_head="Content-Type: application/x-www-form-urlencoded",
lr_useragent="Mozilla",
lr_result,
lr_nill,
lr_postdata,
lr_ct,
lr_msg,
lr_msg1="Authentication Failed! ",
lr_msg2=" from lets-real.com ";
int lr_port=80,lr_session,lr_connect,lr_readfile,lr_request,lr_send,lr_auth_failure_times;
bool lr_auth_success=false,lr_auth_exec=true;
datetime lr_auth_time;
#endif // _LETS_REAL
//+------------------------------------------------------------------+
//| expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
#ifdef _EA_BANK
//----------EA-BANK certification begin
if(!auth()) {
return -1;
}
//----------EA-BANK certification end
// Display the authentication OK string
Comment("EA-BANK認証:OK"); // It means "EA-BANK authentication is OK" in Japanese
#endif // _EA_BANK
#ifdef _LETS_REAL
// Define LET'S REAL authentication code
lr_is_demo=IsDemo()?"1":"0";
if(IsTesting())
{
lr_auth_exec=false;
lr_auth_success=true;
}
if(lr_auth_exec && !IsDllsAllowed())
{
lr_msg=lr_msg1+"Please allow use of DLL "+lr_msg2;
Alert(lr_msg);
Print(lr_msg);
ExpertRemove();
return(INIT_FAILED);
}
#endif // _LETS_REAL
// Hide indicators in use
HideTestIndicators(true);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
#ifdef _EA_BANK
//----------EA-BANK certification begin
if(!auth()) {
return;
}
//----------EA-BANK certification end
#endif // _EA_BANK
#ifdef _LETS_REAL
// Define LET'S REAL authentication code
if(lr_auth_exec && TimeCurrent()>=lr_auth_time)
{
CloseHandle
string lr_p0,lr_p1,lr_p2,lr_p3;
char lr_data[];
lr_p0=IntegerToString(IsTradeAllowed());
lr_p1=AccountCompany();
lr_p2=IntegerToString(AccountNumber());
lr_p3=StringSubstr(WindowExpertName(),StringLen(WindowExpertName())-8,8);
lr_postdata="trade_allowed="+lr_p0+"&fx_company="+lr_p1+"&fx_account="+lr_p2+"&key="+lr_p3+"&ver="+lr_version+"&product_id="+IntegerToString(lr_product_id)+"&platform="+lr_platform+"&is_demo="+lr_is_demo;
StringToCharArray(lr_postdata,lr_data,0,WHOLE_ARRAY,CP_UTF8);
lr_session=InternetOpenW(lr_useragent,OPEN_TYPE_PRECONFIG,lr_nill,lr_nill,0);
lr_connect=InternetConnectW(lr_session,lr_host,lr_port,lr_nill,lr_nill,SERVICE_HTTP,0,0);
lr_request=HttpOpenRequestW(lr_connect,lr_verb,lr_path,lr_vers,lr_nill,lr_nill,FLAG_KEEP_CONNECTION|FLAG_RELOAD|FLAG_PRAGMA_NOCACHE,0);
lr_send=HttpSendRequestW(lr_request,lr_head,StringLen(lr_head),lr_data,ArraySize(lr_data)-1);
if(lr_send<=0)
{
lr_msg=lr_msg1+lr_msg2+"\n"+"Please check your network."+"\n"+
"InternetAttemptConnect="+IntegerToString(InternetAttemptConnect(0))+"\n"+
"session="+IntegerToString(lr_session)+"\n"+
"connect="+IntegerToString(lr_connect)+"\n"+
"request="+IntegerToString(lr_request)+"\n"+
"send="+IntegerToString(lr_send);
Comment(lr_msg);
return;
}
else
{
uchar response[100];
int dwBytes1;
lr_result="";
while(InternetReadFile(lr_request,response,0,dwBytes1))
{
lr_readfile=InternetReadFile(lr_request,response,100,dwBytes1);
if(dwBytes1<=0)
break;
lr_result=lr_result+CharArrayToString(response,0,dwBytes1);
}
string lr_to_split=lr_result;
string lr_sep=",";
string lr_respara[];
ushort lr_u_sep=StringGetCharacter(lr_sep,0);
int k=StringSplit(lr_to_split,lr_u_sep,lr_respara);
if(k>0)
{
for(int j=0; j<k; j++)
{
StringReplace(lr_respara[j],"{","");
StringReplace(lr_respara[j],"}","");
int lr_position=StringFind(lr_respara[j],":",0);
lr_respara[j]=StringSubstr(lr_respara[j],lr_position+1);
}
}
else
{
ArrayResize(lr_respara,1);
lr_respara[0]="false";
}
if(lr_respara[0]!="true")
{
lr_auth_success=false;
lr_auth_failure_times++;
int seconds=lr_auth_failure_times>4?60:5;
lr_auth_time=TimeCurrent()+seconds;
lr_msg=lr_msg1+lr_to_split+lr_msg2;
Comment(lr_msg);
Print(lr_result,lr_msg2);
}
else
{
lr_auth_success=true;
lr_auth_failure_times=0;
lr_auth_time=TimeCurrent()+43200;
Print("Authentication Success "+lr_msg2);
if(StringLen(lr_msg)>0)
Comment("");
}
}
}
if(!lr_auth_success)
return;
#endif // _LETS_REAL
int i;
int ticket;
int CountBuy = 0,CountSell = 0;
double profit;
bool res;
double xpoint = Point;
// Adjust the value of pips according to the number of decimal places
if (Digits() == 3 || Digits() == 5)
{
xpoint = xpoint * 10;
}
// If only the candlestick opening price is active (selection type), start from here
if(Bars != xBars || CandleStartStarting == false)
{
xBars = Bars;
// -------- Close position --------
// Close existing positions
for(i=OrdersTotal()-1; i>=0; i--)
{
// Order selection (if an error occurs, exit the loop)
if (OrderSelect( i, SELECT_BY_POS, MODE_TRADES ) == false)
{
Print("OrderSelect returned the error of ", GetLastError() );
break;
}
// Confirm the order (if the currency pair does not match, return to the beginning of the for statement)
if (OrderSymbol() != Symbol()) continue;
// Check the magic number (if the magic number does not match, return to the beginning of the for statement)
if (OrderMagicNumber() != MagicNumber) continue;
if (OrderType() == OP_BUY)
{
profit = Bid - OrderOpenPrice(); // Calculate the unrealized profit and loss for a lomg position
// Settle the order at market
if (profit >= Internal_TakeProfit_pips * xpoint || profit <= -Internal_StopLoss_pips * xpoint || Exit2() == 2)
{
res = WrapperOrderClose(OrderTicket(),
OrderLots(),
Bid,
NULL,
Green);
}
}
if (OrderType() == OP_SELL)
{
profit = OrderOpenPrice() - Ask; // Calculate the unrealized profit and loss for a short position
// Settle the order at market
if (profit >= Internal_TakeProfit_pips * xpoint || profit <= -Internal_StopLoss_pips * xpoint || Exit1() == 1)
{
res = WrapperOrderClose(OrderTicket(),
OrderLots(),
Ask,
NULL,
Green);
}
}
}
// -------- Close position (until here) --------
// -------- Trailing a position --------
// Trail the SL and TP of the owned position
for (i=OrdersTotal()-1; i>=0; i--)
{
// Order selection (if an error occurs, exit the loop)
if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == false)
{
Print("OrderSelect returned the error of ", GetLastError());
break;
}
// Confirm the order (if the currency pair does not match, return to the beginning of the for statement)
if (OrderSymbol() != Symbol()) continue;
// Check the magic number (if the magic number does not match, return to the beginning of the for statement)
if (OrderMagicNumber() != MagicNumber) continue;
if (OrderType() == OP_BUY)
{
// Calculate buy position profit and trailing levels
double buyProfit = NormalizeDouble(Bid - OrderOpenPrice(), Digits());
double buyStopLevel = NormalizeDouble(TrailingStopStartPips * xpoint, Digits());
// Check trailing stop conditions
if (buyProfit >= buyStopLevel)
{
double newSL = NormalizeDouble(Bid - TrailingStopPips * xpoint, Digits());
// Update only if new SL is more favorable than current SL
if (OrderStopLoss() < newSL || OrderStopLoss() == 0)
{
res = WrapperOrderModify(OrderTicket(),
OrderOpenPrice(),
newSL,
OrderTakeProfit(),
0,
Green);
if(!res) Print("Error modifying buy trailing stop: ", GetLastError());
}
}
// Check limit order adjustment conditions
double limitLevel = NormalizeDouble(OrderOpenPrice() - ReLimitActionPips * xpoint, Digits());
if (Bid <= limitLevel)
{
double newTP = NormalizeDouble(OrderOpenPrice() + ReLimitPips * xpoint, Digits());
// Update only if take profit has changed
if (OrderTakeProfit() != newTP)
{
res = WrapperOrderModify(OrderTicket(),
OrderOpenPrice(),
OrderStopLoss(),
newTP,
0,
Green);
if(!res) Print("Error modifying buy take profit: ", GetLastError());
}
}
}
else if (OrderType() == OP_SELL)
{
// Calculate sell position profit and trailing levels
double sellProfit = NormalizeDouble(OrderOpenPrice() - Ask, Digits());
double sellStopLevel = NormalizeDouble(TrailingStopStartPips * xpoint, Digits());
// Check trailing stop conditions
if (sellProfit >= sellStopLevel)
{
double newSL = NormalizeDouble(Ask + TrailingStopPips * xpoint, Digits());
// Update only if new SL is more favorable than current SL
if (OrderStopLoss() > newSL || OrderStopLoss() == 0)
{
res = WrapperOrderModify(OrderTicket(),
OrderOpenPrice(),
newSL,
OrderTakeProfit(),
0,
Green);
if(!res) Print("Error modifying sell trailing stop: ", GetLastError());
}
}
// Check limit order adjustment conditions
double limitLevel = NormalizeDouble(OrderOpenPrice() + ReLimitActionPips * xpoint, Digits());
if (Ask >= limitLevel)
{
double newTP = NormalizeDouble(OrderOpenPrice() - ReLimitPips * xpoint, Digits());
// Update only if take profit has changed
if (OrderTakeProfit() != newTP)
{
res = WrapperOrderModify(OrderTicket(),
OrderOpenPrice(),
OrderStopLoss(),
newTP,
0,
Green);
if(!res) Print("Error modifying sell take profit: ", GetLastError());
}
}
}
}
// -------- Trailing a position (until here) --------
// -------- Entry of new position --------
// Count the number of positions
for(i=OrdersTotal()-1; i>=0; i--)
{
// Order selection (if an error occurs, exit the loop)
if (OrderSelect( i, SELECT_BY_POS, MODE_TRADES ) == false)
{
Print("OrderSelect returned the error of ", GetLastError() );
break;
}
// Confirm the order (if the currency pair does not match, return to the beginning of the for statement)
if (OrderSymbol() != Symbol()) continue;
// Check the magic number (if the magic number does not match, return to the beginning of the for statement)
if (OrderMagicNumber() != MagicNumber) continue;
if (OrderType() == OP_BUY)
{
CountBuy = CountBuy + 1;
}
if (OrderType() == OP_SELL)
{
CountSell = CountSell + 1;
}
}
double lots = CalculateCompoundLots();
double correct_volume;
string message;
// Check the order quantity
if (!CheckVolumeValue(lots, correct_volume, message))
{
// If the order quantity is incorrect, set the correct quantity
Print(message);
lots = correct_volume;
}
// Check the entry conditions and enter if they are met.
if (Entry() == 1 && CountBuy < maxposition && xxBars != Bars)
{
// Check trading funds
if (!CheckMoneyForTrade(OrderSymbol(), lots, OrderType()))
{
return;
}
ticket = WrapperOrderSend(Symbol(),
OP_BUY,
lots,
Ask,
Slippage,
0,
0,
STR_EA_NAME + " " + STR_OP_BUY,
MagicNumber,
0,
Blue);
// If the entry is successful, set TP and SL
if (ticket != -1)
{
// Select an order using the ticket (if an error occurs, do nothing)
if( OrderSelect( ticket, SELECT_BY_TICKET ) == true )
{
res = WrapperOrderModify(OrderTicket(),
OrderOpenPrice(),
OrderOpenPrice() - StopLoss_pips * xpoint,
OrderOpenPrice() + TakeProfit_pips * xpoint,
0,
MediumSeaGreen);
}
}
xxBars = Bars;
}
else if (Entry() == 2 && CountSell < maxposition && xxBars != Bars)
{
// Check trading funds
if (!CheckMoneyForTrade(OrderSymbol(), lots, OrderType()))
{
return;
}
ticket = WrapperOrderSend(Symbol(),
OP_SELL,
lots,
Bid,
Slippage,
0,
0,
STR_EA_NAME + " " + STR_OP_SELL,
MagicNumber,
0,
Red);
// If the entry is successful, set TP and SL
if (ticket != -1)
{
// Select an order using the ticket (if an error occurs, do nothing)
if( OrderSelect( ticket, SELECT_BY_TICKET ) == true )
{
res = WrapperOrderModify(OrderTicket(),
OrderOpenPrice(),
OrderOpenPrice() + StopLoss_pips * xpoint,
OrderOpenPrice() - TakeProfit_pips * xpoint,
0,
MediumSeaGreen);
}
}
xxBars = Bars;
}
// -------- Entry of new position (until here) --------
}
// The range for candlestick opening price only (selection type) is here
// -------- Set TP, SL for the position --------
// Set TP, SL in the position
for(i=OrdersTotal()-1; i>=0; i--)
{
// Order selection (if an error occurs, exit the loop)
if (OrderSelect( i, SELECT_BY_POS, MODE_TRADES ) == false)
{
Print("OrderSelect returned the error of ", GetLastError() );
break;
}
// Confirm the order (if the currency pair does not match, return to the beginning of the for statement)
if (OrderSymbol() != Symbol()) continue;
// Check the magic number (if the magic number does not match, return to the beginning of the for statement)
if (OrderMagicNumber() != MagicNumber) continue;
// In case of a buy position
if (OrderType() == OP_BUY)
{
profit = Bid - OrderOpenPrice(); // // Calculate the unrealized profit and loss for a lomg position
// If neither TP nor SL is set, set TP and SL
if (OrderStopLoss() == 0 && OrderTakeProfit() == 0)
{
res = WrapperOrderModify(OrderTicket(),
OrderOpenPrice(),
OrderOpenPrice() - StopLoss_pips * xpoint,
OrderOpenPrice() + TakeProfit_pips * xpoint,
0,
MediumSeaGreen);
}
}
// In case of a sell position
if (OrderType() == OP_SELL)
{
profit = OrderOpenPrice() - Ask; // // Calculate the unrealized profit and loss for a short position
// If neither TP nor SL is set, set TP and SL
if (OrderStopLoss() == 0 && OrderTakeProfit() == 0)
{
res = WrapperOrderModify(OrderTicket(),
OrderOpenPrice(),
OrderOpenPrice() + StopLoss_pips * xpoint,
OrderOpenPrice() - TakeProfit_pips * xpoint,
0,
MediumSeaGreen);
}
}
}
// -------- Set TP, SL for the position (until here) --------
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Entry summary |
//+------------------------------------------------------------------+
int Entry()
{
if (Entry_Rule1() == 1 && (Entry_Filter1() == 1 || Entry_Filter1() == 3)
&& (Entry_Filter2() == 1 || Entry_Filter2() == 3)
&& (Entry_Filter3() == 1 || Entry_Filter3() == 3)
&& (Entry_Filter4() == 1 || Entry_Filter4() == 3)
&& (Entry_Filter5() == 1 || Entry_Filter5() == 3)
)
{
return(1);
}
else if (Entry_Rule1() == 2 && (Entry_Filter1() == 2 || Entry_Filter1() == 3)
&& (Entry_Filter2() == 2 || Entry_Filter2() == 3)
&& (Entry_Filter3() == 2 || Entry_Filter3() == 3)
&& (Entry_Filter4() == 2 || Entry_Filter4() == 3)
&& (Entry_Filter5() == 2 || Entry_Filter5() == 3)
)
{
return(2);
}
else
{
return(0);
}
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Summary of exit for closing buying position |
//+------------------------------------------------------------------+
int Exit1()
{
if (Exit_Rule1() == 3 && Exit_Rule2() == 3 && Exit_Rule3() == 3 && Exit_Rule4() == 3 && Exit_Rule5() == 3)
{
return(0);
}
else if (Exit_Rule1() == 1)
{
return(1);
}
else if( Exit_Rule2() == 1)
{
return(1);
}
else if (Exit_Rule3() == 1)
{
return(1);
}
else if( Exit_Rule4() == 1)
{
return(1);
}
else if (Exit_Rule5() == 1)
{
return(1);
}
else
{
return(0);
}
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Summary of exit for closing selling position |
//+------------------------------------------------------------------+
int Exit2()
{
if (Exit_Rule1() == 3 && Exit_Rule2() == 3 && Exit_Rule3() == 3 && Exit_Rule4() == 3 && Exit_Rule5() == 3)
{
return(0);
}
else if (Exit_Rule1() == 2)
{
return(2);
}
else if (Exit_Rule2() == 2)
{
return(2);
}
else if (Exit_Rule3() == 2)
{
return(2);
}
else if (Exit_Rule4() == 2)
{
return(2);
}
else if (Exit_Rule5() == 2)
{
return(2);
}
else
{
return(0);
}
}
//+------------------------------------------------------------------+
//| Check the accuracy of the order quantity |
//+------------------------------------------------------------------+
bool CheckVolumeValue(double volume,double &correct_volume, string &message)
{
// Minimum volume allowed in a trade operation
double min_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
if(volume<min_volume)
{
message=StringFormat("Volume is less than the minimal allowed SYMBOL_VOLUME_MIN=%.2f",min_volume);
correct_volume = min_volume;
return(false);
}
// Maximum volume allowed in a trade operation
double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
if(volume>max_volume)
{
message=StringFormat("Volume is greater than the maximal allowed SYMBOL_VOLUME_MAX=%.2f",max_volume);
correct_volume = max_volume;
return(false);
}
// Get the minimum step for quantity change
double volume_step=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP);
int ratio=(int)MathRound(volume/volume_step);
if(MathAbs(ratio*volume_step-volume)>0.0000001)
{
message=StringFormat("Volume is not a multiple of the minimal step SYMBOL_VOLUME_STEP=%.2f, the closest correct volume is %.2f",
volume_step,ratio*volume_step);
correct_volume = ratio*volume_step;
return(false);
}
message="Correct volume value";
correct_volume = volume;
return(true);
}
//+------------------------------------------------------------------+
//| Check if trading funds are insufficient |
//+------------------------------------------------------------------+
bool CheckMoneyForTrade(string symbol, double lots,int type)
{
if (!IsTesting())
{
double free_margin=AccountFreeMarginCheck(symbol, type, lots);
// If funds are insufficient
if(free_margin<0)
{
string op = (type == OP_BUY ? "Buy" : "Sell");
Print("Not enough money for ", op," ",lots, " ", symbol, " Error code=",GetLastError());
return(false);
}
}
return(true);
}
//+------------------------------------------------------------------+
//| Calculate lot size based on account balance |
//+------------------------------------------------------------------+
double CalculateCompoundLots()
{
// If compound mode is disabled, return the fixed lot size
if (!CompoundMode)
return Lots;
double balance = AccountBalance();
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
// Adjust tick value for JPY pairs
if (Digits == 3 || Digits == 5)
tickValue *= 10;
// Calculate position size based on risk percentage
double riskAmount = balance * (RiskPercent / 100.0);
double lotSize = riskAmount / (StopLoss_pips * tickValue);
// Normalize lot size
lotSize = NormalizeDouble(lotSize, 2);
// Apply minimum and maximum lot constraints
lotSize = MathMax(MinLots, lotSize);
lotSize = MathMin(MaxLots, lotSize);
return lotSize;
}
//+------------------------------------------------------------------+
#ifdef _DEVELOPMENT
//+------------------------------------------------------------------+
//| OnTester - Optimize with the specified optimization type |
//+------------------------------------------------------------------+
double OnTester()
{
// Set the optimization criterion for the number of trades to 100 per years
const double TRADES_STANDARD = 100.0 * (Year() - TimeYear(iTime(NULL, 0, Bars(NULL, 0) - 1)));
// Initializing optimization criterion value
double optimization_value = 0.0;
double recovery_factor = 0.0;
double trades_optimization_factor = 0.0;
double win_rate = 0.0;
// To calculate the recovery factor, obtain the net profit and the maximum margin-based drawdown
double profit = TesterStatistics(STAT_PROFIT);
double max_drawdown = TesterStatistics(STAT_EQUITY_DD);
// If the maximum drawdown is non-zero, calculate the recovery factor
if(max_drawdown != 0)
{
recovery_factor = profit / max_drawdown;
}
//Get the number of trades
double trades = TesterStatistics(STAT_TRADES);
// If the number of trades is not 0, calculate the win rate (total wins/total trades)
if(trades != 0)
{
win_rate = TesterStatistics(STAT_PROFIT_TRADES) / trades;
}
// Finding the optimization coefficient for the number of trades
trades_optimization_factor = trades / TRADES_STANDARD;
// Optimization criterion value = the sum of the element values depending on the optimization type
switch(OP_Type)
{
case OPTYPE_RF_ONLY:
optimization_value = recovery_factor;
break;
case OPTYPE_TR_ONLY:
optimization_value = trades_optimization_factor;
break;
case OPTYPE_WR_ONLY:
optimization_value = win_rate;
break;
case OPTYPE_RF_TR:
optimization_value = recovery_factor * trades_optimization_factor;
break;
case OPTYPE_RF_WR:
optimization_value = recovery_factor * win_rate;
break;
case OPTYPE_TR_WR:
optimization_value = trades_optimization_factor * win_rate;
break;
case OPTYPE_RF_TR_WR:
optimization_value = recovery_factor * trades_optimization_factor * win_rate;
break;
case OPTYPE_NONE:
optimization_value = 0.0;
break;
default:
optimization_value = 0.0;
break;
}
return(optimization_value);
}
//+------------------------------------------------------------------+
#endif // _DEVELOPMENT
//+------------------------------------------------------------------+
//| Check if the market is open |
//+------------------------------------------------------------------+
bool IsMarketOpen(string symbol = NULL, int mode = SYMBOL_TRADE_MODE_FULL)
{
datetime currentTime = TimeCurrent();
// Get the trading hours for the specified symbol
int tradingHours = (int)SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE);
// Determine market conditions based on trading hours
if (tradingHours == SYMBOL_TRADE_MODE_DISABLED)
{
// Not tradable
return false;
}
else if (tradingHours == SYMBOL_TRADE_MODE_CLOSEONLY)
{
if (mode == SYMBOL_TRADE_MODE_CLOSEONLY)
{
// Only closing orders are possible
return true;
}
else
{
// Not tradable
return false;
}
}
else if (tradingHours == SYMBOL_TRADE_MODE_FULL)
{
// Normal trading available
return true;
}
// In cases other than the above, the trade will be deemed unacceptable
return false;
}
//+------------------------------------------------------------------+
//| Convert price from currency units to pips |
//+------------------------------------------------------------------+
double PriceToPips(
double price, // Price to convert to pips
int round_digits = 1 // Number of decimal places to round to
)
{
// Declare the pips value of the conversion result
double pips = 0.0;
// Calculate the conversion multiple based on the broker's decimal point holding unit
int digits = Digits();
// For 3-digit and 5-digit brokers
if(digits == 3 || digits == 5){
pips = price * MathPow(10, digits) / 10;
}
// For 2-digit and 4-digit brokers
if(digits == 2 || digits == 4){
pips = price * MathPow(10, digits);
}
// Rounds a number to a specified number of decimal places
pips = NormalizeDouble(pips, round_digits);
return(pips);
}
//+------------------------------------------------------------------+
//| Convert price from pips to currency units |
//+------------------------------------------------------------------+
double PipsToPrice(
double pips // Pips value converted to currency unit
)
{
// Declare the resulting price value
double price = 0;
// Declare the resulting price value and calculate the conversion multiple
// depending on the broker's decimal point holding unit
int digits = Digits();
// For 3-digit and 5-digit brokers
if(digits == 3 || digits == 5)
{
price = pips / MathPow(10, digits) * 10;
}
// For 2-digit and 4-digit brokers
if(digits == 2 || digits == 4)
{
price = pips / MathPow(10, digits);
}
// Round the price to the correct number of significant digits
price = NormalizeDouble(price, digits);
return(price);
}
//+------------------------------------------------------------------+
//| Convert points to pips |
//+------------------------------------------------------------------+
double PointsToPips(double points)
{
// Calculate pip value and digits dynamically
double tick_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
int pip_digits;
double pip_value;
if(_Digits == 3 || _Digits == 5)
{
pip_digits = 1;
pip_value = tick_size * 10;
}
else
{
pip_digits = 0;
pip_value = tick_size;
}
return NormalizeDouble(points / pip_value, pip_digits);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Get the angle of the hypotenuse with the two specified prices |
//| as the height and the time as the base |
//+------------------------------------------------------------------+
double GetAngle(
double price_from, // Price on the right
datetime time_from, // Time on the right
double price_to, // Price on the left
datetime time_to) // Time on the left
{
// Declaring an angle
double angle = 0.0;
// Calculate the minimum time-price difference
double time_diff = 60; // 60 seconds
double price_diff = PipsToPrice(0.1); // Price difference equivalent to 0.1 pips
// If the difference between the time value and the price is 0,
// an error occurs during division, so if the difference is 0, an angle of 0 is returned
if (time_diff != 0 && price_diff != 0)
{
// Define the correction coefficient that is the base when the maximum/minimum time value/price value
// of the chart is set as the base/height, respectively
const double bottom_correction = 1.0 / time_diff;
const double height_correction = 1.0 / price_diff;
// Calculate the bottom and height values to find the angle
double bottom =(double)(time_from - time_to) * bottom_correction;
double height = (price_from - price_to) * height_correction;
// If bottom is 0, an error occurs during division, so if bottom is 0, the angle 0 is returned
if (bottom != 0.0)
{
// Calculate the angle (theta) of the hypotenuse using the inverse trigonometric function arctangent
// Note that the result is in radians
double theta = MathArctan(height / bottom);
// Convert the calculated angle of the hypotenuse from radians (radian measure) to degrees (degree measure)
angle = theta * 180.0 / M_PI;
}
}
return(angle);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Spread monitoring and control system |
//+------------------------------------------------------------------+
void MonitorSpreadConditions()
{
// Get raw spread and normalize based on digits
CurrentSpread = MarketInfo(Symbol(), MODE_SPREAD);
if(Digits == 3 || Digits == 5)
{
CurrentSpread *= 0.1;
}
// Update spread statistics
UpdateSpreadStatistics();
// Adjust spread limits based on market conditions
AdjustDynamicSpreadLimits();
}
//+------------------------------------------------------------------+
//| Update spread statistics |
//+------------------------------------------------------------------+
void UpdateSpreadStatistics()
{
// Reset statistics on new month
int currentMonthBar = iBars(NULL, PERIOD_MN1);
if(LastMonthBar != currentMonthBar)
{
ResetSpreadStatistics();
LastMonthBar = currentMonthBar;
return;
}
// Update running statistics
SpreadSamples++;
SpreadTotal += CurrentSpread;
SpreadMin = MathMin(SpreadMin, CurrentSpread);
SpreadMax = MathMax(SpreadMax, CurrentSpread);
// Calculate moving average with weight on recent values
if(SpreadSamples > 0)
{
double weight = WeightFactor / (SpreadSamples + 1);
SpreadAvg = (CurrentSpread * weight) + (SpreadAvg * (1 - weight));
}
}
//+------------------------------------------------------------------+
//| Reset spread statistics |
//+------------------------------------------------------------------+
void ResetSpreadStatistics()
{
SpreadTotal = 0;
SpreadSamples = 0;
SpreadMin = DBL_MAX;
SpreadMax = 0;
SpreadAvg = 0;
}
//+------------------------------------------------------------------+
//| Adjust dynamic spread limits |
//+------------------------------------------------------------------+
void AdjustDynamicSpreadLimits()
{
if(AllowSpread == 0) // Auto mode
{
// Calculate volatility factor based on spread range
double volatilityFactor = 1.0;
if(SpreadMax > 0 && SpreadMin < DBL_MAX)
{
volatilityFactor = (SpreadMax - SpreadMin) / SpreadAvg;
volatilityFactor = MathMin(volatilityFactor * VolatilityAdjustment, 2.0);
}
// Apply dynamic adjustment
DynamicSpreadLimit = MathMin(
SpreadAvg * (1 + volatilityFactor),
MaxAllowableSpread
);
}
}
//+------------------------------------------------------------------+
//| Check if the spread is acceptable |
//+------------------------------------------------------------------+
bool IsSpreadAcceptable()
{
// Update spread monitoring
MonitorSpreadConditions();
// Fixed spread mode
if(AllowSpread > 0)
{
return CurrentSpread <= AllowSpread;
}
// Dynamic spread mode - wait for minimum samples
if(SpreadSamples < MinimumSpreadSamples)
{
return CurrentSpread <= BaseSpreadLimit;
}
// Consider market volatility and recent spread patterns
double adjustedLimit = MaxSpread;
// Check for extreme spread conditions
if(CurrentSpread > SpreadAvg * ExtremeSpikeMultiplier)
{
return false; // Reject extreme spread spikes
}
return CurrentSpread <= adjustedLimit;
}
//+------------------------------------------------------------------+
//| Entry rule 1 |
//+------------------------------------------------------------------+
int Entry_Rule1()
{
// Entry rule 1 is here
int shift = 0;
// Recalculate point value
double point = Point;
if(Digits == 3 || Digits == 5) {
point *= 10;
}
// Spread check
if(!IsSpreadAcceptable())
{
#ifdef _DEVELOPMENT
Print("Spread condition not met: Current=", CurrentSpread,
" Limit=", AllowSpread > 0 ? AllowSpread : MaxSpread,
" Avg=", SpreadAvg);
#endif
return(0);
}
// Price movement validation
double currentClose = iClose(NULL, 0, 0);
double previousClose = iClose(NULL, 0, 1);
if(OrderType() == OP_BUY && currentClose - previousClose >= Gap_Pips * point) return(0);
if(OrderType() == OP_SELL && previousClose - currentClose >= Gap_Pips * point) return(0);
// Seasonal trading restrictions
int currentMonth = Month();
int currentDay = Day();
if((currentMonth == 12 && currentDay >= 20) ||
(currentMonth == 1 && currentDay <= 3)) {
return(0);
}
// Time-based restrictions
if(TimeHour(TimeCurrent()) == 0) return(0);
// Sell when the candlestick crosses the upper Bollinger band (do not buy)
double upper_bb1 = iBands(NULL, 0, BB_Period, BB_Deviation, 0, PRICE_CLOSE, MODE_UPPER, shift);
double upper_bb2 = iBands(NULL, 0, BB_Period, BB_Deviation, 0, PRICE_CLOSE, MODE_UPPER, shift+1);
// Sell when the candlestick crosses the upper Bollinger band
if (iClose(NULL, 0, shift) > upper_bb1 && iClose(NULL, 0, shift+1) < upper_bb2)
{
// Calculate the angle of the upper Bollinger band
double upper_angle = GetAngle(upper_bb1, iTime(NULL, 0, shift), upper_bb2, iTime(NULL, 0, shift+1));
// "Entry" when the angle of the upper Bollinger band is less than the specified angle
if (MathAbs(upper_angle) < BB_Angle)
{
return(2);
} // Sell
else
{
return(0);
// No entry
}
}
else
{
return(0); // No entry
}
// Entry rule 1 ends here
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Entry filter 1 |
//+------------------------------------------------------------------+
int Entry_Filter1()
{
// Entry filter 1 is here
// Get short swap points
double swap = MarketInfo(NULL, MODE_SWAPSHORT);
// When the price rate has 3 or 5 significant digits
if(Digits == 3 || Digits == 5)
swap /= 10.0;
// Check if swap is below minimum allowed swap
if(swap < MinAllowedSwap)
return(0); // Trade not allowed
else
return(3); // Trade allowed
// Entry filter 1 ends here
return(3);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Entry filter 2 |
//+------------------------------------------------------------------+
int Entry_Filter2()
{
// Entry filter 2 is here
// Check if the spread is acceptable
if(!IsSpreadAcceptable())
return(0); // Trade not allowed
else
return(3); // Trade allowed
// Entry filter 2 ends here
return(3);
}//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Entry filter 3 |
//+------------------------------------------------------------------+
int Entry_Filter3()
{
// Entry filter 3 is here
// Entry filter 3 ends here
return(3);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Entry filter 4 |
//+------------------------------------------------------------------+
int Entry_Filter4()
{
// Entry filter 4 is here
// Entry filter 4 ends here
return(3);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Entry filter 5 |
//+------------------------------------------------------------------+
int Entry_Filter5()
{
// Entry filter 5 is here
// Entry filter 5 ends here
return(3);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Exit rule 1 |
//+------------------------------------------------------------------+
int Exit_Rule1()
{
// Exit rule 1 is here
int shift = 0;
// If the candlestick falls below the moving average, "close buy" will be made ("close sell" will not be made).
double MA1 = iMA(NULL, 0, MA_Break_Period, 0, MODE_SMA, PRICE_CLOSE, shift);
double MA2 = iMA(NULL, 0, MA_Break_Period, 0, MODE_SMA, PRICE_CLOSE, shift+1);
// If the candlestick falls below the moving average, "close buy" will be made
if (MA1 > iClose(NULL, 0, shift) && MA2 < iClose(NULL, 0, shift+1))
{
return(1);
} // Close buy
// Exit rule 1 ends here
return(3);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Exit rule 2 |
//+------------------------------------------------------------------+
int Exit_Rule2()
{
// Exit rule 2 is here
// If there is an unrealized loss, force close
double profit = 0.0; // Unrealized profit and loss
// In case of a sell position
if(OrderType() == OP_SELL)
{
profit = OrderOpenPrice() - Ask; // Calculate the unrealized profit or loss for a short position
if(PriceToPips(profit) < -SL_Acceptable_Pips)
{
// If the unrealized loss exceeds the allowable limit, close buy will be forced
return(1);
// Close buy
}
}
// Exit rule 2 ends here
return(3);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Exit Rule 3 |
//+------------------------------------------------------------------+
int Exit_Rule3()
{
// Exit rule 3 is here
// Exit rule 3 ends here
return(3);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Exit Rule 4 |
//+------------------------------------------------------------------+
int Exit_Rule4()
{
// Exit rule 4 is here
// Exit rule 4 ends here
return(3);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Exit Rule 5 |
//+------------------------------------------------------------------+
int Exit_Rule5()
{
// Exit rule 5 is here
// Exit rule 5 ends here
return(3);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Trading functions |
//+------------------------------------------------------------------+
// Wrapper function for OrderSend
//
// Places an order after modifying SL and TP to fit market constraints
// Implements retry and error handling in case of OrderSend error
int WrapperOrderSend(string symbol, // Symbol
int cmd, // Order type
double volume, // Lots
double price, // Order price
int slippage, // Slippage
double stoploss, // StopLoss
double takeprofit, // TakeProfit
string comment=NULL, // Comment
int magic=0, // Magic number
datetime expiration=0, // Expiry date
color arrow_color=clrNONE // Arrow color
)
{
int i;
int err = 0; // Error code
int res = 0; // OrderSend return value
// Wait until the market opens
datetime currentTime = TimeCurrent();
datetime waitEndTime = currentTime + TIME_WAIT_FOR_MARKET_OPEN;
while (TimeCurrent() < waitEndTime)
{
// If the market is open, exit the loop
if (IsMarketOpen())
{
break;
}
// Wait 500msec before next retry
Sleep(500);
}
// Retry MAX_ORDER_REPEAT times
for ( i=0; i<MAX_ORDER_REPEAT; i++ )
{
res = OrderSend(symbol,
cmd,
volume,
NormalizeDouble(price, Digits()),
slippage,
NormalizeDouble(stoploss, Digits()),
NormalizeDouble(takeprofit, Digits()),
comment,
magic,
expiration,
arrow_color);
// If the order is successfully placed, the ticket number is returned and the function ends
if ( res != -1 ) return(res);
// If an order fails to be placed, an error message will be displayed and the order will be retried
if ( res == -1 )
{
err = GetLastError();
Print("OrderSend return error: code=",err, ", Error=", ErrorDescription(err));
// Wait 500msec before next retry
Sleep(500);
}
}
return(res);
}
// Wrapper function for OrderModify
//
// Places an order after modifying SL and TP to match market constraints
// Executes retry and error handling in case of an OrderModify error
bool WrapperOrderModify(int ticket, // Ticket number
double price, // Order price
double stoploss, // StopLoss
double takeprofit, // TakeProfit
datetime expiration, // Expiration date
color arrow_color // Arrow color
)
{
int i;
int err = 0; // Error code
bool res = false; // OrderModify return value
// Wait until the market opens
datetime currentTime = TimeCurrent();
datetime waitEndTime = currentTime + TIME_WAIT_FOR_MARKET_OPEN;
while (TimeCurrent() < waitEndTime)
{
// If the market is open, exit the loop
if (IsMarketOpen())
{
break;
}
// Wait 500msec before next retry
Sleep(500);
}
// Retry MAX_ORDER_REPEAT times
for ( i=0; i<MAX_ORDER_REPEAT; i++ )
{
res = OrderModify(ticket,
NormalizeDouble(price, Digits()),
NormalizeDouble(stoploss, Digits()),
NormalizeDouble(takeprofit, Digits()),
expiration,
arrow_color);
// If the order is successfully modified, return the exit flag
if (res) return(res);
// If the order modify fails, display an error and then retry
if ( !res )
{
err = GetLastError();
Print("OrderModify return error: code=",err, ", Error=", ErrorDescription(err));
// Wait 500msec before next retry
Sleep(500);
}
}
return(res);
}
// Wrapper function for OrderClose
//
// Places an order after modifying SL and TP to fit market constraints
// Executes retry and error handling in case of an OrderClose error
bool WrapperOrderClose(int ticket, // Ticket number
double lots, // Lot size
double price, // Order price
int slippage, // Slippage
color arrow_color // Arrow color
)
{
int i;
int err = 0; // Error code
bool res = false; // OrderClose return value
// Wait until the market opens
datetime currentTime = TimeCurrent();
datetime waitEndTime = currentTime + TIME_WAIT_FOR_MARKET_OPEN;
while (TimeCurrent() < waitEndTime)
{
// If the market is open, exit the loop
if (IsMarketOpen(NULL, SYMBOL_TRADE_MODE_CLOSEONLY))
{
break;
}
// Wait 500msec before next retry
Sleep(500);
}
// Retry MAX_ORDER_REPEAT times
for ( i=0; i<MAX_ORDER_REPEAT; i++ )
{
res = OrderClose(ticket,
lots,
NormalizeDouble(price, Digits()),
slippage,
arrow_color);
// If the order is successfully closed, return the exit flag
if ( res ) return(res);
// If the order close fails, an error will be displayed and the order will be retried
if ( !res )
{
err = GetLastError();
Print("OrderClose return error: code=",err, ", Error=", ErrorDescription(err));
// Wait 500msec before next retry
Sleep(500);
}
}
return(res);
}