← Назад

Частичное исполнение лимитной заявки

Добрый день,

Подскажите, пожалуйста, как на S# корректно реализовать следующий алгоритм:

  1. Выставляем лимитную заявку на покупку
  2. При каждом частичном исполнении заявки выставляется тейк-профит в виде противоположной лимитной заявки. Тейк-профит должен быть всегда один, т.е. при каждом новом частичном исполнении заявки старый тейк-профит отменяется, новый же с сальдированным большим объёмом - выставляется. И так, пока заявка на покупку полностью не исполнится. Т.е. в итоге после исполнения лимитной заявки на покупку из n лотов должна быть одна противоположная заявка на продажу (тейк-профит) тоже из n лотов.

Делаю примерно так через WhenNewTrade:


...
_orderOpen.WhenNewTrade(Connector)
                .Do(o =>
                {
                    // фиксируем набранную позицию
                    _summVolumeOpen += o.Trade.Volume;
                    _summPriceVolumeOpen += _orderdata.OpenPrice * o.Trade.Volume;

                    // Создать заявку тейк-профит по набранной позиции
                    CreateTakeProfitOrderSell(_summVolumeOpen);
                })
                .Apply(this);
...

        private void CreateTakeProfitOrderSell(decimal curvol)
        {
            lock (_syncObjSafe)
            {
                if (_orderClose != null && _orderClose.State == OrderStates.Active)
                    CancelOrder(_orderClose);
                
                _orderClose = this.SellAtLimit(_orderdata.ClosePrice, curvol);
                
                ...

                RegisterOrder(_orderClose);
            }
        }

Проблема в том, что при исполнении лимитной заявки по частям WhenNewTrade вызывается быстро несколько раз так, что заявка тейк-профит _orderClose не успевает принять состояние Active и поэтому не отменяется. WhenPartiallyMatched, как я понял, тоже не решает проблему - это правило вызывается столько же раз, сколько и WhenNewTrade. В общем, в результате вместо одного тейк-профита у меня выставляется сразу несколько, что неправильно (все заявки кроме последней должны быть отменёнными).

Комментарии (3)

Вход или Создать аккаунт, Войдите или зарегистрируйтесь, чтобы оставить комментарий

Support
Support
  1. Лучше использовать два события WhenPartyallyMatched и WhenNewTrade, потому что WhenPartyallyMatched обычно приходит раньше WhenNewTrade... В WhenNewTrade мы только проверяем была ли выполнена обработка в WhenPartyallyMatched.. 2. Вам не нужно полностью полагаться на статусы и события зяявки, а использовать собственные флаги для контроля за состоянием...которые проверить
Knup
Knup Автор

А как это реализуется на практике через собственные флаги? Я имею ввиду как дождаться, того, что выставленная заявка станет активной, чтобы к ней можно было применить CancelOrder ? Просто не совсем очевидно.

Support
Support
  1. Есть событие WhenRegistered,,, В этот момент заявка имеет статус Act ive. 2, Могу рекомендовать следующий подход.. Для каждого "логического" типа заявок (вход, тейк, стоп и т.п.) создается метод, в котором собраны все события для данной заявки... В событиях заявки реализуется необходимая логика.. Можно иметь шаблон такого метода и использовать его в разных стратегиях. Ниже показан пример..
   private void TakeOrderProcessing(Order order)
    {

        if (_isStopping)
            return;

        order.Comment = "Take";

        try
        {

            if (this.Root.SessionState != eSessionState.ActiveSession)
                throw new Exception("Не могу выставить заявку - сессия неактивна!");

            order.WhenChanged(this.Connector).Do(o =>
            {
                this.AddDebugLog("{0} заявка изменена. {1}", o.Comment, o);

                var bl = ETSBlocks.FirstOrDefault(b => b.Take.Orders.Any(ord => ord.TransactionId == o.TransactionId));
                if (bl != null)
                    Take.CanChange = bl.Take.Orders.Any(ord => ord.State == OrderStates.Active);

            }).Apply(this);

            order.WhenRegistered(this.Connector).Do(o =>
            {

                var bl = ETSBlocks.FirstOrDefault(b => b.Take.Orders.Any(ord => ord.TransactionId == o.TransactionId));

                if (bl != null)
                    Take.CanChange = bl.Take.Orders.Any(ord => ord.State == OrderStates.Active);


                this.AddDebugLog("{0} заявка зарегистрирована. {1}", o.Comment, o);
            }).Apply(this).Once();

            var orderWhenRegisterFailed = order.WhenRegisterFailed(this.Connector);

            orderWhenRegisterFailed.Do(of =>
            {
                this.AddWarningLog("Ошибка регистрации {0} заявки. {1}. {2}", of.Order.Comment, of.Order, of.Error.Message);
                this.Rules.RemoveRulesByToken(orderWhenRegisterFailed.Token, orderWhenRegisterFailed);
            }).Apply(this);

            var orderWhenCanceled = order.WhenCanceled(this.Connector);

            orderWhenCanceled.Do(o =>
            {
                this.AddInfoLog("Отменена {0} заявка. {1}", o.Comment, o);

                var bl = ETSBlocks.FirstOrDefault(b => b.Take.Orders.Any(ord => ord.TransactionId == o.TransactionId));

                if (bl != null)
                {

                    Take.CanChange = bl.Take.Orders.Any(ord => ord.State == OrderStates.Active);

                    if (AlgorithmStatus == AlgorithmStatuses.SendCancelTake)
                    {


                        if (bl.Take.Orders.All(t => t.State != OrderStates.Active))
                        {
                            var vol = Volume - bl.Take.Orders.Sum(t => t.Volume - t.Balance);

                            var newTake = this.CreateOrder(Direction.Invert(), _newTakePrice, vol);

                            bl.Take.ActivateLevel = _newTakePrice;
                            bl.Take.Orders.Add(newTake);

                            this.AddInfoLog("Изменение тейк-заявки. Послылаем команда на выставление новой тейк-заявки. Новая цена {0}", _newTakePrice);

                            TakeOrderProcessing(newTake);
                        }

                    }
                }
                else
                    this.AddWarningLog("Не найден TakeStopBlock");

                UpdateInfo();

                this.Rules.RemoveRulesByToken(orderWhenCanceled.Token, orderWhenCanceled);
            }).Apply(this);

            var orderWhenMatched = order.WhenMatched(this.Connector);
            orderWhenMatched.Do(o =>
            {
                this.AddInfoLog("Полное исполнение {0} заявки. {1}", o.Comment, o);

                var bl = ETSBlocks.FirstOrDefault(b => b.Take.Orders.Any(ord => ord.TransactionId == o.TransactionId));

                if (bl != null)
                {
                    Take.CanChange = bl.Take.Orders.Any(ord => ord.State == OrderStates.Active);
                    var enterVolume = bl.Enter.Orders.Where(ord => ord.State == OrderStates.Done).Sum(ord => ord.Volume - ord.Balance);
                    var takeVolume = bl.Take.Orders.Where(ord => ord.State == OrderStates.Done).Sum(ord => ord.Volume - ord.Balance);

                    if (enterVolume == takeVolume)
                        AlgorithmStatus = AlgorithmStatuses.Take;

                }
                else
                    this.AddWarningLog("Не найден TakeStopBlock");

                UpdateInfo();

                this.Rules.RemoveRulesByToken(orderWhenMatched.Token, orderWhenMatched);
            }).Apply(this);

            order.WhenPartiallyMatched(this.Connector).Do(o =>
            {
                if (o.IsMatchedPartially())
                {
                    this.AddInfoLog("Частичное исполнение {0} заявки. {1}", o.Comment, o);
                }
            }).Apply(this);

            order.WhenNewTrade(this.Connector).Do(trade =>
            {
                var bl = ETSBlocks.FirstOrDefault(b => b.Take.Orders.Any(o => o.TransactionId == trade.Order.TransactionId));
                if (bl != null)
                {
                    if (bl.Take.Trades.All(t => t.Trade.Id != trade.Trade.Id))
                        bl.Take.Trades.Add(trade);
                }

                UpdateInfo();

            }).Apply(this);

            AlgorithmStatus = AlgorithmStatuses.SendTake;

            this.RegisterOrder(order);

            this.AddInfoLog("Послана {0} заявка. Price: {0}", order.Comment, order.Price);
        }
        catch (Exception ex)
        {
            this.AddErrorLog(string.Format("{0} / {1}", ex.Message, ex.StackTrace));
        }

    } 
Knup