377 lines
27 KiB
Plaintext
377 lines
27 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| TrailingTakeProfit.mq5 |
|
|
//| Copyright 2023, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2023, MetaQuotes Ltd."
|
|
#property link "https://www.mql5.com"
|
|
#property version "1.3"
|
|
#property description "Update: --- Sell order problem solved.. --- "
|
|
|
|
#include <Trade/Trade.mqh>
|
|
#include <Trade/SymbolInfo.mqh>
|
|
CTrade m_trade;
|
|
CSymbolInfo m_symbol;
|
|
CPositionInfo m_position;
|
|
CHistoryOrderInfo m_history;
|
|
|
|
|
|
input group "---------- General ----------"
|
|
input ulong Magic = 11111; // Magic
|
|
input string comentar = "Rectangle Test";
|
|
input group "---------- Trend double MA (EMA+SMA) ----------"
|
|
input int EmaSmall = 45; // EMA Small
|
|
input int EmaBig = 200; // SMA Big
|
|
input group "---------- Risk end Moneymanagement ----------"
|
|
input double Risk = 1; // Risk in (%)
|
|
enum my_lots {
|
|
fixed_volume = 1,
|
|
procent_volume = 2,
|
|
};
|
|
input my_lots volumetype = procent_volume; // Volume type
|
|
input double Lots = 0.01; // Volume
|
|
input int SL_OF_DAY = 1; // Stopploss of Day
|
|
input int SLPoints = 250; // SL Points
|
|
input int TPPoints = 750; // TP Points
|
|
input group "---------- Rectangle setting ----------"
|
|
input bool RectangleColorFill = true; // Rectangle Color fill?
|
|
input ENUM_TIMEFRAMES timeframe = PERIOD_H1;
|
|
input int NumberCandles = 10; // Rectangle Number of candles
|
|
input double RectangleSize = 0.50; // Rectanglesize in %
|
|
input group "---------- Trading start and end (00:00) Filter ----------";
|
|
input string TradeStart = "03:00"; // Trade Start
|
|
input string TradeEnd = "22:50"; // Trade End
|
|
input bool TimeCloseTrue = false; // Order Closing (true/false))
|
|
input int TimeClose = 23; // Order is closed
|
|
|
|
static double lot = 0;
|
|
int MA=0;
|
|
int MA2=0;
|
|
datetime timestamp;
|
|
|
|
int OnInit() {
|
|
|
|
//--- Moving Average indicator
|
|
MA=iMA(_Symbol,PERIOD_CURRENT,EmaSmall,0,MODE_EMA,PRICE_CLOSE);
|
|
if(MA==INVALID_HANDLE) {
|
|
printf("Error creating MA indicator");
|
|
return(INIT_FAILED);
|
|
}
|
|
|
|
//--- Moving2 Average indicator
|
|
MA2=iMA(_Symbol,PERIOD_CURRENT,EmaBig,0,MODE_SMA,PRICE_CLOSE);
|
|
if(MA2==INVALID_HANDLE) {
|
|
printf("Error creating MA indicator");
|
|
return(INIT_FAILED);
|
|
}
|
|
|
|
//---
|
|
if(!m_symbol.Name(_Symbol)) // sets symbol name
|
|
return(INIT_FAILED);
|
|
//---
|
|
m_trade.SetExpertMagicNumber(Magic);
|
|
m_trade.SetDeviationInPoints(0);
|
|
//---
|
|
if(IsFillingTypeAllowed(SYMBOL_FILLING_FOK))
|
|
m_trade.SetTypeFilling(ORDER_FILLING_FOK);
|
|
else if(IsFillingTypeAllowed(SYMBOL_FILLING_IOC))
|
|
m_trade.SetTypeFilling(ORDER_FILLING_IOC);
|
|
else
|
|
m_trade.SetTypeFilling(ORDER_FILLING_RETURN);
|
|
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason) {
|
|
ObjectDelete(0,"Rectangle");
|
|
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick() {
|
|
// Ask end Bid price
|
|
double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
|
|
double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
|
|
|
|
//--- get current Moving Average
|
|
double mawert[1];
|
|
|
|
if(CopyBuffer(MA,0,0,1,mawert)!=1) {
|
|
Print("CopyBuffer from iMA failed, no data");
|
|
return;
|
|
}
|
|
|
|
//--- get current Moving Average
|
|
double mawert2[1];
|
|
|
|
if(CopyBuffer(MA2,0,0,1,mawert2)!=1) {
|
|
Print("CopyBuffer from iMA failed, no data");
|
|
return;
|
|
}
|
|
|
|
//SL of Day function
|
|
if(Loss(0)>=SL_OF_DAY) {
|
|
return;
|
|
}
|
|
|
|
// Candletime
|
|
|
|
datetime CandleTime = iTime(NULL,timeframe,0);
|
|
|
|
// Timestamp of Candles
|
|
if(timestamp != CandleTime) {
|
|
timestamp = CandleTime;
|
|
|
|
int BarHigh, BarLow;
|
|
|
|
//Create Arrays for High and Low
|
|
double High[], Low[];
|
|
|
|
//Sort array downwards drom the current candle
|
|
ArraySetAsSeries(High,true);
|
|
|
|
//Sort array downwards drom the current candle
|
|
ArraySetAsSeries(Low,true);
|
|
|
|
CopyHigh(_Symbol,PERIOD_CURRENT,0,NumberCandles,High);
|
|
CopyHigh(_Symbol,PERIOD_CURRENT,0,NumberCandles,Low);
|
|
|
|
//Calculate the highest
|
|
BarHigh = ArrayMaximum(High,0,NumberCandles);
|
|
|
|
//Calculate the lowest
|
|
BarLow = ArrayMinimum(Low,0,NumberCandles);
|
|
|
|
// Create an Array for prices
|
|
MqlRates PriceInfo[];
|
|
|
|
// Sort it from current candle to oldest candle
|
|
ArraySetAsSeries(PriceInfo,true);
|
|
|
|
// Copy price data into the array
|
|
int Data = CopyRates(Symbol(),PERIOD_CURRENT,0,Bars(Symbol(),Period()),PriceInfo);
|
|
|
|
// Delete Object rectangle
|
|
ObjectDelete(0,"Rectangle");
|
|
|
|
// Create Object Retangle
|
|
ObjectCreate(0,
|
|
"Rectangle",
|
|
OBJ_RECTANGLE,
|
|
0,
|
|
PriceInfo[NumberCandles].time,
|
|
PriceInfo[BarHigh].high,
|
|
PriceInfo[0].time,
|
|
PriceInfo[BarLow].low
|
|
);
|
|
|
|
// double RectangleRange = NormalizeDouble((PriceInfo[BarHigh].high - PriceInfo[BarLow].low)/_Point,_Digits);
|
|
double RangePercent = NormalizeDouble(((PriceInfo[BarHigh].high - PriceInfo[BarLow].low) / PriceInfo[BarHigh].high * 100),2);
|
|
|
|
if(RangePercent > RectangleSize) {
|
|
//--- Set the color of the rectangle
|
|
ObjectSetInteger(0,"Rectangle",OBJPROP_COLOR,clrRed);
|
|
} else {
|
|
//--- Set the color of the rectangle
|
|
ObjectSetInteger(0,"Rectangle",OBJPROP_COLOR,clrGreen);
|
|
}
|
|
//--- Set the style of the rectangle lines
|
|
ObjectSetInteger(0,"Rectangle",OBJPROP_STYLE,STYLE_SOLID);
|
|
//--- Set the line width of the rectangle
|
|
ObjectSetInteger(0,"Rectangle",OBJPROP_WIDTH,1);
|
|
if(RectangleColorFill == true) {
|
|
//--- enable (true) or disable (false) color fill of the rectangle
|
|
ObjectSetInteger(0,"Rectangle",OBJPROP_FILL,true);
|
|
} else {
|
|
//--- enable (true) or disable (false) color fill of the rectangle
|
|
ObjectSetInteger(0,"Rectangle",OBJPROP_FILL,false);
|
|
}
|
|
//--- Show on foreground (false) or background (true).
|
|
ObjectSetInteger(0,"Rectangle",OBJPROP_BACK,false);
|
|
|
|
//Time Close Order
|
|
if(TimeCloseTrue == true) {
|
|
ClosePos();
|
|
}
|
|
|
|
static ENUM_POSITION_TYPE tipe_letzte_position=-1;
|
|
for(int i=PositionsTotal()-1; i>=0; i--) // Returns the number of current positions
|
|
if(m_position.SelectByIndex(i)) // selects position by index for further access to its properties
|
|
if(m_position.Symbol()== _Symbol && m_position.Magic()==Magic) {
|
|
tipe_letzte_position=m_position.PositionType(); // Gets the position type
|
|
return;
|
|
}
|
|
|
|
datetime localtime = TimeCurrent();
|
|
datetime timestart = StringToTime(TradeStart);
|
|
datetime timeend = StringToTime(TradeEnd);
|
|
|
|
if(RangePercent <= RectangleSize && mawert[0] < PriceInfo[BarHigh].high && mawert[0] > PriceInfo[BarLow].low) {
|
|
|
|
if(mawert2[0] < PriceInfo[BarHigh].high && mawert2[0] > PriceInfo[BarLow].low) {
|
|
// Print("Trend sideways!");
|
|
}
|
|
|
|
|
|
if(localtime>timestart && localtime < timeend && RangePercent < RectangleSize && mawert[0] > mawert2[0]) {
|
|
if(Ask > mawert[0]) {
|
|
// Print("Sell Order");
|
|
|
|
double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
|
|
double price = Bid;
|
|
double SL = price + SLPoints * _Point;
|
|
SL = NormalizeDouble(SL,_Digits);
|
|
double TK = price - TPPoints * _Point;
|
|
TK = NormalizeDouble(TK,_Digits);
|
|
|
|
if(volumetype == procent_volume) {
|
|
lot = NormalizeDouble(LotsByRisk(ORDER_TYPE_SELL,Risk,(int)SLPoints),2);
|
|
} else {
|
|
lot = Lots;
|
|
}
|
|
|
|
double volume = lot;
|
|
volume=LotCheck(volume);
|
|
if(volume==0)
|
|
return;
|
|
|
|
m_trade.Sell(volume,NULL,price,SL,TK,comentar);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(localtime>timestart && localtime < timeend && RangePercent < RectangleSize && mawert[0] < mawert2[0]) {
|
|
if(Bid < mawert[0]) {
|
|
// Print("Buy Order");
|
|
double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
|
|
double price = Ask;
|
|
double SL = price - SLPoints * _Point;
|
|
SL = NormalizeDouble(SL,_Digits);
|
|
double TK = price + TPPoints * _Point;
|
|
TK = NormalizeDouble(TK,_Digits);
|
|
|
|
if(volumetype == procent_volume) {
|
|
lot = NormalizeDouble(LotsByRisk(ORDER_TYPE_BUY,Risk,(int)SLPoints),2);
|
|
} else {
|
|
lot = Lots;
|
|
}
|
|
|
|
double volume = lot;
|
|
volume=LotCheck(volume);
|
|
if(volume==0)
|
|
return;
|
|
|
|
m_trade.Buy(volume,NULL,price,SL,TK,comentar);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
void ClosePos() {
|
|
datetime time = TimeCurrent();
|
|
datetime time_end = StringToTime((string)TimeClose);
|
|
|
|
for(int i=PositionsTotal()-1; i>=0; i--)
|
|
if(m_position.SelectByIndex(i))
|
|
if(m_position.Symbol()== _Symbol && m_position.Magic()==Magic) { // Checks by symbol and magic number
|
|
if(time >= time_end) {
|
|
m_trade.PositionClose(m_position.Ticket());
|
|
}
|
|
}
|
|
}
|
|
//------------- Loss Orders Function ---------------------------
|
|
double Loss(int ai_0) {
|
|
|
|
// HistorySelect(from_data,to_data);
|
|
HistorySelect(iTime(_Symbol,PERIOD_D1,ai_0),iTime(_Symbol,PERIOD_D1,ai_0)+60*60*24);
|
|
double loss_trade=0.0;
|
|
uint total=HistoryDealsTotal();
|
|
ulong ticket=0;
|
|
|
|
//--- for all deals
|
|
for(uint i=0; i<total; i++) {
|
|
//--- Searches for tickets greater than zero
|
|
if((ticket=HistoryDealGetTicket(i))>0) {
|
|
long entry=HistoryDealGetInteger(ticket,DEAL_ENTRY);
|
|
if(entry==DEAL_ENTRY_IN)
|
|
continue;
|
|
|
|
string symbol = HistoryDealGetString(ticket,DEAL_SYMBOL);
|
|
long order_magic = HistoryDealGetInteger(ticket,DEAL_MAGIC);
|
|
double deal_commission = HistoryDealGetDouble(ticket,DEAL_COMMISSION);
|
|
double deal_swap = HistoryDealGetDouble(ticket,DEAL_SWAP);
|
|
double deal_profit = HistoryDealGetDouble(ticket,DEAL_PROFIT);
|
|
double profit = deal_commission+deal_swap+deal_profit;
|
|
if(order_magic ==Magic) {
|
|
//... processing of deal with some DEAL_MAGIC
|
|
if(symbol==_Symbol) {
|
|
if(profit<0.0)
|
|
loss_trade++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(loss_trade);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Lot Check |
|
|
//+------------------------------------------------------------------+
|
|
double LotCheck(double lots) {
|
|
//--- calculate maximum volume
|
|
double volume=NormalizeDouble(lots,2);
|
|
double stepvol=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP);
|
|
if(stepvol>0.0)
|
|
volume=stepvol*MathFloor(volume/stepvol);
|
|
//---
|
|
double minvol=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
|
|
if(volume<minvol)
|
|
volume=0.0;
|
|
//---
|
|
double maxvol=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
|
|
if(volume>maxvol)
|
|
volume=maxvol;
|
|
return(volume);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks if the specified filling mode is allowed |
|
|
//+------------------------------------------------------------------+
|
|
bool IsFillingTypeAllowed(int fill_type) {
|
|
//--- Obtain the value of the property that describes allowed filling modes
|
|
int filling=m_symbol.TradeFillFlags();
|
|
//--- Return true, if mode fill_type is allowed
|
|
return((filling & fill_type)==fill_type);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//+-----------------------------------------------------------------
|
|
double LotsByRisk(int op_type,double risk,int sloss) {
|
|
double lot_min = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
|
|
double lot_max = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
|
|
double lot_step = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
|
|
double lotcost = ((SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE)*_Point)/(SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE)));
|
|
lot = 0.0;
|
|
double UsdPerPip = 0.0;
|
|
|
|
lot = AccountInfoDouble(ACCOUNT_BALANCE)*risk/100;
|
|
UsdPerPip = lot/sloss;
|
|
if(UsdPerPip <= 0.0)
|
|
return(0);
|
|
lot = NormalizeDouble(UsdPerPip/lotcost, 2);
|
|
if(lot <= 0.0)
|
|
return(0);
|
|
lot = NormalizeDouble(lot/lot_step, 0) * lot_step;
|
|
if(lot < lot_min)
|
|
lot = lot_min;
|
|
if(lot > lot_max)
|
|
lot = lot_max;
|
|
return(lot);
|
|
}
|
|
//+------------------------------------------------------------------+
|