318 lines
12 KiB
MQL5
318 lines
12 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| JPBP.mqh |
|
|
//| Copyright 2018, MetaQuotes Software Corp. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2018, MetaQuotes Software Corp."
|
|
#property link "https://www.mql5.com"
|
|
|
|
#include <Trade\Trade.mqh>
|
|
CTrade trade;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| global variables |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//--- main input parameters
|
|
input group "main parameters"
|
|
input ulong MagicNumber = 1234;
|
|
input ENUM_TIMEFRAMES TimeFrame = PERIOD_D1;
|
|
input double Lots = 0; //Lots, 0=compound
|
|
input double TpSlRatio = 2; //take profit/stop loss ratio
|
|
input double SlPercent = 2;
|
|
input int AddPoint = 50;
|
|
input int TimeOffset = 0;
|
|
|
|
input group "Trailing and safe modes"
|
|
input bool SafeWave = false;//safe wave on
|
|
input bool SafeMode = false;//safe mode on
|
|
input double SafeActivationRatio = 4;
|
|
input double SafeSlRatio = 2;
|
|
input bool TrailingMode =false;//trailing mode on
|
|
input double TrailingActivationRatio = 6;
|
|
input double TrailingDistance = 20; //trailing distance
|
|
input bool DrawLines = true; //Draw safe and trailing activation lines
|
|
|
|
input group "orders"
|
|
input bool CancelOrderOnTP = true; //cancel orders on TP
|
|
input bool CancelOrderOnSL = true; //cancel orders on profitable SL
|
|
input bool CancelOrderOnLoss = false; //cancel orders on non-profitable SL
|
|
input bool OrderOnce = false; //place orders only once
|
|
|
|
|
|
|
|
//--- market variables
|
|
double volumeStep;
|
|
double tickSize;
|
|
double tickPrice;
|
|
double bid;
|
|
double ask;
|
|
bool barChanged;
|
|
long tickVolume[];
|
|
MqlRates bar[];
|
|
|
|
|
|
//--- previous tick variables
|
|
double prevBid;
|
|
double prevAsk;
|
|
double prevOpen; //open price of bar of the last tick
|
|
|
|
//--- time variables
|
|
MqlDateTime firstBar;
|
|
MqlDateTime today;
|
|
MqlDateTime startTime[];
|
|
MqlDateTime endTime[];
|
|
datetime lastRefreshDay;
|
|
datetime prevDay;
|
|
|
|
//--- account variables
|
|
double accountBalance;
|
|
double accountEquity;
|
|
|
|
//--- trade variables
|
|
double lastOpen; //open price of the bar during which the last position was opened
|
|
|
|
//--- orders and positions handling system variables
|
|
bool orderConditions;
|
|
bool resetOrderPlaced;
|
|
bool orderPlaced[2];
|
|
int arrayIndex;
|
|
int arraySize;
|
|
double cancellingSL[2];
|
|
int prevPositionTotal;
|
|
int positionTotal;
|
|
ulong positionTicket[];
|
|
double safeActivationPrice[];
|
|
double safeSL[];
|
|
double trailingActivationPrice[];
|
|
double trailingDistance[];
|
|
double trailingSL;
|
|
double safeWaveActivationPrice[];
|
|
double safeWaveSL[];
|
|
long positionType;
|
|
double positionOpenPrice;
|
|
double positionCurrentPrice;
|
|
double positionTP;
|
|
double positionSL;
|
|
ulong lastPositionTicket;
|
|
ulong posTicket;
|
|
ulong histOrderTicket;
|
|
ulong histDealTicket;
|
|
double histOrderPrice;
|
|
double initialPositionSL;
|
|
bool profitableSL;
|
|
bool timeSet;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| functions |
|
|
//+------------------------------------------------------------------+
|
|
enum limitMode{
|
|
IS_HIGHER,
|
|
IS_LOWER,
|
|
CLOSE_ABOVE,
|
|
CLOSE_BELOW,
|
|
OPEN_ABOVE,
|
|
OPEN_BELOW,
|
|
CLOSEandOPEN_ABOVE,
|
|
CLOSEandOPEN_BELOW,
|
|
PASS_ABOVE,
|
|
PASS_BELOW
|
|
};
|
|
|
|
enum priceMode{
|
|
BID, //BID
|
|
ASK //ASK
|
|
};
|
|
|
|
bool Limit(double limitValue, limitMode mode, priceMode pm = BID){
|
|
double val=0;
|
|
double prevVal=0;
|
|
if(pm == BID){
|
|
val = bid;
|
|
prevVal = prevBid;
|
|
}
|
|
else{
|
|
val = ask;
|
|
prevVal = prevAsk;
|
|
}
|
|
|
|
switch(mode){
|
|
case IS_HIGHER:
|
|
if(val > limitValue) return true;
|
|
else return false;
|
|
break;
|
|
case IS_LOWER:
|
|
if(val < limitValue) return true;
|
|
else return false;
|
|
break;
|
|
case CLOSE_ABOVE:
|
|
if(bar[1].close > limitValue && bar[1].open < limitValue) return true;
|
|
else return false;
|
|
break;
|
|
case CLOSE_BELOW:
|
|
if(bar[1].close < limitValue && bar[1].open > limitValue) return true;
|
|
else return false;
|
|
break;
|
|
case OPEN_ABOVE:
|
|
if(bar[0].open > limitValue && bar[1].open < limitValue) return true;
|
|
else return false;
|
|
break;
|
|
case OPEN_BELOW:
|
|
if(bar[0].open < limitValue && bar[1].open > limitValue) return true;
|
|
else return false;
|
|
break;
|
|
case CLOSEandOPEN_ABOVE:
|
|
if(bar[1].close > limitValue && bar[1].open < limitValue && bar[0].open > limitValue) return true;
|
|
else return false;
|
|
break;
|
|
case CLOSEandOPEN_BELOW:
|
|
if(bar[1].close < limitValue && bar[1].open > limitValue && bar[0].open < limitValue) return true;
|
|
else return false;
|
|
break;
|
|
case PASS_ABOVE:
|
|
if(prevVal < limitValue && val > limitValue) return true;
|
|
else return false;
|
|
break;
|
|
case PASS_BELOW:
|
|
if(prevVal > limitValue && val < limitValue) return true;
|
|
else return false;
|
|
break;
|
|
default:
|
|
return false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
double NormalisePrice(double p){
|
|
return (MathRound(p/tickSize)*tickSize);
|
|
}
|
|
|
|
double LotsCalculator(double openPrice, double closePrice, double balance){
|
|
double lots = NormalizeDouble(((balance*(SlPercent/100))/(MathAbs(openPrice-closePrice)*(tickPrice/tickSize)))/volumeStep,0)*volumeStep;
|
|
if(lots < SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN)){
|
|
lots = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
|
|
}
|
|
else if(lots > SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX)){
|
|
lots = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
|
|
}
|
|
return lots;
|
|
}
|
|
|
|
void setVariables(){
|
|
lastOpen = 0;
|
|
volumeStep = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
|
|
tickSize = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
|
|
tickPrice = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE);
|
|
}
|
|
|
|
void refreshVariables(){
|
|
bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
|
|
ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
|
|
accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
accountEquity = AccountInfoDouble(ACCOUNT_EQUITY);
|
|
ArraySetAsSeries(bar,true);
|
|
CopyRates(_Symbol,TimeFrame,0,3,bar);
|
|
TimeToStruct(bar[1].time,firstBar);
|
|
TimeToStruct(TimeCurrent(),today);
|
|
//check if new bar start
|
|
if(bar[0].open != prevOpen) barChanged = true;
|
|
else barChanged = false;
|
|
//refresh once a day
|
|
if(today.day != lastRefreshDay){
|
|
volumeStep = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
|
|
tickSize = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
|
|
tickPrice = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE);
|
|
lastRefreshDay = today.day;
|
|
}
|
|
}
|
|
|
|
void getReadyForNextTick(){
|
|
prevBid = bid;
|
|
prevAsk = ask;
|
|
prevOpen = bar[0].open;
|
|
prevPositionTotal = positionTotal;
|
|
}
|
|
|
|
void AddPosition(){ //add a position into the safe and trailing system. The position has to be previously selected and checked
|
|
arrayIndex = arraySize;
|
|
arraySize = arraySize + 1;
|
|
ArrayResize(positionTicket,arraySize,10);
|
|
positionTicket[arrayIndex] = PositionGetInteger(POSITION_TICKET);
|
|
positionType = PositionGetInteger(POSITION_TYPE);
|
|
positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
positionSL = PositionGetDouble(POSITION_SL);
|
|
//check if the SL is already in a safe/profitable place
|
|
if((positionType == POSITION_TYPE_BUY && positionSL < positionOpenPrice) || (positionType==POSITION_TYPE_SELL && positionSL > positionOpenPrice)){
|
|
initialPositionSL = positionSL; //if not profitable SL then the current SL is the initial SL of the position
|
|
profitableSL = false;
|
|
}
|
|
else{
|
|
profitableSL = true;
|
|
if(TrailingMode){
|
|
HistorySelect(0,TimeCurrent());
|
|
for(int j=HistoryOrdersTotal()-1; j>=0; j--){
|
|
histOrderTicket = HistoryOrderGetTicket(j);
|
|
if(HistoryOrderGetInteger(histOrderTicket,ORDER_POSITION_ID) == PositionGetInteger(POSITION_IDENTIFIER)){
|
|
initialPositionSL = HistoryOrderGetDouble(histOrderTicket,ORDER_SL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(SafeMode && !profitableSL){
|
|
ArrayResize(safeActivationPrice,arraySize,10);
|
|
ArrayResize(safeSL,arraySize,10);
|
|
if(positionType == POSITION_TYPE_BUY){
|
|
safeActivationPrice[arrayIndex] = positionOpenPrice + (positionOpenPrice-positionSL)*SafeActivationRatio;
|
|
safeSL[arrayIndex] = NormalisePrice(positionOpenPrice + (positionOpenPrice-positionSL)*SafeSlRatio);
|
|
if(safeSL[arrayIndex]<0)safeSL[arrayIndex] = 0;
|
|
}
|
|
else if(positionType==POSITION_TYPE_SELL){
|
|
safeActivationPrice[arrayIndex] = positionOpenPrice - (positionSL-positionOpenPrice)*SafeActivationRatio;
|
|
safeSL[arrayIndex] = NormalisePrice(positionOpenPrice - (positionSL-positionOpenPrice)*SafeSlRatio);
|
|
if(safeSL[arrayIndex]<0)safeSL[arrayIndex] = 0;
|
|
}
|
|
if(DrawLines){
|
|
ObjectCreate(0,"Safe Activation #"+IntegerToString(positionTicket[arrayIndex]),OBJ_HLINE,0,0,safeActivationPrice[arrayIndex]);
|
|
ObjectSetInteger(0,"Safe Activation #"+IntegerToString(positionTicket[arrayIndex]),OBJPROP_COLOR,clrDodgerBlue);
|
|
ObjectSetInteger(0,"Safe Activation #"+IntegerToString(positionTicket[arrayIndex]),OBJPROP_STYLE,STYLE_DASH);
|
|
}
|
|
}
|
|
if(TrailingMode){
|
|
ArrayResize(trailingActivationPrice,arraySize,10);
|
|
ArrayResize(trailingDistance,arraySize,10);
|
|
if(positionType == POSITION_TYPE_BUY){
|
|
trailingActivationPrice[arrayIndex] = positionOpenPrice + (positionOpenPrice-initialPositionSL)*TrailingActivationRatio;
|
|
trailingDistance[arrayIndex] = (trailingActivationPrice[arrayIndex]-positionOpenPrice)*(TrailingDistance/100);
|
|
}
|
|
else if(positionType==POSITION_TYPE_SELL){
|
|
trailingActivationPrice[arrayIndex] = positionOpenPrice - (initialPositionSL-positionOpenPrice)*TrailingActivationRatio;
|
|
trailingDistance[arrayIndex] = (positionOpenPrice - trailingActivationPrice[arrayIndex])*(TrailingDistance/100);
|
|
}
|
|
if(DrawLines){
|
|
ObjectCreate(0,"Trailing Activation #"+IntegerToString(positionTicket[arrayIndex]),OBJ_HLINE,0,0,trailingActivationPrice[arrayIndex]);
|
|
ObjectSetInteger(0,"Trailing Activation #"+IntegerToString(positionTicket[arrayIndex]),OBJPROP_COLOR,clrMediumOrchid);
|
|
ObjectSetInteger(0,"Trailing Activation #"+IntegerToString(positionTicket[arrayIndex]),OBJPROP_STYLE,STYLE_DASH);
|
|
}
|
|
}
|
|
if(SafeWave){
|
|
ArrayResize(safeWaveActivationPrice,arraySize,10);
|
|
ArrayResize(safeWaveSL,arraySize,10);
|
|
}
|
|
}
|
|
|
|
void CloseAllLongPositions(){
|
|
for(int i=0; i< PositionsTotal(); i++){
|
|
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) trade.PositionClose(PositionGetTicket(i));
|
|
}
|
|
}
|
|
void CloseAllShortPositions(){
|
|
for(int i=0; i< PositionsTotal(); i++){
|
|
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL) trade.PositionClose(PositionGetTicket(i));
|
|
}
|
|
}
|
|
void CloseAllPositions(){
|
|
for(int i=0; i< PositionsTotal(); i++){
|
|
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetSymbol(i) == _Symbol) trade.PositionClose(PositionGetTicket(i));
|
|
}
|
|
}
|
|
|