finsight / QUIKSharp

QUIK# (QUIK Sharp) is the QUIK Lua interface ported to .NET.
Other
232 stars 135 forks source link

Надежный способ получать объект TransactionReply и id выставленного ордера по id транзакции #299

Open MrJimm opened 3 years ago

MrJimm commented 3 years ago

В своем приложении для автоматизации мне нужно получать объект TransactionReply ответа на транзакцию выставления нового стоп ордера, с ее статусом, и id только что выставленного ордера. Сейчас я делаю это через вот такую странную конструкцию, потому что способа лучше пока не нашел:

Подписываюсь на событие OnTransReply и складываю в конкарент очередь все приходящие ответы на транзакцию:

private void Events_OnTransReply(QuikSharp.DataStructures.Transaction.TransactionReply transReply)
{
    _transResponses[transReply.TransID] = transReply;
}

Дальше, при создании ордера, в цикле с таймаутом жду, пока в этой очереди появится реплай с нужным id транзакции. Именно сам реплай мне нужен, чтобы проверять у него статус и сообщение, по которым можно понять, что ордер, например, не выставился, потому что был превышен лимит на кредитование и т.п.:

var createOrderRes = _quik.StopOrders.CreateStopOrder(order).Result;

//await for transaction response
var transId = Math.Abs(createOrderRes);
var getTransRespTimeoutMs = 400;
var getTransRespIterations = 40;
TransactionReply transResp = null;

for (var i = 0; i < getTransRespIterations; ++i)
{
    if (_transResponses.ContainsKey(transId))
    {
        _transResponses.TryRemove(transId, out TransactionReply res);
        transResp = res;
        break;
    }
    Thread.Sleep(getTransRespTimeoutMs);
}
  1. Тут встречается первая проблема: Как видно, таймаут и число попыток достаточно больше, но все равно, в редких случаях я встречаю ситуации, когда transResp остается нулевым - а это значит что, похоже, до этого в очередь в коллбэке события не положили ответ на транзакцию с id == Math.Abs(createOrderRes)

  2. Вторая проблема более странная и встречается тоже внезапно, и тоже раз в пару дней (по ощущениям - когда выставляешь несколько ордеров последовательно, с небольшим таймаутом между ними): в получаемом по transId ордере неверный secCode инструмента!

Как я это обнаруживаю: Если transactionResponse пришел с хорошим кодом и без ошибок, я дальше получаю по нему ордер таким образом:

var newOrder = this.GetNewStopOrderByTransactionId(transId).Result;

Где:

private async Task<QuikSharp.DataStructures.StopOrder> GetNewStopOrderByTransactionId(long transId)
{
    var attemptsCount = 20;
    Thread.Sleep(100);

    var stopOrder = await GetStopOrderByTransactionId(transId);
    if (stopOrder != null)
        return stopOrder;

    //if stop order wasn't found
    for (var i = 2; i <= attemptsCount; ++i)
    {
        Thread.Sleep(500);
        Log($"Attempt {i}/{attemptsCount} to retrieve new stop order for transaction {transId}...");
        stopOrder = await GetStopOrderByTransactionId(transId);
        if (stopOrder != null)
            return stopOrder;
    }

    return null;
}

И, соответственно:

private async Task<QuikSharp.DataStructures.StopOrder> GetStopOrderByTransactionId(long transId)
{
    var stopOrders = await _quik.StopOrders.GetStopOrders();
    if (stopOrders != null)
    {
        var stopOrder = stopOrders.FirstOrDefault(x => x.TransId == transId);
        if (stopOrder != null)
            return stopOrder;
    }
    return null;
} 

И вот тут начинается проблема: я начал ловить ситуации, когда SecCode инструмента в ордере, объект которого я отправил на выставление в метод _quik.StopOrders.CreateStopOrder(order).Result; либы не совпадает с secCode ордера, который я получил по this.GetNewStopOrderByTransactionId(transId) (!!!) То есть периодически выполняется return в этой проверке:

if (newOrder.SecCode != newStopOrder.Ticker)
{
    var msg = $"Placed order secCode {newOrder.SecCode} is not equal to requested ticker {newStopOrder.Ticker}. Check by transaction ids: awaited to createdOrderRes {createOrderRes} transaction id {transId} but obtained new order with trans id {newOrder.TransId}";
    Log($"ERROR! {msg}");
    return new PlaceNewOrderResult
    {
        IsError = true,
        State = PlaceNewOrderResult.ResultState.GeneralError,
        Message = msg
    };
}

Как будто бы, если последовательно выставляются несколько ордеров по разным инструментам, то транзакции по ним могут перепутаться. Проверяя свой код, я пока не нашел в нем ошибку, которая могла бы приводить к такому поведению. Хотел бы услышать, сталкивался ли кто-то еще с таким поведением? И буду благодарен, если сможете подсказать более простой и надежный способ получать выставленный ордер и данные TransactionReply по id транзакции.