360 lines
14 KiB
MQL5
360 lines
14 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| IndianTrailingStopLoss.mq5 |
|
|
//| Jay Davis |
|
|
//| 512jay.github.io |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Jay Davis"
|
|
#property link "512jay.github.io"
|
|
#property version "3.18"
|
|
// Add the first trade details on ITSL like BUY, PRICE and STOP LOSS
|
|
enum TradeDirection
|
|
{
|
|
Buy,
|
|
Sell,
|
|
BuyLimit, // BuyLimit Pending buy with entry below current price
|
|
SellLimit, // SellLimit Pending sell with entry above current price
|
|
BuyStop, // BuyStop Pending buy with entry above current price
|
|
SellStop // SellStop Pending sell with entry below current price
|
|
};
|
|
enum EA_MODE
|
|
{
|
|
Normal, // Normal
|
|
Reverse_Entries, // Reverse Entries
|
|
Reverse_Signal // Reverse Signal (Fool's Gold)
|
|
};
|
|
input group "|---------- General Settings -----------|"
|
|
input EA_MODE Mode = Normal;
|
|
#include "../GrayMatrixLibraries/Virtualization.mqh"
|
|
#include "../GrayMatrixLibraries/Trade.mqh"
|
|
input string DatabaseName = "DATABASE"; //\AppData\Roaming\MetaQuotes\Terminal\Common\DATABASE
|
|
input long MagicNumber = 7775; // Magic Number
|
|
input TradeDirection InitialDirection = Buy; // Initial Direction to Trade
|
|
input double Price = 0; // At Price
|
|
input double Stoploss = 0; // Inital StopLoss
|
|
extern ENUM_ORDER_TYPE DirectionToTrade = (ENUM_ORDER_TYPE) InitialDirection;
|
|
input string InpSym = NULL; // Charting Symbol
|
|
input double BuyInitialLotSize = 0.01; // BUY Initial Lot Size
|
|
input double SellInitialLotSize = 0.01; // SELL Initial Lot Size
|
|
#include "SLM.mqh"
|
|
#include "AddOnOrdersManagement.mqh"
|
|
#include "../GrayMatrixLibraries/WhatsAppAPI.mqh"
|
|
GrayMatrixTrade trade;
|
|
Virtual Virt[];
|
|
double MostRecentOpenedPrice;
|
|
int IdleMovements = 1, SeriesOrderNumber = 0;
|
|
datetime TimeStopLossWasLastModified;
|
|
double MinMovement = MinimumPointsForStopLossMove * _Point;
|
|
string Sym;
|
|
bool InitialOrderTaken;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
InitialOrderTaken = false;
|
|
Print("********* Starting... ", __FILE__, " ********************************* ");
|
|
Print("Virt Size = ", ArraySize(Virt));
|
|
if(ErrorsWithInputs())
|
|
return(INIT_PARAMETERS_INCORRECT);
|
|
CreateDataBase(DatabaseName, false);
|
|
InitializeVirtualObjectArray(Virt);
|
|
Sym = _Symbol;
|
|
string symbols[];
|
|
ArrayResize(symbols, 1);
|
|
symbols[0] = Sym;
|
|
FillVirtualOrderArray(Virt, symbols);
|
|
trade.SetExpertMagicNumber(MagicNumber);
|
|
trade.LogLevel(LOG_LEVEL_ALL);
|
|
if(MQLInfoInteger(MQL_TESTER))
|
|
trade.LogLevel(LOG_LEVEL_NO);
|
|
CheckForInitialOrder();
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
ObjectsDeleteAll(0);
|
|
Comment("");
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert tick function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
if(!CheckForInitialOrder())
|
|
return;
|
|
int size = ArraySize(Virt);
|
|
//Print("Virt Size = ", size);
|
|
if(NoOpenVirtualOrders(Virt))
|
|
{
|
|
PlaceOrder(Opposite(Virt[size - 1].request.type));
|
|
}
|
|
//Dir = Opposite(Dir);
|
|
ModifyStopLoss();
|
|
CheckStops();
|
|
DisplayLines(Virt[FirstActive(Virt, Sym)]);
|
|
Comment(CurrentTradeTradeTypeStopLoss(Virt),
|
|
" Cur Dir: ", EnumToString(Virt[size - 1].request.type));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| True if initial order has been taken if not it tries to take it |
|
|
//+------------------------------------------------------------------+
|
|
bool CheckForInitialOrder()
|
|
{
|
|
if(InitialOrderTaken || UpdateFromResumeTableAtStart)
|
|
return true;
|
|
ENUM_ORDER_TYPE direction = (ENUM_ORDER_TYPE) InitialDirection;
|
|
if(Price == 0 && Stoploss == 0)
|
|
if(PlaceOrder(direction))
|
|
InitialOrderTaken = true;
|
|
if(direction == ORDER_TYPE_BUY_LIMIT && SymbolInfoDouble(_Symbol, SYMBOL_ASK) <= Price)
|
|
if(PlaceOrder(ORDER_TYPE_BUY))
|
|
InitialOrderTaken = true;
|
|
if(direction == ORDER_TYPE_SELL_LIMIT && SymbolInfoDouble(_Symbol, SYMBOL_BID) >= Price)
|
|
if(PlaceOrder(ORDER_TYPE_SELL))
|
|
InitialOrderTaken = true;
|
|
if(direction == ORDER_TYPE_BUY_STOP && SymbolInfoDouble(_Symbol, SYMBOL_ASK) >= Price)
|
|
if(PlaceOrder(ORDER_TYPE_BUY))
|
|
InitialOrderTaken = true;
|
|
if(direction == ORDER_TYPE_SELL_STOP && SymbolInfoDouble(_Symbol, SYMBOL_BID) <= Price)
|
|
if(PlaceOrder(ORDER_TYPE_SELL))
|
|
InitialOrderTaken = true;
|
|
return InitialOrderTaken;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Takes the trade in the direction indicated |
|
|
//+------------------------------------------------------------------+
|
|
bool PlaceOrder(ENUM_ORDER_TYPE dir)
|
|
{
|
|
ENUM_ORDER_TYPE direction = dir;
|
|
if(Mode == Reverse_Signal)
|
|
direction = Opposite(dir);
|
|
if(dir == ORDER_TYPE_BUY)
|
|
return PlaceABuyOrder();
|
|
if(dir == ORDER_TYPE_SELL)
|
|
return PlaceASellOrder();
|
|
return false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the opposite order type |
|
|
//+------------------------------------------------------------------+
|
|
ENUM_ORDER_TYPE Opposite(ENUM_ORDER_TYPE dir)
|
|
{
|
|
if(dir == ORDER_TYPE_BUY)
|
|
return ORDER_TYPE_SELL;
|
|
else // dir = ORDER_TYPE_SELL
|
|
return ORDER_TYPE_BUY;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Prints new trade message to journal |
|
|
//+------------------------------------------------------------------+
|
|
void PrintNewTradeMessage(ENUM_ORDER_TYPE dir, double sl)
|
|
{
|
|
Print(" |||---->>> Dir = ", EnumToString(dir),
|
|
" ATR = ", AverageTrueRange(Sym, TimeframeOfATR, PeriodsInATR),
|
|
" Sell% = ", GetPercentage(Sym, dir, SellStopLossPercentage),
|
|
" StopLoss = ", sl);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Places a sell market order |
|
|
//+------------------------------------------------------------------+
|
|
bool PlaceASellOrder()
|
|
{
|
|
//Print(__FUNCTION__);
|
|
double sl = GetStopLoss(Sym, ORDER_TYPE_SELL, false);
|
|
if(InitialOrderTaken == false && Stoploss != 0)
|
|
sl = Stoploss;
|
|
double bid = SymbolInfoDouble(Sym, SYMBOL_BID);
|
|
double size = LotSizer(Sell);
|
|
string comment = "Sell #1";
|
|
VirtualSell(Virt, size, Sym, bid, sl, 0, comment);
|
|
int arraySize = ArraySize(Virt);
|
|
NoticeMessage("ITSL", Virt[arraySize - 1], 2);
|
|
DisplayLines(Virt[arraySize - 1]);
|
|
if(Mode == Reverse_Entries)
|
|
{
|
|
if(trade.Buy(size, Sym, 0, 0, 0, comment))
|
|
return true;
|
|
}
|
|
if(trade.Sell(size, Sym, bid, useVirtualStops ? 0 : sl, 0, comment))
|
|
return true;
|
|
return false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Set stoploss lines |
|
|
//+------------------------------------------------------------------+
|
|
void DisplayLines(Virtual & virt)
|
|
{
|
|
//Print(__FUNCTION__);
|
|
if(virt.request.type == ORDER_TYPE_BUY)
|
|
{
|
|
HLineCreate(0, "StopLoss", 0, virt.request.sl, clrLawnGreen, STYLE_DASHDOTDOT, 5);
|
|
HLineCreate(0, "Opened", 0, 0, clrMediumBlue, STYLE_SOLID, 3);
|
|
ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrBlack);
|
|
}
|
|
else
|
|
{
|
|
HLineCreate(0, "StopLoss", 0, virt.request.sl, clrHotPink, STYLE_DASHDOTDOT, 5);
|
|
HLineCreate(0, "Opened", 0, 0, clrFuchsia, STYLE_SOLID, 3);
|
|
ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrBlack);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Places a buy market order |
|
|
//+------------------------------------------------------------------+
|
|
bool PlaceABuyOrder()
|
|
{
|
|
//Print(__FUNCTION__);
|
|
double sl = GetStopLoss(Sym, ORDER_TYPE_BUY, false);
|
|
if(InitialOrderTaken == false && Stoploss != 0)
|
|
sl = Stoploss;
|
|
double ask = SymbolInfoDouble(Sym, SYMBOL_ASK);
|
|
double size = LotSizer(Buy);
|
|
string comment = "Buy #1";
|
|
VirtualBuy(Virt, size, Sym, ask, sl, 0, comment);
|
|
int arraySize = ArraySize(Virt);
|
|
NoticeMessage("ITSL", Virt[arraySize - 1], 2);
|
|
DisplayLines(Virt[arraySize - 1]);
|
|
if(Mode == Reverse_Entries)
|
|
{
|
|
if(trade.Sell(size, Sym, 0, 0, 0, comment))
|
|
return true;
|
|
}
|
|
if(trade.Buy(size, Sym, ask, useVirtualStops ? 0 : sl, 0, comment))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Cycles through the virtual orders and checks if any have hit the |
|
|
//| stoploss if so it issues a close order |
|
|
//+------------------------------------------------------------------+
|
|
void CheckStops()
|
|
{
|
|
//Print(__FUNCTION__);
|
|
int size = ArraySize(Virt);
|
|
for(int i = 0; i < size; i++)
|
|
{
|
|
if(Virt[i].active)
|
|
{
|
|
if(Virt[i].request.type == ORDER_TYPE_BUY)
|
|
if(SymbolInfoDouble(Sym, SYMBOL_BID) <= Virt[i].request.sl && Virt[i].request.sl != 0)
|
|
{
|
|
Print(Sym, " Bid = ", SymbolInfoDouble(Sym, SYMBOL_BID), " <= Stoploss ", Virt[i].request.sl);
|
|
if(VirtualOrderClose(Virt, i))
|
|
{
|
|
NoticeMessage("ITSL", Virt[i], 4, " switching Direction");
|
|
trade.PositionClose(Virt[i].request.symbol);
|
|
}
|
|
}
|
|
if(Virt[i].request.type == ORDER_TYPE_SELL)
|
|
if(SymbolInfoDouble(Sym, SYMBOL_ASK) >= Virt[i].request.sl && Virt[i].request.sl != 0)
|
|
{
|
|
Print(Sym, " Ask = ", SymbolInfoDouble(Sym, SYMBOL_BID), " <= Stoploss ", Virt[i].request.sl);
|
|
if(VirtualOrderClose(Virt, i))
|
|
{
|
|
NoticeMessage("ITSL", Virt[i], 4, " switching Direction");
|
|
trade.PositionClose(Virt[i].request.symbol);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculates the virtual profits that occured after a back test |
|
|
//+------------------------------------------------------------------+
|
|
double OnTester()
|
|
{
|
|
double profit = 0;
|
|
int size = ArraySize(Virt);
|
|
for(int i = 0; i < size; i++)
|
|
{
|
|
profit += Virt[i].profit;
|
|
if(verb)
|
|
printf("Virt[%d] profit %.2f ticket #%d : Balance $%.2f Entry %.2f : Exit %.2f",
|
|
i, Virt[i].profit, Virt[i].request.order, profit, Virt[i].request.price, Virt[i].close);
|
|
}
|
|
printf("Profit %.2f on %d orders", profit, size);
|
|
return (profit);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the proper lots size for the order being placed |
|
|
//+------------------------------------------------------------------+
|
|
double LotSizer(TradeDirection dir, double multiplier = 1)
|
|
{
|
|
Print(__FUNCTION__);
|
|
double size = SellInitialLotSize,
|
|
volumeMin = SymbolInfoDouble(Sym, SYMBOL_VOLUME_MIN),
|
|
volumeMax = SymbolInfoDouble(Sym, SYMBOL_VOLUME_MAX);
|
|
if(dir == Buy)
|
|
size = BuyInitialLotSize;
|
|
for(int i = 0; i < SeriesOrderNumber; i++)
|
|
size = size + size * multiplier;
|
|
//if(size > volumeMax)
|
|
// return volumeMax;
|
|
//else
|
|
if(size < volumeMin)
|
|
return volumeMin;
|
|
else
|
|
return NormalizeDouble
|
|
(size, 2);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks if the account has enough money for the trade |
|
|
//+------------------------------------------------------------------+
|
|
bool CheckMoneyForTrade(string symb, double lots, ENUM_ORDER_TYPE type)
|
|
{
|
|
Print(__FUNCTION__);
|
|
//--- Getting the opening price
|
|
MqlTick mqltick;
|
|
SymbolInfoTick(symb, mqltick);
|
|
double price = mqltick.ask;
|
|
if(type == ORDER_TYPE_SELL)
|
|
price = mqltick.bid;
|
|
//--- values of the required and free margin
|
|
double margin, free_margin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
|
|
//--- call of the checking function
|
|
if(!OrderCalcMargin(type, symb, lots, price, margin))
|
|
{
|
|
//--- something went wrong, report and return false
|
|
Print("Error in ", __FUNCTION__, " code=", GetLastError());
|
|
Print(__FUNCSIG__, symb, " ", lots, " ", type);
|
|
return(false);
|
|
}
|
|
//--- if there are insufficient funds to perform the operation
|
|
if(margin > free_margin)
|
|
return(false);
|
|
//--- checking successful
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks for errors with inputs |
|
|
//+------------------------------------------------------------------+
|
|
bool ErrorsWithInputs()
|
|
{
|
|
bool inputError = false;
|
|
if(Price == 0 && (InitialDirection > Sell))
|
|
{
|
|
Print("A Price must be specified for ", EnumToString(InitialDirection), " order type!");
|
|
Alert("A Price must be specified for ", EnumToString(InitialDirection), " order type!");
|
|
inputError = true;
|
|
}
|
|
if(!UpdateFromResumeTableAtStart) // If not resuming
|
|
{
|
|
if((BuyStopLossPercentage <= 0 || SellStopLossPercentage <= 0)
|
|
|| (BuyIdleMinutesRequiringAMove <= 0 && BuyMoveStopLossIfIdle)
|
|
|| (SellIdleMinutesRequiringAMove <= 0 && SellMoveStopLossIfIdle))
|
|
{
|
|
Print("Inputs Error Check: Stop Loss Percentage and Idle Minutes Settings");
|
|
inputError = true;
|
|
}
|
|
if(inputError)
|
|
Alert("Error with inputs! EA Stopping");
|
|
}
|
|
return inputError;
|
|
}
|
|
//+------------------------------------------------------------------+
|