Похоже на выходных обновили сервер. Миллисекунды появились. Возможно будут еще сюрпризы.
Видимо функция OnTradeTransaction работает независимо от журнала операций.
Думаю такое поведение функции оправдано, незачем тормозить поток операций и ждать пока в журнале все запишется и посчитается.
В вашем случае наверное лучше использовать OnTrade,
или ждать и периодически с минимальной паузой проверять когда сделка появится в истории.
Привет, Сергей!
Да обновили, но не в выходные, а в четверг после вечерней сессии (спрашивал у брокера).
Я не могу использовать событие Trade() и ждать, когда обновятся данные в терминале.
Эксперт был написан давно, и до недавнего времени работал "как часы" (возможно мне везло, и событие TRADE_TRANSACTION_DEAL_ADD всегда приходило первым).
Эксперту важно как можно быстрее совершить ответную сделку, поэтому ассинхронный режим и OnTradeTransaction().
Сейчас, эксперт отсылает (иногда) дублирующие ордера на открытие и закрытие позиции.
Вы: "Думаю такое поведение функции оправдано, незачем тормозить поток операций и ждать пока в журнале все запишется и посчитается".
Всё равно пишется и считается после прихода TRADE_TRANSACTION_DEAL_ADD :)
Дело в том, что TRADE_TRANSACTION_DEAL_ADD может потерятся, а TRADE_TRANSACTION_HISTORY_ADD прийти, и тогда в терминале будут устаревшие данные о позиции. :(,
что собственно и происходит.
Странно, что разработчики не подумали об этом.
TRADE_TRANSACTION_HISTORY_ADD приходит лишь в том случае, если ордер исполнился или удалён (отменён), следовательно,
при изменении состояния ордера (соответственно позиция может изменится), в терминал должна поступить информация об изменении позиции,
даже, если TRADE_TRANSACTION_DEAL_ADD - потерялось
Просьба к "учителям" и "всезнайкам" выступать по существу,
а не просто ногу задирать у столба, чтобы отметится.
Прежде чем выпендриваться пред людьми желающими вам помочь, сформулировали бы нормально свой вопрос. Причем здесь асиннхронная отправка ордера, если частичное закрытие выполняется функцией OrderSend(). Вы вообще о чем вопрос то задаете?
Dmitry Fedoseev: Прежде чем выпендриваться пред людьми желающими вам помочь, сформулировали бы нормально свой вопрос. Причем здесь асиннхронная отправка ордера, если частичное закрытие выполняется функцией OrderSend(). Вы вообще о чем вопрос то задаете?
Супер!
Это нужно рассматривать как помощь?
Бред. Какая связь между кэшем, где сохраняются данные о запрашиваемой позиции, и транзакциями?
prostotrader, у Вас там с логикой алгоритма наверное что-то не то. Хотел было покопаться в чужом коде, но влом... потом тип исполнения тут:
request.type_filling=ORDER_FILLING_IOC; // разве так?
request.type_filling=ORDER_FILLING_RETURN; // а может так?
И вообще в каких университетах так учат кодировать?
А Карпутов вообще не причём, просто когда я писал своё сообщение он уже запостил своё, а я не видел.
Изначально вопрос ставился так (если лень ситать сначала)
Как построить логирование, чтобы показать разработчикам наличие ошибки?
Зачем, я сам всё сделал и с логах отчётливо видно, что
после прихода TRADE_TRANSACTION_HISTORY_ADD (перед TRADE_TRANSACTION_DEAL_ADD)
prostotrader, Вам Дмитрий правильно подсказывает, что частичное (и полное) закрытие идёт не в асинхронном порядке в вашем коде, а в синхронном... значит программа ждёт отклика с сервера...
Вероятно, что OnTradeTransaction срабатывает быстрее, чем изменяется сама позиция.
Потом тут:
//+------------------------------------------------------------------+//| TradeTransaction function |//+------------------------------------------------------------------+voidOnTradeTransaction(constMqlTradeTransaction &trans,
constMqlTradeRequest &request,
constMqlTradeResult &result)
{
bool is_select=false;
ENUM_POSITION_TYPE pos_type=NULL;
long volume=0;
switch(trans.type)
{
caseTRADE_TRANSACTION_REQUEST: if((request_id!=0) && (result.request_id==request_id))
{
order_ticket=result.order;
request_id=0;
Print(__FUNCTION__,": Order resived #",result.order);
}
break;
caseTRADE_TRANSACTION_HISTORY_ADD: if((order_ticket!=0) && (trans.order==order_ticket))
{
Print(__FUNCTION__,": Order #",order_ticket," add to history.");
ORDER_DATA order_data;
ENUM_ORD_SELECT order_select=OrderRealSelect(order_ticket,order_data,false);
switch(order_select)
{
case SELECT_TRUE: if(PositionSelect(Symbol()))
{
pos_type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
volume=(long)PositionGetDouble(POSITION_VOLUME);
is_select=true;
}
elsePrint(__FUNCTION__,": Position not exist.");
break;
case SELECT_FALSE: if(PositionSelect(Symbol()))
{
pos_type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
volume=(long)PositionGetDouble(POSITION_VOLUME);
is_select=true;
}
elsePrint(__FUNCTION__,": Position not exist.");
break;
}
if(is_select)
{
Print(__FUNCTION__,": Position exists");
Print(__FUNCTION__,": Position type: ",EnumToString(pos_type));
Print(__FUNCTION__,": Position volume: ",volume);
}
tr_cnt++;
exp_busy=false;
}
break;
}
}
можно попробовать зациклить проверку позиции. Может поможет....
Вот как-то так:
//+------------------------------------------------------------------+//| TradeTransaction function |//+------------------------------------------------------------------+voidOnTradeTransaction(constMqlTradeTransaction &trans,
constMqlTradeRequest &request,
constMqlTradeResult &result)
{
bool is_select=false;
ENUM_POSITION_TYPE pos_type=NULL;
long volume=0;
switch(trans.type)
{
caseTRADE_TRANSACTION_REQUEST:
{
if((request_id!=0) && (result.request_id==request_id))
{
order_ticket=result.order;
request_id=0;
Print(__FUNCTION__,": Order resived #",result.order);
}
break;
}
caseTRADE_TRANSACTION_HISTORY_ADD:
{
if((order_ticket!=0) && (trans.order==order_ticket))
{
Print(__FUNCTION__,": Order #",order_ticket," add to history.");
ORDER_DATA order_data;
ENUM_ORD_SELECT order_select=OrderRealSelect(order_ticket,order_data,false);
bool is_position=false;
//--- 5 попыток на проверку наличия позицииfor(int attempt_idx=0;attempt_idx<5;attempt_idx++)
{
is_position=PositionSelect(Symbol());
if(is_position)
break;
Sleep(25);
}//---if((order_select==SELECT_TRUE) || (order_select==SELECT_FALSE))
{
if(is_position)
{
pos_type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
volume=(long)PositionGetDouble(POSITION_VOLUME);
is_select=true;
}
elsePrint(__FUNCTION__,": Position not exist.");
}
if(is_select)
{
Print(__FUNCTION__,": Position exists");
Print(__FUNCTION__,": Position type: ",EnumToString(pos_type));
Print(__FUNCTION__,": Position volume: ",volume);
}
tr_cnt++;
exp_busy=false;
}
break;
}
}
}
Не зная точный алгоритм (ТО, ЧТО нужно от программы), сложно оценивать правильность его реализации...
//+------------------------------------------------------------------+//| Test_Pos_selct.mq5 |//| Copyright 2016, MetaQuotes Software Corp. |//| https://www.mql5.com |//+------------------------------------------------------------------+#property copyright"Copyright 2016, MetaQuotes Software Corp."#property link"https://www.mql5.com"#property version"1.00"inputuint TrCount=50; //Кол-во транзакцийuint tr_cnt;
ulong order_ticket;
ulong request_id;
ulong Magic=1234567890;
#define ERR_ZERO_TICKET -11;
bool exp_busy;
//+------------------------------------------------------------------+//| |//+------------------------------------------------------------------+enum ENUM_ORD_SELECT
{
SELECT_ERROR = 0,
SELECT_FALSE = 1,
SELECT_TRUE = 2,
SELECT_BUSY = 3
};
//+------------------------------------------------------------------+//| |//+------------------------------------------------------------------+enum ENUM_ORD_REAL_STATE
{
ORD_NOT_SPECIFIED = 0, //Состояние ордера не определено
ORD_NONE_CANCELED = 1, //Ордера нет, отменён пользователем
ORD_NONE_PARTIAL_CANCELED = 2, //Ордера нет, исполнился частично (не был залит вторым объёмом)
ORD_NONE_PARTIAL = 3, //Ордера нет, исполнился частично
ORD_NONE_EXPIRED = 4, //Ордера нет, удалён по сроку
ORD_NONE_FILLED = 5, //Ордера нет, исполнился полностью
ORD_NONE_REJECTED = 6, //Ордера нет, отклонён брокером(биржей)
ORD_BUSY = 7, //Ордер находится в переходном состоянии
ORD_EXIST = 8, //Ордер выставлен на биржу, возможны действия над ним
ORD_EXIST_PARTIAL = 9//Ордер выставлен на биржу, частично исполнился, возможны действия над ним
};
//+------------------------------------------------------------------+//| |//+------------------------------------------------------------------+struct ORDER_DATA
{
int error_code;
datetime time_setup;
ENUM_ORDER_TYPE type;
ENUM_ORDER_STATE state;
ENUM_ORD_REAL_STATE real_state;
datetime expiration;
datetime time_done;
long t_set_msc;
long t_done_msc;
ENUM_ORDER_TYPE_FILLING type_filling;
ENUM_ORDER_TYPE_TIME type_time;
long magic;
long pos_id;
double vol_init;
double vol_cur;
double price_open;
double sl;
double tp;
double price_cur;
double price_stlim;
string symbol;
string comment;
};
//+------------------------------------------------------------------+//| Expert initialization function |//+------------------------------------------------------------------+intOnInit()
{
//---Print(__FUNCTION__, ": Start testing: ",TimeTradeServer());
tr_cnt=0;
order_ticket=0;
request_id=0;
exp_busy=false;
if(!MarketBookAdd(Symbol())){return(INIT_FAILED);}
//---return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+//| Expert deinitialization function |//+------------------------------------------------------------------+voidOnDeinit(constint reason)
{
//---MarketBookRelease(Symbol());
}
//+------------------------------------------------------------------+//| TradeTransaction function |//+------------------------------------------------------------------+voidOnTradeTransaction(constMqlTradeTransaction &trans,
constMqlTradeRequest &request,
constMqlTradeResult &result)
{
bool is_select=false;
ENUM_POSITION_TYPE pos_type=NULL;
long volume=0;
switch(trans.type)
{
caseTRADE_TRANSACTION_REQUEST: if((request_id!=0) && (result.request_id==request_id))
{
order_ticket=result.order;
request_id=0;
Print(__FUNCTION__, ": Order resived #", result.order);
}
break;
caseTRADE_TRANSACTION_DEAL_ADD: if((order_ticket!=0) && (trans.order==order_ticket))
{
Print(__FUNCTION__, ": Deal, based on order #", order_ticket, " done.");
if(PositionSelect(Symbol()))
{
Print(__FUNCTION__, ": Position exists.");
pos_type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
volume=(long)PositionGetDouble(POSITION_VOLUME);
Print(__FUNCTION__, ": Position type: ", EnumToString(pos_type));
Print(__FUNCTION__, ": Position volume: ", volume);
}
elsePrint(__FUNCTION__, ": Position not exist.");
}
break;
caseTRADE_TRANSACTION_HISTORY_ADD: if((order_ticket!=0) && (trans.order==order_ticket))
{
Print(__FUNCTION__, ": Order #", order_ticket, " add to history.");
ORDER_DATA order_data;
ENUM_ORD_SELECT order_select=OrderRealSelect(order_ticket,order_data,false);
switch(order_select)
{
case SELECT_TRUE: if(PositionSelect(Symbol()))
{
pos_type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
volume=(long)PositionGetDouble(POSITION_VOLUME);
is_select=true;
}
elsePrint(__FUNCTION__, ": Position not exist.");
Print(__FUNCTION__, ": Order #",trans.order," exists.");
break;
case SELECT_FALSE: if(PositionSelect(Symbol()))
{
pos_type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
volume=(long)PositionGetDouble(POSITION_VOLUME);
is_select=true;
}
elsePrint(__FUNCTION__, ": Position not exist.");
Print(__FUNCTION__, ": Order #",trans.order," not found.");
break;
}
if(is_select)
{
Print(__FUNCTION__, ": Position exists");
Print(__FUNCTION__, ": Position type: ", EnumToString(pos_type));
Print(__FUNCTION__, ": Position volume: ", volume);
}
tr_cnt++;
exp_busy=false;
}
break;
}
}
//+------------------------------------------------------------------+//| BookEvent function |//+------------------------------------------------------------------+voidOnBookEvent(conststring &symbol)
{
//---if(symbol==Symbol()&&(!exp_busy))
{
if(tr_cnt>=TrCount)
{
if(PositionSelect(Symbol()))
{
order_ticket=0;
ENUM_POSITION_TYPE pos_type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
long vol=(long)PositionGetDouble(POSITION_VOLUME);
ClosePosition(pos_type,vol);
Print(__FUNCTION__, ": End testing: ",TimeTradeServer());
if(order_ticket>0) ExpertRemove();
}
else
{
Print(__FUNCTION__, ": End testing: ",TimeTradeServer());
ExpertRemove();
}
}
else
{
if(PositionSelect(Symbol()))
{
ENUM_POSITION_TYPE pos_type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
long vol=(long)PositionGetDouble(POSITION_VOLUME);
Print(__FUNCTION__, ": Position exists");
Print(__FUNCTION__, ": Position type: ", EnumToString(pos_type));
Print(__FUNCTION__, ": Position volume: ", vol);
switch(int(vol))
{
case1: ClosePosition(pos_type,vol);
break;
default: PartClosePos(pos_type);
break;
}
}
else
{
Print(__FUNCTION__, ": Try open position...");
OpenPosition();
}
}
}
}
//void ClosePosition(ENUM_POSITION_TYPE p_type,constlong volume)
{
MqlTradeRequest request={0};
MqlTradeResult result={0};
switch(p_type)
{
casePOSITION_TYPE_BUY: request.type=ORDER_TYPE_SELL;
break;
casePOSITION_TYPE_SELL: request.type=ORDER_TYPE_BUY;
break;
}
order_ticket=0;
request_id = 0;
request.magic=Magic;
request.symbol=Symbol();
request.volume=(double)volume;
request.type_filling=ORDER_FILLING_IOC;
request.type_time=ORDER_TIME_DAY;
request.action=TRADE_ACTION_DEAL;
request.comment="";
request.price=0;
if(OrderSendAsync(request,result))
{
if(result.retcode==TRADE_RETCODE_PLACED)
{
request_id=result.request_id;
exp_busy=true;
Print(__FUNCTION__, ": Order sent for close position.");
}
}
else
{Print(__FUNCTION__, ": Order not sent for close position!");}
}
//+------------------------------------------------------------------+ void PartClosePos(ENUM_POSITION_TYPE p_type)
{
MqlTradeRequest request={0};
MqlTradeResult result={0};
switch(p_type)
{
casePOSITION_TYPE_BUY: request.type=ORDER_TYPE_SELL;
break;
casePOSITION_TYPE_SELL: request.type=ORDER_TYPE_BUY;
break;
}
order_ticket=0;
request_id=0;
request.magic=Magic;
request.symbol=Symbol();
request.volume=1;
request.type_filling=ORDER_FILLING_IOC;
request.type_time=ORDER_TIME_DAY;
request.action=TRADE_ACTION_DEAL;
request.comment="";
request.price=0;
if(OrderSendAsync(request,result))
{
if(result.retcode==TRADE_RETCODE_PLACED)
{
request_id=result.request_id;
exp_busy=true;
Print(__FUNCTION__, ": Order sent for part close position.");
}
}
else
{Print(__FUNCTION__, ": Order not sent for part close position!");}
}
//+------------------------------------------------------------------+ void OpenPosition()
{
MqlTradeRequest request={0};
MqlTradeResult result={0};
request_id=0;
order_ticket=0;
request.magic=Magic;
request.symbol=Symbol();
request.volume=2;
request.type_filling=ORDER_FILLING_IOC;
request.type_time=ORDER_TIME_DAY;
request.action=TRADE_ACTION_DEAL;
request.comment="";
request.price=0;
request.type=ORDER_TYPE_BUY;
if(OrderSendAsync(request,result))
{
if(result.retcode==TRADE_RETCODE_PLACED)
{
request_id=result.request_id;
exp_busy=true;
Print(__FUNCTION__, ": Order sent successfully for open position volume = ",request.volume);
}
}
elsePrint(__FUNCTION__, ": Order not sent for open position!");
}
ENUM_ORD_SELECT OrderRealSelect(constulong ticket,ORDER_DATA &ord_data,constbool get_data)
{
double init_vol= 0;
double cur_vol = 0;
ZeroMemory(ord_data);
ord_data.real_state = ORD_NOT_SPECIFIED;
ord_data.error_code = ERR_SUCCESS;
ResetLastError();
//--- if(ticket>0)
{
if(HistoryOrderSelect(ticket))
{
if(get_data)
{
ord_data.comment=HistoryOrderGetString(ticket,ORDER_COMMENT);
ord_data.expiration=datetime(HistoryOrderGetInteger(ticket,ORDER_TIME_EXPIRATION));
ord_data.magic=HistoryOrderGetInteger(ticket,ORDER_MAGIC);
ord_data.pos_id=HistoryOrderGetInteger(ticket,ORDER_POSITION_ID);
ord_data.price_cur=HistoryOrderGetDouble(ticket,ORDER_PRICE_CURRENT);
ord_data.price_open=HistoryOrderGetDouble(ticket,ORDER_PRICE_OPEN);
ord_data.price_stlim=HistoryOrderGetDouble(ticket,ORDER_PRICE_STOPLIMIT);
ord_data.sl=HistoryOrderGetDouble(ticket,ORDER_SL);
ord_data.state=ENUM_ORDER_STATE(HistoryOrderGetInteger(ticket,ORDER_STATE));
ord_data.symbol=HistoryOrderGetString(ticket,ORDER_SYMBOL);
ord_data.t_done_msc=datetime(HistoryOrderGetInteger(ticket,ORDER_TIME_DONE_MSC));
ord_data.t_set_msc =datetime( HistoryOrderGetInteger(ticket, ORDER_TIME_SETUP_MSC));
ord_data.time_done =datetime( HistoryOrderGetInteger( ticket, ORDER_TIME_DONE));
ord_data.time_setup=datetime(HistoryOrderGetInteger(ticket,ORDER_TIME_SETUP));
ord_data.tp=HistoryOrderGetDouble(ticket,ORDER_TP);
ord_data.type=ENUM_ORDER_TYPE(HistoryOrderGetInteger(ticket,ORDER_TYPE));
ord_data.type_filling=ENUM_ORDER_TYPE_FILLING(HistoryOrderGetInteger(ticket,ORDER_TYPE_FILLING));
ord_data.type_time=ENUM_ORDER_TYPE_TIME(HistoryOrderGetInteger(ticket,ORDER_TYPE_TIME));
ord_data.vol_cur=HistoryOrderGetDouble(ticket,ORDER_VOLUME_CURRENT);
ord_data.vol_init=HistoryOrderGetDouble(ticket,ORDER_VOLUME_INITIAL);
}
else
{
ord_data.state=ENUM_ORDER_STATE(HistoryOrderGetInteger(ticket,ORDER_STATE));
cur_vol=HistoryOrderGetDouble(ticket,ORDER_VOLUME_CURRENT);
init_vol=HistoryOrderGetDouble(ticket,ORDER_VOLUME_INITIAL);
}
//---switch(ord_data.state)
{
caseORDER_STATE_CANCELED: if(get_data)
{
if(ord_data.vol_init==ord_data.vol_cur)
{
ord_data.real_state=ORD_NONE_CANCELED;
}
else
{
ord_data.real_state=ORD_NONE_PARTIAL_CANCELED;
}
}
else
{
if(init_vol==cur_vol)
{
ord_data.real_state=ORD_NONE_CANCELED;
}
else
{
ord_data.real_state=ORD_NONE_PARTIAL_CANCELED;
}
}
break;
caseORDER_STATE_PARTIAL: ord_data.real_state=ORD_NONE_PARTIAL;
break;
caseORDER_STATE_EXPIRED: ord_data.real_state=ORD_NONE_EXPIRED;
break;
caseORDER_STATE_FILLED: ord_data.real_state=ORD_NONE_FILLED;
break;
caseORDER_STATE_REJECTED: ord_data.real_state=ORD_NONE_REJECTED;
break;
}
}
elseif(OrderSelect(ticket))
{
if(get_data)
{
ord_data.comment=OrderGetString(ORDER_COMMENT);
ord_data.expiration=datetime(OrderGetInteger(ORDER_TIME_EXPIRATION));
ord_data.magic=OrderGetInteger(ORDER_MAGIC);
ord_data.pos_id=OrderGetInteger(ORDER_POSITION_ID);
ord_data.price_cur=OrderGetDouble(ORDER_PRICE_CURRENT);
ord_data.price_open=OrderGetDouble(ORDER_PRICE_OPEN);
ord_data.price_stlim=OrderGetDouble(ORDER_PRICE_STOPLIMIT);
ord_data.sl=OrderGetDouble(ORDER_SL);
ord_data.state=ENUM_ORDER_STATE(OrderGetInteger(ORDER_STATE));
ord_data.symbol=OrderGetString(ORDER_SYMBOL);
ord_data.t_done_msc=datetime(OrderGetInteger(ORDER_TIME_DONE_MSC));
ord_data.t_set_msc =datetime( OrderGetInteger(ORDER_TIME_SETUP_MSC));
ord_data.time_done =datetime( OrderGetInteger(ORDER_TIME_DONE ));
ord_data.time_setup=datetime(OrderGetInteger(ORDER_TIME_SETUP));
ord_data.tp=OrderGetDouble(ORDER_TP);
ord_data.type=ENUM_ORDER_TYPE(OrderGetInteger(ORDER_TYPE));
ord_data.type_filling=ENUM_ORDER_TYPE_FILLING(OrderGetInteger(ORDER_TYPE_FILLING));
ord_data.type_time=ENUM_ORDER_TYPE_TIME(OrderGetInteger(ORDER_TYPE_TIME));
ord_data.vol_cur=OrderGetDouble(ORDER_VOLUME_CURRENT);
ord_data.vol_init=OrderGetDouble(ORDER_VOLUME_INITIAL);
}
else
{
ord_data.state=ENUM_ORDER_STATE(OrderGetInteger(ORDER_STATE));
}
//--- switch(ord_data.state)
{
caseORDER_STATE_STARTED:
caseORDER_STATE_REQUEST_ADD:
caseORDER_STATE_REQUEST_MODIFY:
caseORDER_STATE_REQUEST_CANCEL: ord_data.real_state=ORD_BUSY;
break;
caseORDER_STATE_PARTIAL: ord_data.real_state=ORD_EXIST_PARTIAL;
break;
caseORDER_STATE_PLACED: ord_data.real_state=ORD_EXIST;
break;
}
}
else
{
ord_data.error_code=GetLastError();
}
//--- if(( ord_data.error_code!=ERR_SUCCESS) ||
(ord_data.real_state==ORD_NOT_SPECIFIED))
{
return(SELECT_ERROR);
}
else
{
switch(ord_data.real_state)
{
case ORD_BUSY: return(SELECT_BUSY);
break;
case ORD_EXIST:
case ORD_EXIST_PARTIAL: return(SELECT_TRUE);
break;
default: return(SELECT_FALSE);
break;
}
}
}
else
{
ord_data.error_code=ERR_ZERO_TICKET;
return(SELECT_ERROR);
}
}
//+------------------------------------------------------------------+
Но ничего не изменилось
2016.08.0316:57:53.415 Test_Pos_selct (GAZR-9.16,M1) OnBookEvent: Position exists
2016.08.0316:57:53.415 Test_Pos_selct (GAZR-9.16,M1) OnBookEvent: Position type: POSITION_TYPE_BUY2016.08.0316:57:53.415 Test_Pos_selct (GAZR-9.16,M1) OnBookEvent: Position volume: 22016.08.0316:57:53.415 Test_Pos_selct (GAZR-9.16,M1) PartClosePos: Order sent for part close position.
2016.08.0316:57:53.423 Test_Pos_selct (GAZR-9.16,M1) OnTradeTransaction: Order resived #502761792016.08.0316:57:53.454 Test_Pos_selct (GAZR-9.16,M1) OnTradeTransaction: Order #50276179add to history.
2016.08.0316:57:53.454 Test_Pos_selct (GAZR-9.16,M1) OnTradeTransaction: Order #50276179not found.
2016.08.0316:57:53.454 Test_Pos_selct (GAZR-9.16,M1) OnTradeTransaction: Position exists
2016.08.0316:57:53.454 Test_Pos_selct (GAZR-9.16,M1) OnTradeTransaction: Position type: POSITION_TYPE_BUY2016.08.0316:57:53.454 Test_Pos_selct (GAZR-9.16,M1) OnTradeTransaction: Position volume: 22016.08.0316:57:53.454 Test_Pos_selct (GAZR-9.16,M1) OnTradeTransaction: Deal, based on order #50276179done.
2016.08.0316:57:53.454 Test_Pos_selct (GAZR-9.16,M1) OnTradeTransaction: Position exists.
2016.08.0316:57:53.454 Test_Pos_selct (GAZR-9.16,M1) OnTradeTransaction: Position type: POSITION_TYPE_BUY2016.08.0316:57:53.454 Test_Pos_selct (GAZR-9.16,M1) OnTradeTransaction: Position volume: 1
Это было очевидно, но "ради чистоты" эксперимента...
Я выше говорил - не увлекайтесь ордерами: смотрите на сделки. Вот короткий код, который показывает КОГДА изменяется объём позиции и какой тип транзакции при этом:
Karputov Vladimir И упростить OnTradeTransaction() - рассматривать только добавление сделки в историю - никаких ордеров
Ну и будет у Вас всё нормально, даже и не начинайте писать (не тратьте время).
А начал разбираться с этим вопросом, потому что на реале сбои пошли.
Похоже на выходных обновили сервер. Миллисекунды появились. Возможно будут еще сюрпризы.
Видимо функция OnTradeTransaction работает независимо от журнала операций.
Думаю такое поведение функции оправдано, незачем тормозить поток операций и ждать пока в журнале все запишется и посчитается.
В вашем случае наверное лучше использовать OnTrade,
или ждать и периодически с минимальной паузой проверять когда сделка появится в истории.
Похоже на выходных обновили сервер. Миллисекунды появились. Возможно будут еще сюрпризы.
Видимо функция OnTradeTransaction работает независимо от журнала операций.
Думаю такое поведение функции оправдано, незачем тормозить поток операций и ждать пока в журнале все запишется и посчитается.
В вашем случае наверное лучше использовать OnTrade,
или ждать и периодически с минимальной паузой проверять когда сделка появится в истории.
Привет, Сергей!
Да обновили, но не в выходные, а в четверг после вечерней сессии (спрашивал у брокера).
Я не могу использовать событие Trade() и ждать, когда обновятся данные в терминале.
Эксперт был написан давно, и до недавнего времени работал "как часы" (возможно мне везло, и событие TRADE_TRANSACTION_DEAL_ADD всегда приходило первым).
Эксперту важно как можно быстрее совершить ответную сделку, поэтому ассинхронный режим и OnTradeTransaction().
Сейчас, эксперт отсылает (иногда) дублирующие ордера на открытие и закрытие позиции.
Вы: "Думаю такое поведение функции оправдано, незачем тормозить поток операций и ждать пока в журнале все запишется и посчитается".
Всё равно пишется и считается после прихода TRADE_TRANSACTION_DEAL_ADD :)
Дело в том, что TRADE_TRANSACTION_DEAL_ADD может потерятся, а TRADE_TRANSACTION_HISTORY_ADD прийти, и тогда в терминале будут устаревшие данные о позиции. :(,
что собственно и происходит.
Странно, что разработчики не подумали об этом.
TRADE_TRANSACTION_HISTORY_ADD приходит лишь в том случае, если ордер исполнился или удалён (отменён), следовательно,
при изменении состояния ордера (соответственно позиция может изменится), в терминал должна поступить информация об изменении позиции,
даже, если TRADE_TRANSACTION_DEAL_ADD - потерялось
Посмотрим, что ответят разработчики.
Karputov Vladimir И упростить OnTradeTransaction() - рассматривать только добавление сделки в историю - никаких ордеров
Ну и будет у Вас всё нормально, даже и не начинайте писать (не тратьте время).
Просьба к "учителям" и "всезнайкам" выступать по существу,
а не просто ногу задирать у столба, чтобы отметится.
Прежде чем выпендриваться пред людьми желающими вам помочь, сформулировали бы нормально свой вопрос. Причем здесь асиннхронная отправка ордера, если частичное закрытие выполняется функцией OrderSend(). Вы вообще о чем вопрос то задаете?
Супер!
Это нужно рассматривать как помощь?
А Карпутов вообще не причём, просто когда я писал своё сообщение он уже запостил своё, а я не видел.
Изначально вопрос ставился так (если лень ситать сначала)
Как построить логирование, чтобы показать разработчикам наличие ошибки?
Зачем, я сам всё сделал и с логах отчётливо видно, что
после прихода TRADE_TRANSACTION_HISTORY_ADD (перед TRADE_TRANSACTION_DEAL_ADD)
терминал не обновляет информацию о позиции.
prostotrader, Вам Дмитрий правильно подсказывает, что частичное (и полное) закрытие идёт не в асинхронном порядке в вашем коде, а в синхронном... значит программа ждёт отклика с сервера...
Вероятно, что OnTradeTransaction срабатывает быстрее, чем изменяется сама позиция.
Потом тут:
можно попробовать зациклить проверку позиции. Может поможет....
Вот как-то так:
Не зная точный алгоритм (ТО, ЧТО нужно от программы), сложно оценивать правильность его реализации...
Изменил полностью на ассинхронный режим
Но ничего не изменилось
Это было очевидно, но "ради чистоты" эксперимента...
Полные логи в подвале
1. >Не зная точный алгоритм (ТО, ЧТО нужно от программы), сложно оценивать правильность его реализации...
Думал, что не сложно понять, что делает программа, ну если не понятно, то
Экперт открывает позизию на рынке ФОРТС объёмом 2 контракта, если позизия открыта, то она частично закрывается объёмом в 1 контракт,
затем, позиция закрывается полностью. И так несколько раз, пока счётчик tr_cnt<50
2. Вы можете "крутить" PositionSelect() хоть миллион раз - это ничего не изменит, потому что
пока вы в цикле, событие TRADE_TRANSACTION_DEAL_ADD не поступит, а значит терминал не обновит
информацию о позициию
...
Я выше говорил - не увлекайтесь ордерами: смотрите на сделки. Вот короткий код, который показывает КОГДА изменяется объём позиции и какой тип транзакции при этом:
И вот принты, при частичном закрытии:
Хорошо видно, что как только прошло события с типом торговой транзакции TRADE_TRANSACTION_DEAL_ADD - всё, данные позиции в терминале обновлены.