Exemplo n.º 1
0
        public async Task <BackendResponse <AccountDepositWithdrawResponse> > AccountDeposit([FromBody] AccountDepositWithdrawRequest request)
        {
            var account = _accountsCacheService.Get(request.ClientId, request.AccountId);

            var changeTransferLimit = _marginSettings.IsLive &&
                                      request.PaymentType == PaymentType.Transfer &&
                                      !IsCrypto(account.BaseAssetId);

            try
            {
                var transactionId = await _accountManager.UpdateBalanceAsync(account, Math.Abs(request.Amount),
                                                                             AccountHistoryType.Deposit, "Account deposit", request.TransactionId, changeTransferLimit);

                _operationsLogService.AddLog($"account deposit {request.PaymentType}", request.ClientId, request.AccountId, request.ToJson(), true.ToJson());

                return(BackendResponse <AccountDepositWithdrawResponse> .Ok(
                           new AccountDepositWithdrawResponse { TransactionId = transactionId }));
            }
            catch (Exception e)
            {
                await _log.WriteErrorAsync(nameof(AccountsBalanceController), "AccountDeposit", request?.ToJson(), e);

                return(BackendResponse <AccountDepositWithdrawResponse> .Error(e.Message));
            }
        }
Exemplo n.º 2
0
        public async Task TestAccountUpdate_Success(string accountId, string updatedTradingConditionId,
                                                    decimal updatedWithdrawTransferLimit, bool isDisabled, bool isWithdrawalDisabled)
        {
            var account = Accounts.Single(x => x.Id == accountId);
            var time    = DateService.Now().AddMinutes(1);

            var accountsProjection = AssertEnv();

            var updatedContract = new AccountContract()
            {
                Id                    = accountId,
                ClientId              = account.ClientId,
                TradingConditionId    = updatedTradingConditionId,
                BaseAssetId           = account.BaseAssetId,
                Balance               = account.Balance,
                WithdrawTransferLimit = updatedWithdrawTransferLimit,
                LegalEntity           = account.LegalEntity,
                IsDisabled            = isDisabled,
                ModificationTimestamp = account.ModificationTimestamp,
                IsWithdrawalDisabled  = account.IsWithdrawalDisabled,
                IsDeleted             = false,
                AdditionalInfo        = "{}"
            };

            await accountsProjection.Handle(new AccountChangedEvent(time, "test",
                                                                    updatedContract, AccountChangedEventTypeContract.Updated));

            var resultedAccount = _accountsCacheService.Get(accountId);

            Assert.AreEqual(updatedTradingConditionId, resultedAccount.TradingConditionId);
            Assert.AreEqual(updatedWithdrawTransferLimit, resultedAccount.WithdrawTransferLimit);
            Assert.AreEqual(isDisabled, resultedAccount.IsDisabled);
            Assert.AreEqual(isWithdrawalDisabled, resultedAccount.IsWithdrawalDisabled);
        }
Exemplo n.º 3
0
        public void Check_Account_Calculations_Correct()
        {
            var position1 = TestObjectsFactory.CreateOpenedPosition("EURUSD", Accounts[0],
                                                                    MarginTradingTestsUtils.TradingConditionId, 1000, 1.02M);

            _ordersCache.Positions.Add(position1);
            position1.UpdateClosePrice(1.04M);

            position1.GetFpl();
            var account = _accountsCacheService.Get(position1.AccountId);

            Assert.IsNotNull(account);
            Assert.AreEqual(1000, account.Balance);
            Assert.AreEqual(20, Math.Round(account.GetPnl(), 5));
            Assert.AreEqual(1020, account.GetTotalCapital());
            Assert.AreEqual(6.8M, account.GetUsedMargin());
            Assert.AreEqual(1009.8M, account.GetMarginAvailable());
            Assert.AreEqual(150M, account.GetMarginUsageLevel());

            var position2 = TestObjectsFactory.CreateOpenedPosition("EURUSD", Accounts[0],
                                                                    MarginTradingTestsUtils.TradingConditionId, -30000, 1.02M);

            _ordersCache.Positions.Add(position2);
            position2.UpdateClosePrice(1.04M);
            position2.GetFpl();

            Assert.IsNotNull(account);
            Assert.AreEqual(1000, account.Balance);
            Assert.AreEqual(-580, Math.Round(account.GetPnl(), 5));
            Assert.AreEqual(420, Math.Round(account.GetTotalCapital(), 5));
            Assert.AreEqual(214.8m, account.GetUsedMargin());
            Assert.AreEqual(97.8m, Math.Round(account.GetMarginAvailable(), 5));
            Assert.AreEqual(1.95531m, Math.Round(account.GetMarginUsageLevel(), 5));
        }
Exemplo n.º 4
0
        public bool IsEnoughBalance(Order order)
        {
            _fplService.CalculateMargin(order, order.FplData);
            var orderMargin            = order.GetMarginInit();
            var accountMarginAvailable = _accountsCacheService.Get(order.ClientId, order.AccountId).GetMarginAvailable();

            return(accountMarginAvailable >= orderMargin);
        }
Exemplo n.º 5
0
        public bool IsEnoughBalance(Order order)
        {
            var volumeInAccountAsset = _cfdCalculatorService.GetVolumeInAccountAsset(order.GetOrderType(), order.AccountAssetId, order.Instrument, Math.Abs(order.Volume));
            var account      = _accountsCacheService.Get(order.ClientId, order.AccountId);
            var accountAsset = _accountAssetsCacheService.GetAccountAsset(order.TradingConditionId, order.AccountAssetId, order.Instrument);

            return(account.GetMarginAvailable() * accountAsset.LeverageInit >= volumeInAccountAsset);
        }
Exemplo n.º 6
0
        private void MakeOrderActive(Order order)
        {
            order.OpenDate = DateTime.UtcNow;
            order.Status   = OrderStatus.Active;

            var account = _accountsCacheService.Get(order.ClientId, order.AccountId);

            _swapCommissionService.SetCommissionRates(account.TradingConditionId, account.BaseAssetId, order);
            _ordersCache.ActiveOrders.Add(order);
            _orderActivatedEventChannel.SendEvent(this, new OrderActivatedEventArgs(order));
        }
Exemplo n.º 7
0
        public async Task FreezeWithdrawalMargin(string accountId, string operationId, decimal amount)
        {
            var account = _accountsCacheService.Get(accountId);

            if (account.AccountFpl.WithdrawalFrozenMarginData.TryAdd(operationId, amount))
            {
                account.AccountFpl.WithdrawalFrozenMargin = account.AccountFpl.WithdrawalFrozenMarginData.Values.Sum();
                //TODO: think about approach
                //await _accountMarginFreezingRepository.TryInsertAsync(new AccountMarginFreezing(operationId,
                //    accountId, amount));
            }
        }
Exemplo n.º 8
0
        public Task <AccountStatContract> GetAccountStats(string accountId)
        {
            try
            {
                var stats = _accountsCacheService.Get(accountId);

                return(Task.FromResult(stats.ConvertToContract()));
            }
            catch (AccountNotFoundException ex)
            {
                throw new LogInfoOnlyException(ex.Message, ex);
            }
        }
Exemplo n.º 9
0
        public void UpdateOrderFpl(IOrder order, FplData fplData)
        {
            fplData.AccountBaseAssetAccuracy = _assetsCache.GetAssetAccuracy(order.AccountAssetId);
            fplData.QuoteRate = _cfdCalculatorService.GetQuoteRateForQuoteAsset(order.AccountAssetId, order.Instrument);

            var fpl = order.GetOrderType() == OrderDirection.Buy
                ? (order.ClosePrice - order.OpenPrice) * fplData.QuoteRate * order.GetMatchedVolume()
                : (order.OpenPrice - order.ClosePrice) * fplData.QuoteRate * order.GetMatchedVolume();

            fplData.Fpl = Math.Round(fpl, fplData.AccountBaseAssetAccuracy);

            var accountAsset = _accountAssetsCacheService.GetAccountAsset(order.TradingConditionId, order.AccountAssetId, order.Instrument);

            fplData.MarginInit        = Math.Round(order.ClosePrice * order.GetMatchedVolume() * fplData.QuoteRate / accountAsset.LeverageInit, fplData.AccountBaseAssetAccuracy);
            fplData.MarginMaintenance = Math.Round(order.ClosePrice * order.GetMatchedVolume() * fplData.QuoteRate / accountAsset.LeverageMaintenance, fplData.AccountBaseAssetAccuracy);

            fplData.OpenCrossPrice  = Math.Round(order.OpenPrice * fplData.QuoteRate, order.AssetAccuracy);
            fplData.CloseCrossPrice = Math.Round(order.ClosePrice * fplData.QuoteRate, order.AssetAccuracy);

            fplData.OpenPrice     = order.OpenPrice;
            fplData.ClosePrice    = order.ClosePrice;
            fplData.SwapsSnapshot = order.GetSwaps();

            fplData.CalculatedHash = fplData.ActualHash;

            fplData.TotalFplSnapshot = order.GetTotalFpl(fplData.SwapsSnapshot);

            var account = _accountsCacheService.Get(order.ClientId, order.AccountId);

            account.CacheNeedsToBeUpdated();
        }
Exemplo n.º 10
0
        public async Task Handle(FailLiquidationInternalCommand command,
                                 IEventPublisher publisher)
        {
            var executionInfo = await _operationExecutionInfoRepository.GetAsync <LiquidationOperationData>(
                operationName : LiquidationSaga.OperationName,
                id : command.OperationId);

            if (executionInfo?.Data == null)
            {
                return;
            }

            _accountUpdateService.RemoveLiquidationStateIfNeeded(executionInfo.Data.AccountId,
                                                                 $"Liquidation [{command.OperationId}] failed ({command.Reason})", command.OperationId,
                                                                 executionInfo.Data.LiquidationType);

            _chaosKitty.Meow(
                $"{nameof(FailLiquidationInternalCommand)}:" +
                $"Publish_LiquidationFailedInternalEvent:" +
                $"{command.OperationId}");

            publisher.PublishEvent(new LiquidationFailedEvent
            {
                OperationId                     = command.OperationId,
                CreationTime                    = _dateService.Now(),
                Reason                          = command.Reason,
                LiquidationType                 = command.LiquidationType.ToType <LiquidationTypeContract>(),
                AccountId                       = executionInfo.Data.AccountId,
                AssetPairId                     = executionInfo.Data.AssetPairId,
                Direction                       = executionInfo.Data.Direction?.ToType <PositionDirectionContract>(),
                QuoteInfo                       = executionInfo.Data.QuoteInfo,
                ProcessedPositionIds            = executionInfo.Data.ProcessedPositionIds,
                LiquidatedPositionIds           = executionInfo.Data.LiquidatedPositionIds,
                OpenPositionsRemainingOnAccount = _ordersCache.Positions.GetPositionsByAccountIds(executionInfo.Data.AccountId).Count,
                CurrentTotalCapital             = _accountsCache.Get(executionInfo.Data.AccountId).GetTotalCapital(),
            });

            _liquidationEndEventChannel.SendEvent(this, new LiquidationEndEventArgs
            {
                OperationId           = command.OperationId,
                CreationTime          = _dateService.Now(),
                AccountId             = executionInfo.Data.AccountId,
                LiquidatedPositionIds = executionInfo.Data.LiquidatedPositionIds,
                FailReason            = command.Reason,
            });
        }
Exemplo n.º 11
0
        public async Task <OrderInitialParameters> GetOrderInitialParameters(string assetPairId, string accountId)
        {
            var account = _accountsCacheService.Get(accountId);

            var equivalentPricesSettings = GetReportingEquivalentPricesSettings(account.LegalEntity);

            return(await GetOrderInitialParameters(assetPairId, account.LegalEntity, equivalentPricesSettings,
                                                   account.BaseAssetId));
        }
Exemplo n.º 12
0
        private void UpdatePendingOrderMargin(Position order, FplData fplData)
        {
            fplData.AccountBaseAssetAccuracy = _assetsCache.GetAssetAccuracy(order.AccountAssetId);

            CalculateMargin(order, fplData);

            fplData.CalculatedHash = fplData.ActualHash;
            _accountsCacheService.Get(order.AccountId).CacheNeedsToBeUpdated();
        }
Exemplo n.º 13
0
        private async Task Handle(FreezeAmountForWithdrawalCommand command, IEventPublisher publisher)
        {
            var(executionInfo, _) = await _operationExecutionInfoRepository.GetOrAddAsync(
                operationName : OperationName,
                operationId : command.OperationId,
                factory : () => new OperationExecutionInfo <WithdrawalFreezeOperationData>(
                    operationName: OperationName,
                    id: command.OperationId,
                    lastModified: _dateService.Now(),
                    data: new WithdrawalFreezeOperationData
            {
                State     = OperationState.Initiated,
                AccountId = command.AccountId,
                Amount    = command.Amount,
            }
                    ));

            MarginTradingAccount account = null;

            try
            {
                account = _accountsCacheService.Get(command.AccountId);
            }
            catch
            {
                publisher.PublishEvent(new AmountForWithdrawalFreezeFailedEvent(command.OperationId, _dateService.Now(),
                                                                                command.AccountId, command.Amount, $"Failed to get account {command.AccountId}"));
                return;
            }

            if (executionInfo.Data.SwitchState(OperationState.Initiated, OperationState.Started))
            {
                if (account.GetFreeMargin() >= command.Amount)
                {
                    await _accountUpdateService.FreezeWithdrawalMargin(command.AccountId, command.OperationId,
                                                                       command.Amount);

                    _chaosKitty.Meow(command.OperationId);

                    publisher.PublishEvent(new AmountForWithdrawalFrozenEvent(command.OperationId, _dateService.Now(),
                                                                              command.AccountId, command.Amount, command.Reason));
                }
                else
                {
                    publisher.PublishEvent(new AmountForWithdrawalFreezeFailedEvent(command.OperationId,
                                                                                    _dateService.Now(),
                                                                                    command.AccountId, command.Amount, "Not enough free margin"));
                }

                _chaosKitty.Meow(command.OperationId);

                await _operationExecutionInfoRepository.Save(executionInfo);
            }
        }
        private void NotifyAccountStatsChanged(string accountId)
        {
            var account = _accountsCacheService.Get(accountId);

            account.CacheNeedsToBeUpdated();

            // not needed right now

            //var stats = account.ToRabbitMqContract();

            //_rabbitMqNotifyService.UpdateAccountStats(new AccountStatsUpdateMessage {Accounts = new[] {stats}});
        }
Exemplo n.º 15
0
        public OrderBackendContract[] GetAccountOpenPositions([FromBody] AccountClientIdBackendRequest request)
        {
            var account = _accountsCacheService.Get(request.ClientId, request.AccountId);

            var positions = _ordersCache.ActiveOrders.GetOrdersByAccountIds(account.Id).Select(item => item.ToBackendContract()).ToList();
            var orders    = _ordersCache.WaitingForExecutionOrders.GetOrdersByAccountIds(account.Id).Select(item => item.ToBackendContract()).ToList();

            positions.AddRange(orders);
            var result = positions.ToArray();

            return(result);
        }
Exemplo n.º 16
0
        private string GetEquivalentAsset(string clientId, string accountId)
        {
            var account            = _accountsCacheService.Get(clientId, accountId);
            var equivalentSettings =
                _marginSettings.ReportingEquivalentPricesSettings.FirstOrDefault(x => x.LegalEntity == account.LegalEntity);

            if (string.IsNullOrEmpty(equivalentSettings?.EquivalentAsset))
            {
                throw new Exception($"No reporting equivalent prices asset found for legalEntity: {account.LegalEntity}");
            }

            return(equivalentSettings.EquivalentAsset);
        }
Exemplo n.º 17
0
        private void UpdateOrderFplData(IOrder order, FplData fplData)
        {
            fplData.AccountBaseAssetAccuracy = _assetsCache.GetAssetAccuracy(order.AccountAssetId);
            fplData.FplRate = _cfdCalculatorService.GetQuoteRateForQuoteAsset(order.AccountAssetId, order.Instrument,
                                                                              order.LegalEntity, order.Volume * (order.ClosePrice - order.OpenPrice) > 0);

            var fpl = (order.ClosePrice - order.OpenPrice) * fplData.FplRate * order.Volume;

            fplData.Fpl = Math.Round(fpl, fplData.AccountBaseAssetAccuracy);

            CalculateMargin(order, fplData);

            fplData.OpenPrice     = order.OpenPrice;
            fplData.ClosePrice    = order.ClosePrice;
            fplData.SwapsSnapshot = order.GetSwaps();

            fplData.CalculatedHash = fplData.ActualHash;

            fplData.TotalFplSnapshot = order.GetTotalFpl(fplData.SwapsSnapshot);

            _accountsCacheService.Get(order.ClientId, order.AccountId).CacheNeedsToBeUpdated();
        }
Exemplo n.º 18
0
        private void OnClosed(OrderUpdateBaseEventArgs ea)
        {
            var order = ea.Order;

            _threadSwitcher.SwitchThread(async() =>
            {
                _clientNotifyService.NotifyOrderChanged(order);

                var totalFpl = order.GetTotalFpl();
                var account  = _accountsCacheService.Get(order.ClientId, order.AccountId);
                await _accountManager.UpdateBalanceAsync(account, totalFpl, AccountHistoryType.OrderClosed,
                                                         $"Balance changed on order close (id = {order.Id})", order.Id);

                await SendOrderChangedNotification(order.ClientId, order);
            });
        }
Exemplo n.º 19
0
        public override void Start()
        {
            var orders = _marginTradingBlobRepository.Read <List <Order> >(LykkeConstants.StateBlobContainer, BlobName) ?? new List <Order>();

            orders.ForEach(o =>
            {
                // migrate orders to add LegalEntity field
                // todo: can be removed once published to prod
                if (o.LegalEntity == null)
                {
                    o.LegalEntity = _accountsCacheService.Get(o.ClientId, o.AccountId).LegalEntity;
                }
            });

            _orderCache.InitOrders(orders);

            base.Start();
        }
Exemplo n.º 20
0
        public Task Handle(MarketStateChangedEvent e, ICommandSender sender)
        {
            if (!e.IsEnabled)
            {
                return(Task.CompletedTask);
            }

            // resuming liquidations on corresponding accounts upon market open
            _accountsCacheService.GetAll()
            .Where(a => a.IsInLiquidation())
            .SelectMany(a => _ordersCache.Positions.GetPositionsByAccountIds(a.Id))
            .GroupBy(p => p.AccountId)
            .Where(AnyAssetRelatesToMarket)
            .ForEach(account => SendResumeCommand(account.Key));

            return(Task.CompletedTask);

            #region internal functions

            bool AnyAssetRelatesToMarket(IGrouping <string, Position> positions) =>
            positions
            .Select(p => _assetPairsCache.GetAssetPairById(p.AssetPairId))
            .Any(assetPair => assetPair.MarketId == e.Id);

            void SendResumeCommand(string accountId)
            {
                var account = _accountsCacheService.Get(accountId);

                sender.SendCommand(new ResumeLiquidationInternalCommand
                {
                    OperationId  = account.LiquidationOperationId,
                    CreationTime = DateTime.UtcNow,
                    IsCausedBySpecialLiquidation = false,
                    ResumeOnlyFailed             = true,
                    Comment = "Trying to resume liquidation because market has been opened"
                }, _cqrsContextNamesSettings.TradingEngine);
            }

            #endregion
        }
Exemplo n.º 21
0
        public void PerformEmailNotification(DateTime calculationTime)
        {
            _threadSwitcher.SwitchThread(async() =>
            {
                await _semaphore.WaitAsync();

                try
                {
                    var processedCalculations = (await _overnightSwapHistoryRepository.GetAsync(calculationTime, null))
                                                .Where(x => x.IsSuccess && x.Time >= calculationTime)
                                                .ToList();

                    var notifications = processedCalculations
                                        .GroupBy(x => x.ClientId)
                                        .Select(c => new OvernightSwapNotification
                    {
                        CliendId = c.Key,
                        CalculationsByAccount = c.GroupBy(a => a.AccountId)
                                                .Select(a =>
                        {
                            var account = _accountsCacheService.Get(c.Key, a.Key);
                            if (account == null)
                            {
                                return(null);
                            }
                            return(new OvernightSwapNotification.AccountCalculations()
                            {
                                AccountId = a.Key,
                                AccountCurrency = account.BaseAssetId,
                                Calculations = a.Select(calc =>
                                {
                                    var instrumentName = _assetPairsCache.GetAssetPairByIdOrDefault(calc.Instrument)?.Name
                                                         ?? calc.Instrument;
                                    return new OvernightSwapNotification.SingleCalculation
                                    {
                                        Instrument = instrumentName,
                                        Direction = calc.Direction == OrderDirection.Buy ? "Long" : "Short",
                                        Volume = calc.Volume,
                                        SwapRate = calc.SwapRate,
                                        Cost = calc.Value,
                                        PositionId = calc.OpenOrderId,
                                    };
                                }).ToList()
                            });
                        }).Where(x => x != null).ToList()
                    }
                                                );

                    var clientsWithIncorrectMail = new List <string>();
                    var clientsSentEmails        = new List <string>();
                    foreach (var notification in notifications)
                    {
                        try
                        {
                            var clientEmail = await _clientAccountService.GetEmail(notification.CliendId);
                            if (string.IsNullOrEmpty(clientEmail))
                            {
                                clientsWithIncorrectMail.Add(notification.CliendId);
                                continue;
                            }

                            await _emailService.SendOvernightSwapEmailAsync(clientEmail, notification);
                            clientsSentEmails.Add(notification.CliendId);
                        }
                        catch (Exception e)
                        {
                            await _log.WriteErrorAsync(nameof(OvernightSwapNotificationService),
                                                       nameof(PerformEmailNotification), e, DateTime.UtcNow);
                        }
                    }

                    if (clientsWithIncorrectMail.Any())
                    {
                        await _log.WriteWarningAsync(nameof(OvernightSwapNotificationService), nameof(PerformEmailNotification),
                                                     $"Emails of some clients are incorrect: {string.Join(", ", clientsWithIncorrectMail)}.", DateTime.UtcNow);
                    }
                    if (clientsSentEmails.Any())
                    {
                        await _log.WriteInfoAsync(nameof(OvernightSwapNotificationService), nameof(PerformEmailNotification),
                                                  $"Emails sent to: {string.Join(", ", clientsSentEmails)}.", DateTime.UtcNow);
                    }
                }
                finally
                {
                    _semaphore.Release();
                }
            });
        }
Exemplo n.º 22
0
        public void Check_Account_Calculations_Correct()
        {
            var order1 = new Order
            {
                Id = Guid.NewGuid().ToString("N"),
                Instrument = "EURUSD",
                AccountId = Accounts[0].Id,
                ClientId = Accounts[0].ClientId,
                TradingConditionId = MarginTradingTestsUtils.TradingConditionId,
                AccountAssetId = Accounts[0].BaseAssetId,
                AssetAccuracy = 5,
                LegalEntity = "LYKKETEST",
                Volume = 1000,
                MatchedOrders =
                    new MatchedOrderCollection(new List<MatchedOrder>
                    {
                        new MatchedOrder {MatchedDate = DateTime.UtcNow, Volume = 1000}
                    }), //need for GetMatchedVolume()
                OpenPrice = 1.02M,
                Status = OrderStatus.Active,
            };

            _ordersCache.ActiveOrders.Add(order1);
            order1.UpdateClosePrice(1.04M);

            order1.GetFpl();
            var account = _accountsCacheService.Get(order1.ClientId, order1.AccountId);

            Assert.IsNotNull(account);
            Assert.AreEqual(1000, account.Balance);
            Assert.AreEqual(20, Math.Round(account.GetPnl(), 5));
            Assert.AreEqual(1020, account.GetTotalCapital());
            Assert.AreEqual(6.93333333m, account.GetUsedMargin());
            Assert.AreEqual(1009.60, account.GetMarginAvailable());
            Assert.AreEqual(147.11538468611316571447748352m, account.GetMarginUsageLevel());

            var order2 = new Order
            {
                Id = Guid.NewGuid().ToString("N"),
                Instrument = "EURUSD",
                AccountId = Accounts[0].Id,
                ClientId = Accounts[0].ClientId,
                TradingConditionId = MarginTradingTestsUtils.TradingConditionId,
                AccountAssetId = Accounts[0].BaseAssetId,
                AssetAccuracy = 5,
                LegalEntity = "LYKKETEST",
                Volume = -30000,
                MatchedOrders = new MatchedOrderCollection(new List<MatchedOrder> { new MatchedOrder { MatchedDate = DateTime.UtcNow, Volume = 30000 } }), //need for GetMatchedVolume()
                OpenPrice = 1.02M,
                Status = OrderStatus.Active,
            };

            _ordersCache.ActiveOrders.Add(order2);
            order2.UpdateClosePrice(1.04M);
            order2.GetFpl();

            Assert.IsNotNull(account);
            Assert.AreEqual(1000, account.Balance);
            Assert.AreEqual(-580, Math.Round(account.GetPnl(), 5));
            Assert.AreEqual(420, Math.Round(account.GetTotalCapital(), 5));
            Assert.AreEqual(214.93333333m, account.GetUsedMargin());
            Assert.AreEqual(97.60, Math.Round(account.GetMarginAvailable(), 5));
            Assert.AreEqual(1.95409m, Math.Round(account.GetMarginUsageLevel(), 5));
        }
Exemplo n.º 23
0
        private async Task Handle(BlockAccountsForDeletionCommand command, IEventPublisher publisher)
        {
            var executionInfo = await _operationExecutionInfoRepository.GetOrAddAsync(
                operationName : OperationName,
                operationId : command.OperationId,
                factory : () => new OperationExecutionInfo <DeleteAccountsOperationData>(
                    operationName: OperationName,
                    id: command.OperationId,
                    lastModified: _dateService.Now(),
                    data: new DeleteAccountsOperationData
            {
                State = OperationState.Initiated
            }
                    ));

            //todo think how to remove state saving from commands handler here and for Withdrawal
            if (executionInfo.Data.SwitchState(OperationState.Initiated, OperationState.Started))
            {
                var failedAccounts = new Dictionary <string, string>();

                foreach (var accountId in command.AccountIds)
                {
                    MarginTradingAccount account = null;
                    try
                    {
                        account = _accountsCacheService.Get(accountId);
                    }
                    catch (Exception exception)
                    {
                        failedAccounts.Add(accountId, exception.Message);
                        continue;
                    }

                    var positionsCount = _orderReader.GetPositions().Count(x => x.AccountId == accountId);
                    if (positionsCount != 0)
                    {
                        failedAccounts.Add(accountId, $"Account contain {positionsCount} open positions which must be closed before account deletion.");
                        continue;
                    }

                    var orders = _orderReader.GetPending().Where(x => x.AccountId == accountId).ToList();
                    if (orders.Any())
                    {
                        var(failedToCloseOrderId, failReason) = ((string)null, (string)null);
                        foreach (var order in orders)
                        {
                            try
                            {
                                _tradingEngine.CancelPendingOrder(order.Id, order.AdditionalInfo, command.OperationId,
                                                                  $"{nameof(DeleteAccountsCommandsHandler)}: force close all orders.",
                                                                  OrderCancellationReason.AccountInactivated);
                            }
                            catch (Exception exception)
                            {
                                failedToCloseOrderId = order.Id;
                                failReason           = exception.Message;
                                break;
                            }
                        }

                        if (failedToCloseOrderId != null)
                        {
                            failedAccounts.Add(accountId, $"Failed to close order [{failedToCloseOrderId}]: {failReason}.");
                            continue;
                        }
                    }

                    if (account.AccountFpl.WithdrawalFrozenMarginData.Any())
                    {
                        await _log.WriteErrorAsync(nameof(DeleteAccountsCommandsHandler),
                                                   nameof(BlockAccountsForDeletionCommand), account.ToJson(),
                                                   new Exception("While deleting an account it contained some frozen withdrawal data. Account is deleted."));
                    }

                    if (account.AccountFpl.UnconfirmedMarginData.Any())
                    {
                        await _log.WriteErrorAsync(nameof(DeleteAccountsCommandsHandler),
                                                   nameof(BlockAccountsForDeletionCommand), account.ToJson(),
                                                   new Exception("While deleting an account it contained some unconfirmed margin data. Account is deleted."));
                    }

                    if (account.Balance != 0)
                    {
                        await _log.WriteErrorAsync(nameof(DeleteAccountsCommandsHandler),
                                                   nameof(BlockAccountsForDeletionCommand), account.ToJson(),
                                                   new Exception("While deleting an account it's balance on side of TradingCore was non zero. Account is deleted."));
                    }

                    if (!await UpdateAccount(account, true,
                                             r => failedAccounts.Add(accountId, r), command.Timestamp))
                    {
                        continue;
                    }
                }

                publisher.PublishEvent(new AccountsBlockedForDeletionEvent(
                                           operationId: command.OperationId,
                                           eventTimestamp: _dateService.Now(),
                                           failedAccountIds: failedAccounts
                                           ));

                _chaosKitty.Meow($"{nameof(BlockAccountsForDeletionCommand)}: " +
                                 "Save_OperationExecutionInfo: " +
                                 $"{command.OperationId}");

                await _operationExecutionInfoRepository.Save(executionInfo);
            }
        }
Exemplo n.º 24
0
        private void NotifyAccountStatsChanged(string clientId, string accountId)
        {
            var account = _accountsCacheService.Get(clientId, accountId);

            NotifyAccountStatsChanged(account);
        }
Exemplo n.º 25
0
        private async Task <Order> ExecuteOrderByMatchingEngineAsync(Order order, IMatchingEngineBase matchingEngine,
                                                                     bool checkStopout, OrderModality modality = OrderModality.Regular)
        {
            //TODO: think how not to execute one order twice!!!

            var now = _dateService.Now();

            //just in case )
            if (order.OrderType != OrderType.Market &&
                order.Validity.HasValue &&
                now.Date > order.Validity.Value.Date)
            {
                order.Expire(now);
                _orderCancelledEventChannel.SendEvent(this,
                                                      new OrderCancelledEventArgs(order,
                                                                                  new OrderCancelledMetadata {
                    Reason = OrderCancellationReasonContract.Expired
                }));
                return(order);
            }

            order.StartExecution(_dateService.Now(), matchingEngine.Id);

            _orderExecutionStartedEvenChannel.SendEvent(this, new OrderExecutionStartedEventArgs(order));

            if (order.PositionsToBeClosed.Any())
            {
                var netVolume    = 0M;
                var rejectReason = default(OrderRejectReason?);
                foreach (var positionId in order.PositionsToBeClosed)
                {
                    if (!_ordersCache.Positions.TryGetPositionById(positionId, out var position))
                    {
                        rejectReason = OrderRejectReason.ParentPositionDoesNotExist;
                        continue;
                    }
                    if (position.Status != PositionStatus.Active)
                    {
                        rejectReason = OrderRejectReason.ParentPositionIsNotActive;
                        continue;
                    }

                    netVolume += position.Volume;

                    position.StartClosing(_dateService.Now(), order.OrderType.GetCloseReason(), order.Originator, "");
                }

                if (netVolume == 0M && rejectReason.HasValue)
                {
                    order.Reject(rejectReason.Value,
                                 rejectReason.Value == OrderRejectReason.ParentPositionDoesNotExist
                        ? "Related position does not exist"
                        : "Related position is not active", "", _dateService.Now());
                    _orderRejectedEventChannel.SendEvent(this, new OrderRejectedEventArgs(order));
                    return(order);
                }

                // there is no any global lock of positions / orders, that's why it is possible to have concurrency
                // in position close process
                // since orders, that have not empty PositionsToBeClosed should close positions and not open new ones
                // volume of executed order should be equal to position volume, but should have opposite sign
                if (order.Volume != -netVolume)
                {
                    var metadata = new OrderChangedMetadata
                    {
                        OldValue        = order.Volume.ToString("F2"),
                        UpdatedProperty = OrderChangedProperty.Volume
                    };
                    order.ChangeVolume(-netVolume, _dateService.Now(), order.Originator);
                    _orderChangedEventChannel.SendEvent(this, new OrderChangedEventArgs(order, metadata));
                }
            }

            var equivalentRate = _cfdCalculatorService.GetQuoteRateForQuoteAsset(order.EquivalentAsset,
                                                                                 order.AssetPairId, order.LegalEntity);
            var fxRate = _cfdCalculatorService.GetQuoteRateForQuoteAsset(order.AccountAssetId,
                                                                         order.AssetPairId, order.LegalEntity);

            order.SetRates(equivalentRate, fxRate);

            var shouldOpenNewPosition = ShouldOpenNewPosition(order);

            if (modality == OrderModality.Regular && order.Originator != OriginatorType.System)
            {
                try
                {
                    _validateOrderService.MakePreTradeValidation(
                        order,
                        shouldOpenNewPosition,
                        matchingEngine);
                }
                catch (ValidateOrderException ex)
                {
                    RejectOrder(order, ex.RejectReason, ex.Message, ex.Comment);
                    return(order);
                }
            }

            var matchedOrders = await matchingEngine.MatchOrderAsync(order, shouldOpenNewPosition, modality);

            if (!matchedOrders.Any())
            {
                RejectOrder(order, OrderRejectReason.NoLiquidity, "No orders to match", "");
                return(order);
            }

            if (matchedOrders.SummaryVolume < Math.Abs(order.Volume))
            {
                if (order.FillType == OrderFillType.FillOrKill)
                {
                    RejectOrder(order, OrderRejectReason.NoLiquidity, "Not fully matched", "");
                    return(order);
                }
                else
                {
                    order.PartiallyExecute(_dateService.Now(), matchedOrders);
                    _ordersCache.InProgress.Add(order);
                    return(order);
                }
            }

            if (order.Status == OrderStatus.ExecutionStarted)
            {
                var accuracy = _assetPairsCache.GetAssetPairByIdOrDefault(order.AssetPairId)?.Accuracy ??
                               AssetPairsCache.DefaultAssetPairAccuracy;

                order.Execute(_dateService.Now(), matchedOrders, accuracy);

                _orderExecutedEventChannel.SendEvent(this, new OrderExecutedEventArgs(order));

                if (checkStopout)
                {
                    var account      = _accountsCacheService.Get(order.AccountId);
                    var accountLevel = account.GetAccountLevel();

                    if (accountLevel == AccountLevel.StopOut)
                    {
                        CommitStopOut(account, null);
                    }
                    else if (accountLevel > AccountLevel.None)
                    {
                        _marginCallEventChannel.SendEvent(this, new MarginCallEventArgs(account, accountLevel));
                    }
                }
            }

            return(order);
        }
Exemplo n.º 26
0
        public void CalculateAndChargeSwaps()
        {
            _currentStartTimestamp = _dateService.Now();

            var filteredOrders = GetOrdersForCalculation();

            //start calculation in a separate thread
            _threadSwitcher.SwitchThread(async() =>
            {
                using (await _mutex.LockAsync())
                {
                    foreach (var accountOrders in filteredOrders.GroupBy(x => x.AccountId))
                    {
                        var clientId = accountOrders.First().ClientId;
                        MarginTradingAccount account;
                        try
                        {
                            account = _accountsCacheService.Get(clientId, accountOrders.Key);
                        }
                        catch (Exception ex)
                        {
                            await ProcessFailedOrders(accountOrders, accountOrders.Key, null, ex);
                            continue;
                        }

                        foreach (var ordersByInstrument in accountOrders.GroupBy(x => x.Instrument))
                        {
                            var firstOrder = ordersByInstrument.FirstOrDefault();
                            IAccountAssetPair accountAssetPair;
                            try
                            {
                                accountAssetPair = _accountAssetsCacheService.GetAccountAsset(
                                    firstOrder?.TradingConditionId, firstOrder?.AccountAssetId, firstOrder?.Instrument);
                            }
                            catch (Exception ex)
                            {
                                await ProcessFailedOrders(ordersByInstrument, account.Id, ordersByInstrument.Key, ex);
                                continue;
                            }

                            foreach (OrderDirection direction in Enum.GetValues(typeof(OrderDirection)))
                            {
                                var orders = ordersByInstrument.Where(order => order.GetOrderType() == direction).ToList();
                                if (orders.Count == 0)
                                {
                                    continue;
                                }

                                try
                                {
                                    await ProcessOrders(orders, ordersByInstrument.Key, account, accountAssetPair, direction);
                                }
                                catch (Exception ex)
                                {
                                    await ProcessFailedOrders(orders, account.Id, ordersByInstrument.Key, ex);
                                    continue;
                                }
                            }
                        }
                    }
                }
            });
        }
Exemplo n.º 27
0
        public async Task ExecuteAsync(IEventPublisher failurePublisher, string accountId, string operationId, string reason)
        {
            if (failurePublisher == null)
            {
                throw new ArgumentNullException(nameof(failurePublisher));
            }

            if (string.IsNullOrEmpty(accountId))
            {
                throw new ArgumentNullException(nameof(accountId));
            }

            if (string.IsNullOrEmpty(operationId))
            {
                throw new ArgumentNullException(nameof(operationId));
            }

            var executionInfo = await _operationExecutionInfoRepository.GetAsync <LiquidationOperationData>(
                LiquidationSaga.OperationName,
                operationId);

            if (executionInfo?.Data == null)
            {
                await _log.WriteWarningAsync(nameof(LiquidationFailureExecutor),
                                             nameof(ExecuteAsync),
                                             new { operationId, accountId }.ToJson(),
                                             $"Unable to execute failure. Liquidation execution info was not found.");

                return;
            }

            _accountUpdateService.RemoveLiquidationStateIfNeeded(accountId, reason, operationId, executionInfo.Data.LiquidationType);

            var account = _accountsCache.Get(accountId);

            failurePublisher.PublishEvent(new LiquidationFailedEvent
            {
                OperationId                     = operationId,
                CreationTime                    = _dateService.Now(),
                Reason                          = reason,
                LiquidationType                 = executionInfo.Data.LiquidationType.ToType <LiquidationTypeContract>(),
                AccountId                       = executionInfo.Data.AccountId,
                AssetPairId                     = executionInfo.Data.AssetPairId,
                Direction                       = executionInfo.Data.Direction?.ToType <PositionDirectionContract>(),
                QuoteInfo                       = executionInfo.Data.QuoteInfo,
                ProcessedPositionIds            = executionInfo.Data.ProcessedPositionIds,
                LiquidatedPositionIds           = executionInfo.Data.LiquidatedPositionIds,
                OpenPositionsRemainingOnAccount = _ordersCache.Positions.GetPositionsByAccountIds(executionInfo.Data.AccountId).Count,
                CurrentTotalCapital             = account.GetTotalCapital(),
            });

            await _log.WriteInfoAsync(nameof(LiquidationFailureExecutor),
                                      nameof(ExecuteAsync),
                                      new { account, operationId, reason }.ToJson(),
                                      $"Successfully published {nameof(LiquidationFailedEvent)} event");

            _liquidationEndEventChannel.SendEvent(this, new LiquidationEndEventArgs
            {
                OperationId           = operationId,
                CreationTime          = _dateService.Now(),
                AccountId             = executionInfo.Data.AccountId,
                LiquidatedPositionIds = executionInfo.Data.LiquidatedPositionIds,
                FailReason            = reason,
            });

            await _log.WriteInfoAsync(nameof(LiquidationFailureExecutor),
                                      nameof(ExecuteAsync),
                                      new { account, operationId, reason }.ToJson(),
                                      $"Successfully published {nameof(LiquidationEndEventArgs)} event");
        }
Exemplo n.º 28
0
        public void CalculateAndChargeSwaps()
        {
            _currentStartTimestamp = _dateService.Now();

            var filteredOrders = GetOrdersForCalculation();

            //start calculation in a separate thread
            _threadSwitcher.SwitchThread(async() =>
            {
                await _semaphore.WaitAsync();

                try
                {
                    await _log.WriteInfoAsync(nameof(OvernightSwapService), nameof(CalculateAndChargeSwaps),
                                              $"Started, # of orders: {filteredOrders.Count}.", DateTime.UtcNow);

                    foreach (var accountOrders in filteredOrders.GroupBy(x => x.AccountId))
                    {
                        var clientId = accountOrders.First().ClientId;
                        var orders   = accountOrders.ToList();
                        MarginTradingAccount account;
                        try
                        {
                            account = _accountsCacheService.Get(clientId, accountOrders.Key);
                        }
                        catch (Exception ex)
                        {
                            await ProcessFailedOrders(orders, clientId, accountOrders.Key, null, ex);
                            continue;
                        }

                        foreach (var order in orders)
                        {
                            IAccountAssetPair accountAssetPair;
                            try
                            {
                                accountAssetPair = _accountAssetsCacheService.GetAccountAsset(
                                    order?.TradingConditionId, order?.AccountAssetId, order?.Instrument);
                            }
                            catch (Exception ex)
                            {
                                await ProcessFailedOrder(order, clientId, account.Id, order.AccountId, ex);
                                continue;
                            }

                            try
                            {
                                await ProcessOrder(order, account, accountAssetPair);
                            }
                            catch (Exception ex)
                            {
                                await ProcessFailedOrder(order, clientId, account.Id, order.AccountId, ex);
                                continue;
                            }
                        }
                    }

                    await ClearOldState();

                    await _log.WriteInfoAsync(nameof(OvernightSwapService), nameof(CalculateAndChargeSwaps),
                                              $"Finished, # of calculations: {_overnightSwapCache.GetAll().Count(x => x.Time >= _currentStartTimestamp)}.", DateTime.UtcNow);
                }
                finally
                {
                    _semaphore.Release();
                }
            });
        }
Exemplo n.º 29
0
 public MarginTradingAccountBackendContract GetAccount(string clientId, string accountId)
 {
     return(_accountsCacheService.Get(clientId, accountId).ToFullBackendContract(_settings.IsLive));
 }
Exemplo n.º 30
0
        private void SendPositionHistoryEvent(Position position, PositionHistoryTypeContract historyType,
                                              decimal chargedPnl, string orderAdditionalInfo, Order dealOrder = null, decimal?dealVolume = null,
                                              PositionOpenMetadata metadata = null)
        {
            DealContract deal = null;

            if (dealOrder != null && dealVolume != null)
            {
                var sign = position.Volume > 0 ? 1 : -1;

                var accountBaseAssetAccuracy = AssetsConstants.DefaultAssetAccuracy;

                var fpl = Math.Round((dealOrder.ExecutionPrice.Value - position.OpenPrice) *
                                     dealOrder.FxRate * dealVolume.Value * sign, accountBaseAssetAccuracy);
                var balanceDelta = fpl - Math.Round(chargedPnl, accountBaseAssetAccuracy);

                var dealId = historyType == PositionHistoryTypeContract.Close
                    ? position.Id
                    : _identityGenerator.GenerateAlphanumericId();

                deal = new DealContract
                {
                    DealId                  = dealId,
                    PositionId              = position.Id,
                    Volume                  = dealVolume.Value,
                    Created                 = dealOrder.Executed.Value,
                    OpenTradeId             = position.OpenTradeId,
                    OpenOrderType           = position.OpenOrderType.ToType <OrderTypeContract>(),
                    OpenOrderVolume         = position.OpenOrderVolume,
                    OpenOrderExpectedPrice  = position.ExpectedOpenPrice,
                    CloseTradeId            = dealOrder.Id,
                    CloseOrderType          = dealOrder.OrderType.ToType <OrderTypeContract>(),
                    CloseOrderVolume        = dealOrder.Volume,
                    CloseOrderExpectedPrice = dealOrder.Price,
                    OpenPrice               = position.OpenPrice,
                    OpenFxPrice             = position.OpenFxPrice,
                    ClosePrice              = dealOrder.ExecutionPrice.Value,
                    CloseFxPrice            = dealOrder.FxRate,
                    Fpl             = fpl,
                    PnlOfTheLastDay = balanceDelta,
                    AdditionalInfo  = dealOrder.AdditionalInfo,
                    Originator      = dealOrder.Originator.ToType <OriginatorTypeContract>()
                };

                var account = _accountsCacheService.Get(position.AccountId);

                _cqrsSender.PublishEvent(new PositionClosedEvent(account.Id, account.ClientId,
                                                                 deal.DealId, position.AssetPairId, balanceDelta));

                _accountUpdateService.FreezeUnconfirmedMargin(position.AccountId, deal.DealId, balanceDelta)
                .GetAwaiter().GetResult();    //todo consider making this async or pass to broker
            }

            var positionContract = _convertService.Convert <Position, PositionContract>(position,
                                                                                        o => o.ConfigureMap(MemberList.Destination).ForMember(x => x.TotalPnL, c => c.Ignore()));

            positionContract.TotalPnL = position.GetFpl();

            var historyEvent = new PositionHistoryEvent
            {
                PositionSnapshot    = positionContract,
                Deal                = deal,
                EventType           = historyType,
                Timestamp           = _dateService.Now(),
                ActivitiesMetadata  = metadata?.ToJson(),
                OrderAdditionalInfo = orderAdditionalInfo,
            };

            _rabbitMqNotifyService.PositionHistory(historyEvent);
        }