1647 lines
58 KiB
MQL4
1647 lines
58 KiB
MQL4
//+------------------------------------------------------------------+
|
||
//| 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);
|
||
}
|