public bool CloseOrder(MarketOrder order, decimal price, PositionExitReason exitReason)
        {
            using (var ctx = DatabaseContext.Instance.Make())
            {
                var account = ctx.ACCOUNT.FirstOrDefault(ac => ac.ID == order.AccountID);
                if (account == null)
                {
                    Logger.ErrorFormat("Закрытие ордера #{0}: невозможно прочитать данные счета ({1})",
                                       order.ID, order.AccountID);
                    return(false);
                }

                // провести ордер через биллинг
                ORDER_BILL bill = null;
                if (order.State == PositionState.Opened)
                {
                    bill = BillingManager.ProcessPriceForOrderClosing(order, LinqToEntity.DecorateAccount(account), ctx);
                }

                // посчитать результат
                // и обновить объект
                order.State     = PositionState.Closed;
                order.PriceExit = (float?)price;
                var deltaAbs = order.Side * (order.PriceExit.Value - order.PriceEnter);
                order.ResultPoints = DalSpot.Instance.GetPointsValue(order.Symbol, deltaAbs);
                var    deltaDepo = deltaAbs * order.Volume;
                var    quotes    = QuoteStorage.Instance.ReceiveAllData();
                string errorStr;
                var    resultedDepo = DalSpot.Instance.ConvertToTargetCurrency(order.Symbol, false, account.Currency,
                                                                               deltaDepo, quotes, out errorStr, false);

                if (!resultedDepo.HasValue)
                {
                    Logger.ErrorFormat("#{0} ({1} {2}{3}, {4:f1} пп): ошибка расчета прибыли в валюте депозита - {5}",
                                       order.ID,
                                       order.Side > 0 ? "B" : "S",
                                       order.Symbol,
                                       order.Volume,
                                       order.ResultPoints,
                                       errorStr);
                    return(false);
                }
                order.ResultDepo = (float)resultedDepo.Value;
                //order.Swap = (float)swap;
                order.ExitReason = exitReason;
                order.TimeExit   = DateTime.Now;
                var posClosed = LinqToEntity.UndecorateClosedPosition(order);

                POSITION pos = null;
                try
                {
                    // занести ордер в список закрытых позиций (создать новую запись "истории")
                    ctx.POSITION_CLOSED.Add(posClosed);

                    // удалить открытый ордер
                    pos = ctx.POSITION.FirstOrDefault(p => p.ID == order.ID);
                    if (pos == null)
                    {
                        Logger.ErrorFormat("CloseOrder - позиция {0} не найдена", order.ID);
                        ServiceManagerClientManagerProxy.Instance.CloseOrderResponse(null, RequestStatus.ServerError, "crudsav");

                        return(false);
                    }
                    ctx.POSITION.Remove(pos);

                    // посчитать профиты
                    if (bill != null)
                    {
                        BillingManager.ProcessOrderClosing(order, account, bill, ctx, quotes, brokerRepository.BrokerCurrency);
                    }

                    // сохранить изменения
                    ctx.SaveChanges();

                    // обновить баланс
                    var resultAbs = Math.Abs(order.ResultDepo);
                    if (!UpdateAccountBalance(ctx,
                                              account, (decimal)resultAbs,
                                              order.ResultDepo >= 0
                                                  ? BalanceChangeType.Profit
                                                  : BalanceChangeType.Loss,
                                              string.Format("результат сделки #{0}", posClosed.ID), DateTime.Now, order.ID))
                    {
                        Logger.ErrorFormat("Не удалось применить обновление баланса #{0}", posClosed.ID);
                    }
                }
                catch (OptimisticConcurrencyException ex)
                {
                    Logger.Error("CloseOrder - OptimisticConcurrencyException", ex);
                    ctx.Entry(posClosed).State = EntityState.Modified;
                    ((IObjectContextAdapter)ctx).ObjectContext.Refresh(RefreshMode.ClientWins, posClosed);
                    if (pos != null)
                    {
                        ctx.Entry(pos).State = EntityState.Modified;
                        ((IObjectContextAdapter)ctx).ObjectContext.Refresh(RefreshMode.ClientWins, pos);
                    }
                    ctx.SaveChanges();
                }
                catch (Exception ex)
                {
                    Logger.ErrorFormat("Ошибка закрытия позиции {0} (счет #{1}) (фиксация в БД): {2}",
                                       order.ID, order.AccountID, ex);
                    ServiceManagerClientManagerProxy.Instance.CloseOrderResponse(null, RequestStatus.ServerError, "crudsave");
                    return(false);
                }
            }

            // уведомить клиента
            ServiceManagerClientManagerProxy.Instance.CloseOrderResponse(order, RequestStatus.OK, "");

            // разослать торговый сигнал
            MakeOrderClosedSignal(order.AccountID, order.ID, (float)price);

            return(true);
        }
Exemple #2
0
        /// <summary>
        /// Закрытие сделок
        /// </summary>
        /// <param name="strId">Уникальные идентификаторы закрываемых сделок, перечисленные через запятую</param>
        /// <param name="timeExit">Время выхода</param>
        /// <param name="exitReason">Symbol, Side (в виде Ask и Bid), Price</param>
        /// <param name="lstPrice">Причина закрытия сделки, указанная пользователем</param>
        public List <string> ClosingPositions(string strId, DateTime timeExit, PositionExitReason exitReason, List <Tuple <string, int, float> > lstPrice)
        {
            Logger.Info("Начинаем закрывать сделки " + strId);

            var result = new List <string>();
            var id     = strId.ToIntArrayUniform();

            try
            {
                using (var ctx = DatabaseContext.Instance.Make())
                {
                    // Вытаскиваем все открытые сделки, которые нужно закрыть
                    var selOrders = new List <MarketOrder>();
                    // ReSharper disable LoopCanBeConvertedToQuery
                    foreach (var order in ctx.POSITION.Where(x => id.Contains(x.ID)))
                    {
                        selOrders.Add(LinqToEntity.DecorateOrder(order));
                    }
                    // ReSharper restore LoopCanBeConvertedToQuery

                    if (selOrders.Count != id.Length)
                    {
                        Logger.Error("ClosingPositions() - в таблице 'POSITION' не найдены некоторые или все сделки с идентификаторами " + strId);
                    }

                    // Группируем все сделки по счётам
                    var selOrderGroupByAccount = selOrders.GroupBy(x => x.AccountID);

                    // Перебираем все счета
                    foreach (var orderGroup in selOrderGroupByAccount)
                    {
                        // Список удачно закрытых сделок в текущем счёте
                        var successClosedPositions = new List <string>();

                        var acc = accountRepository.GetAccount(orderGroup.Key);
                        if (acc == null)
                        {
                            Logger.Error("ClosingPositions() - не удалось получить счёт " + orderGroup.Key);
                            continue;
                        }

                        Logger.Info("Начинаем закрывать сделки в счёте " + orderGroup.Key);
                        // Перебираем все сделки в текущем счёте
                        foreach (var order in orderGroup)
                        {
                            #region
                            //Ищем цену выхода, указанную пользователем, для сделок с таким тикером и направлением
                            var priceExitTuple = lstPrice.FirstOrDefault(x => x.Item1 == order.Symbol && x.Item2 == order.Side);

                            if (priceExitTuple == null)
                            {
                                Logger.Error(string.Format("ClosingPositions() - не найдена цена выхода, указанная пользователем, для сделок счёта {0} с тикером {1} и направлением {2}",
                                                           order.ID, order.Symbol, order.Side)); continue;
                            }

                            var closedOrder = order.MakeCopy();
                            closedOrder.State      = PositionState.Closed;
                            closedOrder.TimeExit   = timeExit;
                            closedOrder.PriceExit  = priceExitTuple.Item3;
                            closedOrder.ExitReason = exitReason;

                            // посчитать прибыль
                            string errorStr;
                            if (!DealProfitCalculator.CalculateOrderProfit(closedOrder, acc.Currency, priceExitTuple.Item3, out errorStr))
                            {
                                if (!string.IsNullOrEmpty(errorStr))
                                {
                                    Logger.Error("Сделка " + closedOrder.ID + " не будет закрыта - не удалось пересчитать прибыль : " + errorStr);
                                }
                                continue;
                            }

                            var balance = new BALANCE_CHANGE
                            {
                                AccountID   = order.AccountID,
                                ChangeType  = closedOrder.ResultBase > 0 ? (int)BalanceChangeType.Profit : (int)BalanceChangeType.Loss,
                                Description = string.Format("результат сделки #{0}", order.ID),
                                Amount      = (decimal)Math.Abs(closedOrder.ResultDepo),
                                ValueDate   = closedOrder.TimeExit.Value
                            };

                            // убрать сделку из числа открытых, добавить закрытую и добавить проводку по счету
                            try
                            {
                                // убрать
                                var pos = ctx.POSITION.FirstOrDefault(p => p.ID == order.ID);
                                ctx.POSITION.Remove(pos);
                                Logger.Info("запись о сделке " + order.ID + " удалена из таблици POSITION");

                                ctx.POSITION_CLOSED.Add(LinqToEntity.UndecorateClosedPosition(closedOrder));
                                Logger.Info("запись о сделке " + order.ID + " добавленав таблицу POSITION_CLOSED");

                                // добавить проводку по счету
                                ctx.BALANCE_CHANGE.Add(balance);
                                var acBase = ctx.ACCOUNT.FirstOrDefault(ac => ac.ID == order.AccountID);

                                if (acBase == null)
                                {
                                    Logger.Error("ClosingPositions() - не удалось найти счёт " + order.AccountID + " в таблице 'ACCOUNT', что бы добавить проводку");
                                    continue;
                                }

                                acBase.Balance += (decimal)closedOrder.ResultDepo;
                                Logger.Info("Баланс счёта " + order.AccountID + " изменён на величину " + (decimal)closedOrder.ResultDepo);
                            }
                            catch (Exception ex)
                            {
                                Logger.Error("ClosingPositions() - Ошибка при попытке убрать сделку из числа открытых, добавить закрытую и добавить проводку по счету", ex);
                                continue;
                            }
                            #endregion
                            successClosedPositions.Add(order.ID.ToString());
                            Logger.Error("Сделка " + order.ID + " отредактирована удачно");
                        }
                        ReCalculateAccountBalance(ctx, acc.ID);

                        Logger.Info("Начинаем сохранять в базу данных изменения по счёту " + orderGroup.Key);
                        ctx.SaveChanges();
                        result.AddRange(successClosedPositions);
                    }

                    if (result.Count == 0)
                    {
                        Logger.Info("Не удалось закрыть ни одной из указанных сделок");
                    }
                    else
                    {
                        Logger.Info("Изменения сохранены. Из указанных сделок " + strId + " закрыты следующие: " + string.Join(", ", result));
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Error("ClosingPositions() - возникла ошибка при попытке сохранить изменения в базу данных. Не удалось закрыть сделки " + strId, ex);
            }
            return(result);
        }
Exemple #3
0
        private static void SaveTrackInDatabase(RobotContextBacktest context, int accountId,
                                                List <BalanceChange> transfers,
                                                int transfersInDbCount)
        {
            try
            {
                Logger.InfoFormat("Сохранение изменений в БД для счета {0}", accountId);

                int nextPosId;
                using (var conn = DatabaseContext.Instance.Make())
                {
                    nextPosId = Math.Max(conn.POSITION.Max(p => p.ID), conn.POSITION_CLOSED.Max(p => p.ID)) + 1;
                }

                Logger.InfoFormat("Запись {0} позиций для счета {1}",
                                  context.PosHistory.Count, accountId);

                // закрытые ордера
                var listPos = new List <POSITION_CLOSED>();
                foreach (var pos in context.PosHistory)
                {
                    var orderClosed = LinqToEntity.UndecorateClosedPosition(pos);
                    orderClosed.ID = ++nextPosId;
                    listPos.Add(orderClosed);
                }

                using (var conn = DatabaseContext.Instance.Make())
                {
                    conn.BulkInsert(listPos);
                    conn.SaveChanges();
                }

                // трансферы...
                var listTrans =
                    transfers.Skip(transfersInDbCount).Select(t =>
                                                              new BALANCE_CHANGE
                {
                    AccountID  = accountId,
                    Amount     = t.Amount,
                    ChangeType = (int)t.ChangeType,
                    ValueDate  = t.ValueDate,
                }).ToList();
                // + трансферы по закрытым ордерам
                foreach (var pos in listPos)
                {
                    var transfer = new BALANCE_CHANGE
                    {
                        AccountID  = accountId,
                        Amount     = Math.Abs(pos.ResultDepo),
                        ChangeType = (int)(pos.ResultDepo >= 0
                            ? BalanceChangeType.Profit
                            : BalanceChangeType.Loss),
                        ValueDate = pos.TimeExit,
                        Position  = pos.ID,
                    };
                    listTrans.Add(transfer);
                }

                using (var conn = DatabaseContext.Instance.Make())
                {
                    conn.BulkInsert(listTrans);
                    conn.SaveChanges();
                }

                // открытые сделки - как есть
                using (var conn = DatabaseContext.Instance.Make())
                {
                    foreach (var pos in context.Positions)
                    {
                        var orderOpened = LinqToEntity.UndecorateOpenedPosition(pos);
                        orderOpened.ID = ++nextPosId;
                        conn.POSITION.Add(orderOpened);
                    }

                    conn.SaveChanges();
                }
                Logger.InfoFormat("Сохранение успешно для счета {0}: {1} сделок сохранено",
                                  accountId, context.PosHistory.Count + context.Positions.Count);
            }
            catch (Exception ex)
            {
                Logger.Error("Ошибка сохранения трека пользователя в БД", ex);
            }
        }