369 lines
33 KiB
MQL5
369 lines
33 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| ExampleV.mq5 |
|
|
//| Copyright 2017, MetaQuotes Software Corp. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2017, MetaQuotes Software Corp."
|
|
#property link "https://www.mql5.com"
|
|
#property version "1.00"
|
|
//
|
|
enum wdr_period { none=-3, days=-2, weeks=-1, months=1, quarters=3, halfyears=6, years=12 };
|
|
//--- input parameters
|
|
double InpLots =SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); // Lots
|
|
bool DescriptionModeFull=false; // Режим детального вывода
|
|
input double kLots =1.6; // kMLots * prior Lots = next Lots Martin
|
|
input int PipsStep =100; // PipsStep [pips] step for open next order
|
|
input double kPips =0.94; // kPips * prior Pips = next Pips
|
|
input int TimeStep =0; // TimeStep [sec] step sec for open next order
|
|
input int MaxTrades =14; // MaxTrades max count orders
|
|
input int TakeProfit =100; // TakeProfit [pips] if==0:PipsStep
|
|
input double kTP =1.1; // kTP * prior TP = next TP
|
|
//
|
|
input double MarginLevel =100.0; // Freez Margin Level [%]
|
|
input int Delay =20; // Delay [min]
|
|
//
|
|
input wdr_period WDR_Period =months; // WDR_Period: none days weeks months quarters halfyears years
|
|
//
|
|
double WDR_Balance; // начальный баланс здесь не инициализировать
|
|
double _wdr_summa =0;
|
|
int _wdr_day_of_year_last =0;
|
|
int _wdr_mon_last =0;
|
|
int _wdr_year_last =0;
|
|
int MagicNumber =0; // Идентификатор эксперта
|
|
int MinPipsStep =50;
|
|
int MinTakeProfit =40;
|
|
datetime _dtForbiden =0;
|
|
int _CountDelay =0;
|
|
int _TimeStep;
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
Comment(setupRobot);
|
|
WDR_Balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
int curr_positions=PositionsTotal();
|
|
int curr_orders=OrdersTotal();
|
|
if( OrdersTotal()>0||PositionsTotal()>0 ) {Print("*** WARNING Erroe OnInit. Order is opens"); return(INIT_FAILED); }
|
|
if(TimeStep==0)_TimeStep=1; else _TimeStep=TimeStep;
|
|
OnZero();
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
//------------------------------------------------------------------ OnDeinit
|
|
void OnDeinit(const int reason)
|
|
{
|
|
EventKillTimer();
|
|
Print("*** WARNING Количество задержек = ",_CountDelay,", Снято всего = ",_wdr_summa);
|
|
}
|
|
//------------------------------------------------------------------ OnTick
|
|
void OnTick()
|
|
{
|
|
if(CheckTradeDelay()) return;
|
|
if(AccountInfoDouble(ACCOUNT_MARGIN_LEVEL)>0.0 && AccountInfoDouble(ACCOUNT_MARGIN_LEVEL)<=MarginLevel) { SetTtradeDelay(); return; }
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| SetTtradeDelay |
|
|
//+------------------------------------------------------------------+
|
|
bool SetTtradeDelay()
|
|
{
|
|
datetime dt;
|
|
dt=TimeLocal(); // loacal значит киевское время !!!
|
|
_dtForbiden=dt+Delay*60;
|
|
Remove();
|
|
Close();
|
|
_CountDelay++;
|
|
Print("*** WARNING date ",TimeToString(_dtForbiden,TIME_DATE)," Close all orders and trade forbiden CountDelay=",_CountDelay);
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| CheckTradeDelay |
|
|
//+------------------------------------------------------------------+
|
|
bool CheckTradeDelay()
|
|
{
|
|
if(_dtForbiden==0) return false;
|
|
datetime dt=TimeLocal(); // loacal значит киевское время !!!
|
|
if(dt<_dtForbiden) return true;
|
|
OnZero();
|
|
_dtForbiden=0;
|
|
Print("*** WARNING date ",TimeToString(dt,TIME_DATE)," trade allowed");
|
|
return false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Trade function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTrade()
|
|
{
|
|
static int prev_positions=0,prev_orders=0;
|
|
//
|
|
int curr_positions=PositionsTotal();
|
|
int curr_orders=OrdersTotal();
|
|
bool OnClose=false, OnExec=false;
|
|
if(prev_positions>curr_positions)OnClose=true;// что-то закрылось!
|
|
if(prev_positions<curr_positions)OnExec=true; //что-то пошло на исполнение!
|
|
prev_positions=curr_positions;
|
|
prev_orders=curr_orders;
|
|
//---
|
|
if(OnClose) { prev_positions=0; OnTP(); }
|
|
if(OnExec) { Remove(); EventSetTimer(_TimeStep); }// только один раз!!
|
|
//
|
|
TimeOfWithDrawal();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| OnTP function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTP()
|
|
{
|
|
Close();
|
|
Remove();
|
|
OnZero();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| OnZero function |
|
|
//+------------------------------------------------------------------+
|
|
void OnZero()
|
|
{
|
|
MagicNumber=0;
|
|
BuyAsync(MagicNumber);
|
|
SellAsync(MagicNumber);
|
|
}
|
|
//------------------------------------------------------------------ OnTimer
|
|
void OnTimer()
|
|
{
|
|
EventKillTimer();
|
|
OnTimerExec();
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| OnTimerExec() function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTimerExec()
|
|
{
|
|
MagicNumber=PositionsTotal(); // количество открытых позиций неявно MagicNumber++ !!!!!!!
|
|
if(MagicNumber>=MaxTrades) return;
|
|
ulong ticket = PositionGetTicket(MagicNumber-1); // надо всегда для инициализации
|
|
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) BuyAsync(MagicNumber);
|
|
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL) SellAsync(MagicNumber);
|
|
return;
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| PipsOptimized |
|
|
//+------------------------------------------------------------------+
|
|
int PipsOptimized(int count)
|
|
{
|
|
int pips = PipsStep;
|
|
for(int i=0;i<count;i++)
|
|
{ pips=int(NormalizeDouble(pips*kPips,0)); }
|
|
if(pips<MinPipsStep) pips=MinPipsStep;
|
|
return(pips);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LotsOptimized |
|
|
//+------------------------------------------------------------------+
|
|
double LotsOptimized(int count)
|
|
{
|
|
double lot=InpLots;
|
|
for(int i=0;i<count;i++) { lot=lot*kLots; }
|
|
lot=NormalizeDouble(lot,2);
|
|
double maxlot=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
|
|
if(lot>maxlot) lot=maxlot;
|
|
double minlot=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
|
|
if(lot<minlot) lot=minlot;
|
|
return(lot);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| TPOptimized |
|
|
//+------------------------------------------------------------------+
|
|
int TPOptimized(int count )
|
|
{
|
|
int pips = TakeProfit; if(pips==0) pips=PipsStep;
|
|
for(int i=0;i<count;i++)
|
|
{ pips=int(NormalizeDouble(pips*kTP,0)); }
|
|
if(pips<MinTakeProfit) pips=MinTakeProfit;
|
|
return(pips);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Покупка через асинхронную функцию OrderSendAsync() |
|
|
//+------------------------------------------------------------------+
|
|
void BuyAsync(int count)
|
|
{
|
|
//--- подготовим запрос
|
|
int pips = PipsOptimized(count);
|
|
double vol = LotsOptimized(count);
|
|
int tp = TPOptimized(count);
|
|
double OpenPrice=0.0;
|
|
ulong ticket=0;
|
|
MqlTradeRequest req={0};
|
|
if(count==0) {
|
|
OpenPrice = NormalizeDouble(SymbolInfoDouble(Symbol(),SYMBOL_ASK)-pips*_Point,_Digits);// против рынка !
|
|
req.action= TRADE_ACTION_PENDING; // установка отложенного ордера ;
|
|
req.type = ORDER_TYPE_BUY_LIMIT;
|
|
}
|
|
if(count>0) {
|
|
ticket = PositionGetTicket(MagicNumber-1); // надо всегда для инициализации
|
|
OpenPrice = NormalizeDouble(PositionGetDouble(POSITION_PRICE_OPEN)-pips*_Point,_Digits); // против рынка !
|
|
if(OpenPrice<SymbolInfoDouble(Symbol(),SYMBOL_ASK)) {
|
|
req.action= TRADE_ACTION_PENDING; // установка отложенного ордера
|
|
req.type = ORDER_TYPE_BUY_LIMIT;
|
|
} else {
|
|
OpenPrice = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
|
|
req.action= TRADE_ACTION_DEAL;; // немедленное выполнение ордера
|
|
req.type = ORDER_TYPE_BUY;
|
|
}
|
|
}
|
|
req.symbol =_Symbol;
|
|
req.magic =MagicNumber;
|
|
req.volume =vol;
|
|
req.price =OpenPrice;
|
|
req.tp =NormalizeDouble(req.price+tp*_Point,_Digits);
|
|
req.deviation =10;
|
|
req.comment ="Magic = "+DoubleToString(MagicNumber,0);
|
|
MqlTradeResult res={0};
|
|
if(!OrderSendAsync(req,res)) { Print(__FUNCTION__,": ошибка ",GetLastError(),", retcode = ",res.retcode); }
|
|
//---
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Продажа через асинхронную функцию OrderSendAsync() |
|
|
//+------------------------------------------------------------------+
|
|
void SellAsync(int count)
|
|
{
|
|
//--- подготовим запрос
|
|
int pips = PipsOptimized(count);
|
|
double vol = LotsOptimized(count);
|
|
int tp = TPOptimized(count);
|
|
double OpenPrice=0.0;
|
|
ulong ticket=0;
|
|
MqlTradeRequest req={0};
|
|
if(count==0) {
|
|
OpenPrice = NormalizeDouble(SymbolInfoDouble(Symbol(),SYMBOL_BID)+pips*_Point,_Digits);// против рынка !
|
|
req.action= TRADE_ACTION_PENDING; // установка отложенного ордера ;
|
|
req.type = ORDER_TYPE_SELL_LIMIT;
|
|
}
|
|
if(count>0) {
|
|
ticket = PositionGetTicket(MagicNumber-1); // надо всегда для инициализации
|
|
OpenPrice = NormalizeDouble(PositionGetDouble(POSITION_PRICE_OPEN)+pips*_Point,_Digits); // против рынка !
|
|
if(OpenPrice>SymbolInfoDouble(Symbol(),SYMBOL_BID)) {
|
|
req.action= TRADE_ACTION_PENDING; // установка отложенного ордера
|
|
req.type = ORDER_TYPE_SELL_LIMIT;
|
|
} else {
|
|
OpenPrice = SymbolInfoDouble(Symbol(),SYMBOL_BID);
|
|
req.action= TRADE_ACTION_DEAL;; // немедленное выполнение ордера
|
|
req.type = ORDER_TYPE_SELL;
|
|
}
|
|
}
|
|
req.symbol =_Symbol;
|
|
req.magic =MagicNumber;
|
|
req.volume =vol;
|
|
req.price =OpenPrice;
|
|
req.tp =NormalizeDouble(req.price-tp*_Point,_Digits);
|
|
req.deviation =10;
|
|
req.comment ="Magic = "+DoubleToString(MagicNumber,0);
|
|
MqlTradeResult res={0};
|
|
if(!OrderSendAsync(req,res)) { Print(__FUNCTION__,": ошибка ",GetLastError(),", retcode = ",res.retcode); }
|
|
//---
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Check for close order -1 close all очистить переменные цепочки |
|
|
//+------------------------------------------------------------------+
|
|
void Close(ENUM_POSITION_TYPE _type=-1) // -1 close all
|
|
{
|
|
MqlTradeRequest request;
|
|
MqlTradeResult result;
|
|
int total=PositionsTotal(); // количество открытых позиций
|
|
//--- перебор всех открытых позиций
|
|
for(int i=total-1; i>=0; i--)
|
|
{
|
|
ulong ticket = PositionGetTicket(i); // надо всегда для инициализации
|
|
ZeroMemory(request);
|
|
ZeroMemory(result);
|
|
//--- установка параметров операции
|
|
request.action =TRADE_ACTION_DEAL; // тип торговой операции
|
|
request.position =PositionGetTicket(i); // тикет позиции
|
|
request.symbol =PositionGetString(POSITION_SYMBOL);// символ
|
|
if(request.symbol!=Symbol()) continue;//////////////////////////////////>>
|
|
request.volume =PositionGetDouble(POSITION_VOLUME);// объем позиции
|
|
request.deviation=20; // допустимое отклонение от цены
|
|
//--- установка цены и типа ордера в зависимости от типа позиции
|
|
ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // тип позиции
|
|
if(_type != -1 && _type != type) continue;
|
|
if(type==POSITION_TYPE_BUY){
|
|
request.price=SymbolInfoDouble(request.symbol,SYMBOL_BID);
|
|
request.type =ORDER_TYPE_SELL;
|
|
}else if(type==POSITION_TYPE_SELL){
|
|
request.price=SymbolInfoDouble(request.symbol,SYMBOL_ASK);
|
|
request.type =ORDER_TYPE_BUY;
|
|
}
|
|
//PrintFormat(__FUNCTION__," Close #%I64d type=%s magic=[%I64d]",request.position,EnumToString(type),request.magic);
|
|
if(!OrderSend(request,result)) PrintFormat(__FUNCTION__," Close error %d",GetLastError()); // если отправить запрос не удалось, вывести код ошибки
|
|
//Sleep(120);
|
|
/// PrintFormat(__FUNCTION__," Close retcode=%u deal=%I64u order=%I64u ticket_position=%I64u",result.retcode,result.deal,result.order,ticket);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Удаляет все отложенные ордера |
|
|
//+------------------------------------------------------------------+
|
|
void Remove()
|
|
{
|
|
ulong order_ticket;
|
|
for(int i=OrdersTotal()-1;i>=0;i--)
|
|
{
|
|
if((order_ticket=OrderGetTicket(i))>0) //--- пройдем по всем отложенным ордерам
|
|
{
|
|
MqlTradeResult result={0};
|
|
MqlTradeRequest request={0};
|
|
request.order=order_ticket;
|
|
request.action=TRADE_ACTION_REMOVE;
|
|
if(!OrderSend(request,result)) Print(__FUNCTION__," Error: ",result.comment," код ответа ",result.retcode);
|
|
else Print(__FUNCTION__," Succes: ",result.comment," код ответа ",result.retcode);
|
|
}
|
|
}
|
|
//---
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//--- Вывод информации о тестировании |
|
|
//+------------------------------------------------------------------+
|
|
double OnTester(void)
|
|
{
|
|
double Equity=AccountInfoDouble(ACCOUNT_BALANCE) - WDR_Balance;
|
|
if(Equity>=0.0) return (_wdr_summa); else return Equity;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
int _iVspread; //*** Anton ***
|
|
string setupRobot=DoubleToString(kLots,1)+" / "+DoubleToString(PipsStep,0)+
|
|
" / "+DoubleToString(kPips,2)+" / "+DoubleToString(TimeStep,0)+" / "+DoubleToString(MaxTrades,0)+" / "+DoubleToString(TakeProfit,0)+
|
|
" / "+DoubleToString(kTP,2)+" / "+DoubleToString(MarginLevel,0)+" / "+DoubleToString(Delay,0);
|
|
//+------------------------------------------------------------------+
|
|
//--- Функция снятия средств со счета |
|
|
//+------------------------------------------------------------------+
|
|
void TimeOfWithDrawal()
|
|
{
|
|
MqlDateTime dt_struct; // переменная типа структуры
|
|
//
|
|
if(WDR_Period == none ) return; // выход, если снятие средств запрещено
|
|
TimeCurrent (dt_struct);
|
|
switch(WDR_Period) // WDR_Period days -2 weeks -1 months 1 quarters 3 halfyears 6 none -3
|
|
{
|
|
case days: if(_wdr_day_of_year_last != dt_struct.day_of_year) _wdr_day_of_year_last = dt_struct.day_of_year; else return;
|
|
break;
|
|
case weeks: if(dt_struct.day_of_week != 1) return;
|
|
if(_wdr_day_of_year_last == dt_struct.day_of_year) return;
|
|
break;
|
|
case months: if(_wdr_mon_last != dt_struct.mon) _wdr_mon_last = dt_struct.mon; else return;
|
|
break;
|
|
case years: if(_wdr_year_last != dt_struct.year) _wdr_year_last = dt_struct.year; else return;
|
|
break;
|
|
default: if(dt_struct.mon % WDR_Period != 1) return;
|
|
if(_wdr_mon_last != dt_struct.mon) _wdr_mon_last = dt_struct.mon; else return;
|
|
}
|
|
_wdr_day_of_year_last = dt_struct.day_of_year;
|
|
double wdr_value = AccountInfoDouble(ACCOUNT_EQUITY) - WDR_Balance-1;///////////////-1
|
|
if(wdr_value<0.0) return; // выход, если нечего снимать
|
|
if(TesterWithdrawal(wdr_value))
|
|
{
|
|
_wdr_summa = _wdr_summa + wdr_value;
|
|
Print("*** TesterWithdrawal wdr_value = ",wdr_value," _wdr_summa =",_wdr_summa," CountDelay=",_CountDelay);
|
|
}
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|