Заготовка для робота на MQL5
По просьбам коллег выкладываю своего советника-обертку для MT5. Обертку — потому, что я изначально писал стратегию на С++ и, соответственно, она может быть физически размещена либо в dll, либо в другом процессе, к которому подключается dll. В моем случае подключение происходит через сокеты. Так или иначе, здесь выкладывается только обертка, которая заинтересованным поможет быстрее поднять своего бота на MT5:
yadi.sk/d/WPgi-8fd3QoetS
Этот код тестировался с dll, которую он использует в тестере стратегий MT5, при этом не падал и не зависал)) Единственное, там какой-то глюк был с кнопкой «Finalize», но с этим разобраться так и не получилось. Я даже задавал вопрос на соответствующем форуме, но проблему выявить не получилось, только местная публика успела выразить свою обиду на мои ругательства в адрес платформы МетаТрейдер)
Так уж вышло, я не пишу комментарии к коду, зато стараюсь делать так, чтобы он хорошо читался. Для написания собственного эксперта на основе этой обертки надо просто заменить вызовы функций из dll:
#import «MyLib.dll»
bool initialize( double startBalance, double _point, double leverage, double volumeStep, double spread, int brokerTimeZone );
bool finalalize();
bool passTick( datetime time, double ask, double bid, uint volume, double interest );
bool passBook( datetime time, MqlBookInfo& book[], uint bookSize );
bool addOrder( ulong orderTicket, uint orderType, datetime time, double volume, double openPice );
int closeOrder( ulong orderTicket, datetime time, double closePice );
void reportRequestCompletion( uint reqId );
bool isTimeToOpenOrder( uint& reqId, uint& srvOrderType, double& volume, double& openPice );
bool isTimeToCloseOrder( ulong& orderTickets[], uint idsInBuffer, uint& idsWritten );
#import
Функции выделенные цветом при написании собственного советника придется реализовать самостоятельно. А точнее — полностью переписать по-своему. В моем случае они работали следующим образом.
isTimeToOpenOrder — вызывается при получении каждого тика, если условия для входа в сделку соблюдаются, она возвращает true и параметры заявки (на тот момент я не различал такие понятия как ордера и позиции, поэтому вы тут видите словосочетания вроде «открыть ордер»), среди которых есть уникальный идентификатор запроса reqId, который ассоциируется с данной заявкой. Этот код
if( isTimeToOpenOrder( reqId, srvOrderType, volume, openPrice ) )
OpenOrder( reqId, srvOrderType, volume );
асинхронно посылает заявку на сервер, после чего надо дождаться ее подтверждения в вызове системной функции OnTrade().
addOrder — вызывается внутри OnTrade() и посылает в dll актуальные данные об изменении позиции, ссылаясь на соответствующий reqId. Главным образом нужно отправлять столько параметров потому, что цена совершенной сделки может отличаться от той, что была получена в isTimeToOpenOrder (запросе на открытие позиции).
reportRequestCompletion — сейчас даже не вспомню, но зачем-то сильно была нужна, хотя по идее вызова addOrder с переданным параметром reqId должно было быть достаточно.
isTimeToCloseOrder — возвращает true когда необходимо закрыть позицию. Параметры которые она должна при этом получить — список идентификаторов запросов соответствующих ордеров (на самом деле там всегда передавался только один идентификатор) idsBuff и его длина idsWritten. Данный блок
ulong idsBuff[10];
uint idsWritten;
if( isTimeToCloseOrder( idsBuff, 10, idsWritten ) )
{
for( unsigned i = 0; i < idsWritten; i++ )
{
if( !currOrder.cancelclose && currOrder.reqId == idsBuff[i] )
{
currOrder.cancelclose = true;
CloseOrder( idsBuff[i] );
}
}
}
в конечном итоге вызывает асинхронно посылает заявку на закрытие позиции. А уже подтверждение этой заявки будет при вызове OnTrade:
if( currOrder.cancelclose && !currOrder.closed )
{
if( abs( currOrder.volume + posVolumeChange ) >= volumeStep / 2.0 ) // на самом деле позиция должна быть точно равна нулю, но она и не может быть меньше volumeStep
Print( __FUNCTION__, ": Something strange (closing), actual volume change doesn't correspond current order" );
datetime closeTime = SymbolInfoInteger( _Symbol, SYMBOL_TIME );
double closePrice = ( currOrder.volume >= 0.0 )? SymbolInfoDouble( _Symbol, SYMBOL_BID ): SymbolInfoDouble( _Symbol, SYMBOL_ASK );;
closeOrder( currOrder.reqId, closeTime, closePrice );
currOrder.passedToServer = false;
currOrder.closed = true;
return;
}
closeOrder — отправляет в dll данные по соответствующему ордеру (reqId) с актуальной ценой закрытия позиции и временем.
В общем, надеюсь, это будет для кого-то полезным. Вопросы по коду приветствуются)