public async Task <Order> PlaceOrderAsync(Order order) { _orderPlacedEventChannel.SendEvent(this, new OrderPlacedEventArgs(order)); try { if (order.OrderType != OrderType.Market) { await PlacePendingOrder(order); return(order); } return(await PlaceOrderByMarketPrice(order)); } catch (ValidateOrderException ex) { RejectOrder(order, ex.RejectReason, ex.Message, ex.Comment); return(order); } catch (Exception ex) { RejectOrder(order, OrderRejectReason.TechnicalError, ex.Message); _log.WriteError(nameof(TradingEngine), nameof(PlaceOrderByMarketPrice), ex); return(order); } }
public void Is_Fpl_Buy_Correct() { const string instrument = "BTCUSD"; _bestPriceConsumer.SendEvent(this, new BestPriceChangeEventArgs(new InstrumentBidAskPair { Instrument = instrument, Ask = 800, Bid = 790 })); var order = new Order { Id = Guid.NewGuid().ToString(), AccountId = Accounts[0].Id, ClientId = Accounts[0].ClientId, AccountAssetId = Accounts[0].BaseAssetId, TradingConditionId = Accounts[0].TradingConditionId, Instrument = instrument, Volume = 10, MatchedOrders = new MatchedOrderCollection(new List <MatchedOrder> { new MatchedOrder { MatchedDate = DateTime.UtcNow, Volume = 10 } }), //need for GetMatchedVolume() OpenPrice = 790, LegalEntity = "LYKKEVU", Status = OrderStatus.Active, }; order.UpdateClosePrice(800); Assert.AreEqual(100, order.GetFpl()); }
public async Task OvernightSwapCalculation_Success() { _bestPriceConsumer.SendEvent(this, new BestPriceChangeEventArgs(new InstrumentBidAskPair { Instrument = "BTCUSD", Bid = 9000M, Ask = 9010M })); await _accountAssetsRepository.AddOrReplaceAsync(new AccountAssetPair { TradingConditionId = MarginTradingTestsUtils.TradingConditionId, BaseAssetId = "USD", Instrument = "BTCUSD", LeverageInit = 100, LeverageMaintenance = 150, SwapLong = 100, SwapShort = 100, OvernightSwapLong = 1, OvernightSwapShort = 1 }); await _accountAssetsManager.UpdateAccountAssetsCache(); var accountId = (await _fakeMarginTradingAccountsRepository.GetAllAsync("1")) .First(x => x.ClientId == "1" && x.BaseAssetId == "USD").Id; _ordersCache.ActiveOrders.Add(new Order { Id = "1", AccountId = accountId, Instrument = "BTCUSD", ClientId = "1", TradingConditionId = "1", AccountAssetId = "USD", Volume = 1, OpenDate = new DateTime(2017, 01, 01, 20, 50, 0), CloseDate = new DateTime(2017, 01, 02, 20, 50, 0), MatchedOrders = new MatchedOrderCollection(new List <MatchedOrder> { new MatchedOrder() { Volume = 1 } }), LegalEntity = "LYKKETEST", }); var accountBalance = (await _fakeMarginTradingAccountsRepository.GetAsync(accountId)).Balance; _overnightSwapService.CalculateAndChargeSwaps(); var calc = _overnightSwapCache.GetAll().First(); Assert.AreEqual(24.68493151M, calc.Value); Assert.True(calc.IsSuccess); Assert.AreEqual(accountBalance - 24.68493151M, (await _fakeMarginTradingAccountsRepository.GetAsync(accountId)).Balance); }
public void Is_Volume_Ivalid(decimal volume, bool isValid) { const string instrument = "BTCUSD"; var quote = new InstrumentBidAskPair { Instrument = instrument, Bid = 1.55M, Ask = 1.57M }; _bestPriceConsumer.SendEvent(this, new BestPriceChangeEventArgs(quote)); var order = new Order { CreateDate = DateTime.UtcNow, Id = Guid.NewGuid().ToString("N"), AccountId = Accounts[0].Id, ClientId = Accounts[0].ClientId, Instrument = instrument, Volume = volume, FillType = OrderFillType.FillOrKill }; if (isValid) { Assert.DoesNotThrow(() => _validateOrderService.Validate(order)); } else { var ex = Assert.Throws <ValidateOrderException>(() => _validateOrderService.Validate(order)); Assert.That(ex.RejectReason == OrderRejectReason.InvalidVolume); } }
private Task <Order> PlaceMarketOrderByMatchingEngineAsync(Order order, IMatchingEngineBase matchingEngine) { order.OpenOrderbookId = matchingEngine.Id; order.MatchingEngineMode = matchingEngine.Mode; matchingEngine.MatchMarketOrderForOpen(order, matchedOrders => { if (!matchedOrders.Any()) { order.CloseDate = DateTime.UtcNow; order.Status = OrderStatus.Rejected; order.RejectReason = OrderRejectReason.NoLiquidity; order.RejectReasonText = "No orders to match"; return(false); } if (matchedOrders.SummaryVolume < Math.Abs(order.Volume) && order.FillType == OrderFillType.FillOrKill) { order.CloseDate = DateTime.UtcNow; order.Status = OrderStatus.Rejected; order.RejectReason = OrderRejectReason.NoLiquidity; order.RejectReasonText = "Not fully matched"; return(false); } try { CheckIfWeCanOpenPosition(order, matchedOrders); } catch (ValidateOrderException e) { order.CloseDate = DateTime.UtcNow; order.Status = OrderStatus.Rejected; order.RejectReason = e.RejectReason; order.RejectReasonText = e.Message; return(false); } _equivalentPricesService.EnrichOpeningOrder(order); MakeOrderActive(order); return(true); }); if (order.Status == OrderStatus.Rejected) { _orderRejectedEventChannel.SendEvent(this, new OrderRejectedEventArgs(order)); } return(Task.FromResult(order)); }
public void Is_Quote_Returned() { const string instrument = "EURUSD"; _bestPriceConsumer.SendEvent(this, new BestPriceChangeEventArgs(new InstrumentBidAskPair { Instrument = instrument, Ask = 1.05M, Bid = 1.04M })); var quote = _quoteCacheService.GetQuote(instrument); Assert.IsNotNull(quote); Assert.AreEqual(1.04, quote.Bid); Assert.AreEqual(1.05, quote.Ask); }
private void CancelRelatedOrdersForPosition(Position position, OrderCancellationReason reason) { var metadata = new OrderCancelledMetadata { Reason = reason.ToType <OrderCancellationReasonContract>() }; foreach (var relatedOrderInfo in position.RelatedOrders) { if (_ordersCache.Active.TryPopById(relatedOrderInfo.Id, out var relatedOrder)) { relatedOrder.Cancel(_dateService.Now(), null); _orderCancelledEventChannel.SendEvent(this, new OrderCancelledEventArgs(relatedOrder, metadata)); } } }
public Task SetQuote(ExternalExchangeOrderbookMessage orderBookMessage) { var isDayOff = _assetPairDayOffService.IsDayOff(orderBookMessage.AssetPairId); var isEodOrderbook = orderBookMessage.ExchangeName == ExternalOrderbookService.EodExternalExchange; // we should process normal orderbook only if asset is currently tradable if (isDayOff && !isEodOrderbook) { return(Task.CompletedTask); } // and process EOD orderbook only if asset is currently not tradable if (!isDayOff && isEodOrderbook) { _log.WriteWarning("EOD FX quotes processing", "", $"EOD FX quote for {orderBookMessage.AssetPairId} is skipped, because instrument is within trading hours"); return(Task.CompletedTask); } var bidAskPair = CreatePair(orderBookMessage); if (bidAskPair == null) { return(Task.CompletedTask); } SetQuote(bidAskPair); _fxBestPriceChangeEventChannel.SendEvent(this, new FxBestPriceChangeEventArgs(bidAskPair)); return(Task.CompletedTask); }
private void CommitStopout(MarginTradingAccount account) { var activeOrders = _ordersCache.ActiveOrders.GetOrdersByAccountIds(account.Id); var ordersToClose = new List <Order>(); var newAccountUsedMargin = account.GetUsedMargin(); foreach (var order in activeOrders.OrderBy(o => o.GetTotalFpl())) { if (newAccountUsedMargin <= 0 || account.GetTotalCapital() / newAccountUsedMargin > account.GetMarginCallLevel()) { break; } ordersToClose.Add(order); newAccountUsedMargin -= order.GetMarginMaintenance(); } if (!ordersToClose.Any()) { return; } _stopoutEventChannel.SendEvent(this, new StopOutEventArgs(account, ordersToClose.ToArray())); foreach (var order in ordersToClose) { SetOrderToClosingState(order, OrderCloseReason.StopOut); } }
public void ChangeOrderLimits(string orderId, decimal?stopLoss, decimal?takeProfit, decimal?expectedOpenPrice) { using (_contextFactory.GetWriteSyncContext($"{nameof(TradingEngine)}.{nameof(ChangeOrderLimits)}")) { var order = _ordersCache.GetOrderById(orderId); if (order.Status != OrderStatus.WaitingForExecution && expectedOpenPrice > 0) { return; } var quote = _quoteCashService.GetQuote(order.Instrument); var tp = takeProfit == 0 ? null : takeProfit; var sl = stopLoss == 0 ? null : stopLoss; var expOpenPrice = expectedOpenPrice == 0 ? null : expectedOpenPrice; var accountAsset = _accountAssetsCacheService.GetAccountAsset(order.TradingConditionId, order.AccountAssetId, order.Instrument); _validateOrderService.ValidateOrderStops(order.GetOrderType(), quote, accountAsset.DeltaBid, accountAsset.DeltaAsk, tp, sl, expOpenPrice, order.AssetAccuracy); order.TakeProfit = tp.HasValue ? Math.Round(tp.Value, order.AssetAccuracy) : (decimal?)null; order.StopLoss = sl.HasValue ? Math.Round(sl.Value, order.AssetAccuracy) : (decimal?)null; order.ExpectedOpenPrice = expOpenPrice.HasValue ? Math.Round(expOpenPrice.Value, order.AssetAccuracy) : (decimal?)null; _orderLimitsChangesEventChannel.SendEvent(this, new OrderLimitsChangedEventArgs(order)); } }
public async Task Is_Swaps_Correct() { _bestPriceConsumer.SendEvent(this, new BestPriceChangeEventArgs(new InstrumentBidAskPair { Instrument = "EURUSD", Bid = 1.02M, Ask = 1.04M })); _accountAssetsRepository.AddOrReplaceAsync(new AccountAssetPair { TradingConditionId = MarginTradingTestsUtils.TradingConditionId, BaseAssetId = "USD", Instrument = "EURUSD", LeverageInit = 100, LeverageMaintenance = 150, SwapLong = 100, SwapShort = 100 }).Wait(); await _accountAssetsManager.UpdateAccountAssetsCache(); var dayOrder = new Order { AccountAssetId = "USD", Instrument = "EURUSD", Volume = 20, OpenDate = new DateTime(2017, 01, 01, 20, 50, 0), CloseDate = new DateTime(2017, 01, 02, 20, 50, 0), MatchedOrders = new MatchedOrderCollection(new List <MatchedOrder> { new MatchedOrder() { Volume = 20 } }), SwapCommission = 100 }; var swapsForDay = _swapService.GetSwaps(dayOrder); var twoDayOrder = new Order { AccountAssetId = "USD", Instrument = "EURUSD", Volume = 20, OpenDate = new DateTime(2017, 01, 01, 20, 50, 0), CloseDate = new DateTime(2017, 01, 03, 20, 50, 0), MatchedOrders = new MatchedOrderCollection(new List <MatchedOrder>() { new MatchedOrder() { Volume = 20 } }), SwapCommission = 100 }; var swapsFor2Days = _swapService.GetSwaps(twoDayOrder); Assert.AreEqual(5.69863014m, swapsForDay); Assert.AreEqual(11.39726027m, swapsFor2Days); }
private void ProduceBestPrice(string instrument) { var bestPrice = _orderBooks.GetBestPrice(instrument); if (bestPrice != null) { _bestPriceChangeEventChannel.SendEvent(this, new BestPriceChangeEventArgs(bestPrice)); } }
private void NotifyAccountLevelChanged(MarginTradingAccount account, AccountLevel newAccountLevel) { switch (newAccountLevel) { case AccountLevel.MarginCall: _marginCallEventChannel.SendEvent(this, new MarginCallEventArgs(account)); break; } }
public async Task Is_Swaps_Correct() { _bestPriceConsumer.SendEvent(this, new BestPriceChangeEventArgs(new InstrumentBidAskPair { Instrument = "EURUSD", Bid = 1.02M, Ask = 1.04M })); var instrumentContract = new TradingInstrumentContract { TradingConditionId = MarginTradingTestsUtils.TradingConditionId, Instrument = "EURUSD", LeverageInit = 100, LeverageMaintenance = 150, SwapLong = 100, SwapShort = 100 }; Mock.Get(_tradingInstruments).Setup(s => s.List(It.IsAny <string>())) .ReturnsAsync(new List <TradingInstrumentContract> { instrumentContract }); await _accountAssetsManager.UpdateTradingInstrumentsCacheAsync(); var dayPosition = new Position(Guid.NewGuid().ToString("N"), 0, "EURUSD", 20, Accounts[0].Id, MarginTradingTestsUtils.TradingConditionId, Accounts[0].BaseAssetId, null, MatchingEngineConstants.DefaultMm, new DateTime(2017, 01, 01, 20, 50, 0), "OpenTrade", OrderType.Market, 20, 1, 1, "USD", 1, new List <RelatedOrderInfo>(), "LYKKETEST", OriginatorType.Investor, "", "EURUSD", FxToAssetPairDirection.Straight, ""); dayPosition.SetCommissionRates(100, 0, 0, 1); dayPosition.StartClosing(new DateTime(2017, 01, 02, 20, 50, 0), PositionCloseReason.Close, OriginatorType.Investor, ""); dayPosition.Close(new DateTime(2017, 01, 02, 20, 50, 0), MatchingEngineConstants.DefaultMm, 2, 1, 1, OriginatorType.Investor, PositionCloseReason.Close, "", "CloseTrade"); var swapsForDay = _swapService.GetSwaps(dayPosition); var twoDayPosition = new Position(Guid.NewGuid().ToString("N"), 0, "EURUSD", 20, Accounts[0].Id, MarginTradingTestsUtils.TradingConditionId, Accounts[0].BaseAssetId, null, MatchingEngineConstants.DefaultMm, new DateTime(2017, 01, 01, 20, 50, 0), "OpenTrade", OrderType.Market, 20, 1, 1, "USD", 1, new List <RelatedOrderInfo>(), "LYKKETEST", OriginatorType.Investor, "", "EURUSD", FxToAssetPairDirection.Straight, ""); twoDayPosition.SetCommissionRates(100, 0, 0, 1); twoDayPosition.StartClosing(new DateTime(2017, 01, 03, 20, 50, 0), PositionCloseReason.Close, OriginatorType.Investor, ""); twoDayPosition.Close(new DateTime(2017, 01, 03, 20, 50, 0), MatchingEngineConstants.DefaultMm, 2, 1, 1, OriginatorType.Investor, PositionCloseReason.Close, "", "CloseTrade"); var swapsFor2Days = _swapService.GetSwaps(twoDayPosition); Assert.AreEqual(5.69863014m, swapsForDay); Assert.AreEqual(11.39726027m, swapsFor2Days); }
private void CancelWaitingForExecutionOrder(Order order, OrderCloseReason reason) { order.Status = OrderStatus.Closed; order.CloseDate = DateTime.UtcNow; order.CloseReason = reason; _ordersCache.WaitingForExecutionOrders.Remove(order); _orderCancelledEventChannel.SendEvent(this, new OrderCancelledEventArgs(order)); }
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)); }
private void PlacePendingOrder(Order order) { var me = _meRouter.GetMatchingEngineForOpen(order); order.MatchingEngineMode = me.Mode; using (_contextFactory.GetWriteSyncContext($"{nameof(TradingEngine)}.{nameof(PlacePendingOrder)}")) _ordersCache.WaitingForExecutionOrders.Add(order); _orderPlacedEventChannel.SendEvent(this, new OrderPlacedEventArgs(order)); }
public void Is_Volume_Ivalid(decimal volume, bool isValid) { const string instrument = "BTCUSD"; var quote = new InstrumentBidAskPair { Instrument = instrument, Bid = 1.55M, Ask = 1.57M }; _bestPriceConsumer.SendEvent(this, new BestPriceChangeEventArgs(quote)); var request = new OrderPlaceRequest { AccountId = Accounts[0].Id, Direction = OrderDirectionContract.Buy, InstrumentId = instrument, Type = OrderTypeContract.Market, Volume = volume }; if (isValid) { Assert.DoesNotThrow( () => { var order = _validateOrderService.ValidateRequestAndCreateOrders(request).Result.order; _validateOrderService.MakePreTradeValidation(order, true, _me, 0); }); } else { var ex = Assert.ThrowsAsync <ValidateOrderException>( async() => { var order = (await _validateOrderService.ValidateRequestAndCreateOrders(request)).order; _validateOrderService.MakePreTradeValidation(order, true, _me, 0); }); Assert.That(ex.RejectReason == (volume == 0 ? OrderRejectReason.InvalidVolume : OrderRejectReason.MaxOrderSizeLimit)); } }
private void CancelRelatedOrders(List <RelatedOrderInfo> relatedOrderInfos, OrderCancellationReason reason) { var metadata = new OrderCancelledMetadata { Reason = reason.ToType <OrderCancellationReasonContract>() }; foreach (var relatedOrderInfo in relatedOrderInfos) { if (_ordersCache.Inactive.TryPopById(relatedOrderInfo.Id, out var inactiveRelatedOrder)) { inactiveRelatedOrder.Cancel(_dateService.Now(), null); _orderCancelledEventChannel.SendEvent(this, new OrderCancelledEventArgs(inactiveRelatedOrder, metadata)); } else if (_ordersCache.Active.TryPopById(relatedOrderInfo.Id, out var activeRelatedOrder)) { activeRelatedOrder.Cancel(_dateService.Now(), null); _orderCancelledEventChannel.SendEvent(this, new OrderCancelledEventArgs(activeRelatedOrder, metadata)); } } }
private void ActivateRelatedOrders(Position position) { foreach (var relatedOrderInfo in position.RelatedOrders) { if (_ordersCache.Inactive.TryPopById(relatedOrderInfo.Id, out var relatedOrder)) { relatedOrder.Activate(_dateService.Now(), true, position.ClosePrice); _ordersCache.Active.Add(relatedOrder); _orderActivatedEventChannel.SendEvent(this, new OrderActivatedEventArgs(relatedOrder)); } } }
private Task <Order> CloseActiveOrderByMatchingEngineAsync(Order order, IMatchingEngineBase matchingEngine, OrderCloseReason reason, string comment) { order.CloseOrderbookId = matchingEngine.Id; order.StartClosingDate = DateTime.UtcNow; order.CloseReason = reason; order.Comment = comment; matchingEngine.MatchMarketOrderForClose(order, matchedOrders => { if (!matchedOrders.Any()) { order.CloseRejectReasonText = "No orders to match"; return(false); } order.MatchedCloseOrders.AddRange(matchedOrders); _equivalentPricesService.EnrichClosingOrder(order); if (!order.GetIsCloseFullfilled()) { order.Status = OrderStatus.Closing; _ordersCache.ActiveOrders.Remove(order); _ordersCache.ClosingOrders.Add(order); _orderClosingEventChannel.SendEvent(this, new OrderClosingEventArgs(order)); } else { order.Status = OrderStatus.Closed; order.CloseDate = DateTime.UtcNow; _ordersCache.ActiveOrders.Remove(order); _orderClosedEventChannel.SendEvent(this, new OrderClosedEventArgs(order)); } return(true); }); return(Task.FromResult(order)); }
public async Task <string> UpdateBalanceAsync(IMarginTradingAccount account, decimal amount, AccountHistoryType historyType, string comment, string eventSourceId = null, bool changeTransferLimit = false, string auditLog = null) { if (historyType == AccountHistoryType.Deposit && changeTransferLimit) { CheckDepositLimits(account, amount); } if (changeTransferLimit) { CheckTransferLimits(account, amount); } var semaphore = GetSemaphore(account); await semaphore.WaitAsync(); try { var updatedAccount = await _repository.UpdateBalanceAsync(account.ClientId, account.Id, amount, changeTransferLimit); _acountBalanceChangedEventChannel.SendEvent(this, new AccountBalanceChangedEventArgs(updatedAccount)); //todo: move to separate event consumers _accountsCacheService.UpdateBalance(updatedAccount); _clientNotifyService.NotifyAccountUpdated(updatedAccount); var transactionId = Guid.NewGuid().ToString("N"); await _rabbitMqNotifyService.AccountHistory( transactionId, account.Id, account.ClientId, amount, updatedAccount.Balance, updatedAccount.WithdrawTransferLimit, historyType, _equivalentPricesService.GetUsdEquivalent(amount, account.BaseAssetId, account.LegalEntity), comment, eventSourceId, account.LegalEntity, auditLog); return(transactionId); } finally { semaphore.Release(); } }
private void HandleOvernightMarginWarnings() { foreach (var account in _accountsCacheService.GetAll()) { var accountOvernightUsedMargin = _accountUpdateService.CalculateOvernightUsedMargin(account); var accountLevel = account.GetAccountLevel(accountOvernightUsedMargin); if (accountLevel == AccountLevel.StopOut) { _marginCallEventChannel.SendEvent(this, new MarginCallEventArgs(account, AccountLevel.OvernightMarginCall)); } } }
public void SetOrderbook(ExternalOrderBook orderbook) { var isEodOrderbook = orderbook.ExchangeName == ExternalOrderbookService.EodExternalExchange; var instrumentTradingStatus = _assetPairDayOffService.IsAssetTradingDisabled(orderbook.AssetPairId); if (!isEodOrderbook && !instrumentTradingStatus.TradingEnabled && instrumentTradingStatus.Reason == InstrumentTradingDisabledReason.InstrumentTradingDisabled) { return; } if (_orderbookValidation.ValidateInstrumentStatusForEodQuotes && isEodOrderbook || _orderbookValidation.ValidateInstrumentStatusForTradingQuotes && !isEodOrderbook) { // we should process normal orderbook only if instrument is currently tradable if (_orderbookValidation.ValidateInstrumentStatusForTradingQuotes && instrumentTradingStatus && !isEodOrderbook) { return; } // and process EOD orderbook only if instrument is currently not tradable if (_orderbookValidation.ValidateInstrumentStatusForEodQuotes && !instrumentTradingStatus && isEodOrderbook) { //log current schedule for the instrument var schedule = _scheduleSettingsCache.GetMarketTradingScheduleByAssetPair(orderbook.AssetPairId); _log.WriteWarning("EOD quotes processing", $"Current schedule for the instrument's market: {schedule.ToJson()}", $"EOD quote for {orderbook.AssetPairId} is skipped, because instrument is within trading hours"); return; } } if (!CheckZeroQuote(orderbook, isEodOrderbook)) { return; } orderbook.ApplyExchangeIdFromSettings(_defaultExternalExchangeId); var bba = orderbook.GetBestPrice(); _orderbooks.AddOrUpdate(orderbook.AssetPairId, a => orderbook, (s, book) => orderbook); _bestPriceChangeEventChannel.SendEvent(this, new BestPriceChangeEventArgs(bba, isEodOrderbook)); }
public void SetOrderbook(ExternalOrderBook orderbook) { if (!ValidateOrderbook(orderbook)) { return; } var bba = new InstrumentBidAskPair { Bid = 0, Ask = decimal.MaxValue, Date = _dateService.Now(), Instrument = orderbook.AssetPairId }; Dictionary <string, ExternalOrderBook> UpdateOrderbooksDictionary(string assetPairId, Dictionary <string, ExternalOrderBook> dict) { dict[orderbook.ExchangeName] = orderbook; foreach (var pair in dict.Values.RequiredNotNullOrEmptyCollection(nameof(dict))) { // guaranteed to be sorted best first var bestBid = pair.Bids.First().Price; var bestAsk = pair.Asks.First().Price; if (bestBid > bba.Bid) { bba.Bid = bestBid; } if (bestAsk < bba.Ask) { bba.Ask = bestAsk; } } return(dict); } _orderbooks.AddOrUpdate(orderbook.AssetPairId, k => UpdateOrderbooksDictionary(k, new Dictionary <string, ExternalOrderBook>()), UpdateOrderbooksDictionary); _bestPriceChangeEventChannel.SendEvent(this, new BestPriceChangeEventArgs(bba)); }
private void CommitStopout(MarginTradingAccount account) { var pendingOrders = _ordersCache.WaitingForExecutionOrders.GetOrdersByAccountIds(account.Id); var cancelledPendingOrders = new List <Order>(); foreach (var pendingOrder in pendingOrders) { cancelledPendingOrders.Add(pendingOrder); CancelPendingOrder(pendingOrder.Id, OrderCloseReason.CanceledBySystem, "Stop out"); } var activeOrders = _ordersCache.ActiveOrders.GetOrdersByAccountIds(account.Id); var ordersToClose = new List <Order>(); var newAccountUsedMargin = account.GetUsedMargin(); foreach (var order in activeOrders.OrderBy(o => o.GetTotalFpl())) { if (newAccountUsedMargin <= 0 || account.GetTotalCapital() / newAccountUsedMargin > account.GetMarginCallLevel()) { break; } ordersToClose.Add(order); newAccountUsedMargin -= order.GetMarginMaintenance(); } if (!ordersToClose.Any() && !cancelledPendingOrders.Any()) { return; } _stopoutEventChannel.SendEvent(this, new StopOutEventArgs(account, ordersToClose.Concat(cancelledPendingOrders).ToArray())); foreach (var order in ordersToClose) { SetOrderToClosingState(order, OrderCloseReason.StopOut); } }
private void ChangeRelatedOrderVolume(List <RelatedOrderInfo> relatedOrderInfos, decimal newVolume) { foreach (var relatedOrderInfo in relatedOrderInfos) { if (_ordersCache.TryGetOrderById(relatedOrderInfo.Id, out var relatedOrder) && relatedOrder.Volume != newVolume) { var oldVolume = relatedOrder.Volume; relatedOrder.ChangeVolume(newVolume, _dateService.Now(), OriginatorType.System); var metadata = new OrderChangedMetadata { UpdatedProperty = OrderChangedProperty.Volume, OldValue = oldVolume.ToString("F2") }; _orderChangedEventChannel.SendEvent(this, new OrderChangedEventArgs(relatedOrder, metadata)); } } }
private void RemoveRelatedOrderFromParent(Order order) { if (!string.IsNullOrEmpty(order.ParentOrderId) && _ordersCache.TryGetOrderById(order.ParentOrderId, out var parentOrder)) { var metadata = new OrderChangedMetadata { UpdatedProperty = OrderChangedProperty.RelatedOrderRemoved, OldValue = order.Id }; parentOrder.RemoveRelatedOrder(order.Id); _orderChangedEventChannel.SendEvent(this, new OrderChangedEventArgs(parentOrder, metadata)); } if (!string.IsNullOrEmpty(order.ParentPositionId) && _ordersCache.Positions.TryGetPositionById(order.ParentPositionId, out var parentPosition)) { parentPosition.RemoveRelatedOrder(order.Id); } }
public void SetOrderbook(ExternalOrderBook orderbook) { if (!CheckZeroQuote(orderbook)) { return; } var isDayOff = _assetPairDayOffService.IsDayOff(orderbook.AssetPairId); var isEodOrderbook = orderbook.ExchangeName == ExternalOrderbookService.EodExternalExchange; // we should process normal orderbook only if instrument is currently tradable if (isDayOff && !isEodOrderbook) { return; } // and process EOD orderbook only if instrument is currently not tradable if (!isDayOff && isEodOrderbook) { //log current schedule for the instrument var schedule = _scheduleSettingsCache.GetCompiledScheduleSettings( orderbook.AssetPairId, _dateService.Now(), TimeSpan.Zero); _log.WriteWarning("EOD quotes processing", $"Current schedule: {schedule.ToJson()}", $"EOD quote for {orderbook.AssetPairId} is skipped, because instrument is within trading hours"); return; } orderbook.ApplyExchangeIdFromSettings(_defaultExternalExchangeId); var bba = orderbook.GetBestPrice(); _orderbooks.AddOrUpdate(orderbook.AssetPairId, a => orderbook, (s, book) => orderbook); _bestPriceChangeEventChannel.SendEvent(this, new BestPriceChangeEventArgs(bba)); }
private void CommitStopOut(MarginTradingAccount account, InstrumentBidAskPair quote) { if (account.IsInLiquidation()) { return; } var liquidationType = account.GetUsedMargin() == account.GetCurrentlyUsedMargin() ? LiquidationType.Normal : LiquidationType.Mco; _cqrsSender.SendCommandToSelf(new StartLiquidationInternalCommand { OperationId = _identityGenerator.GenerateGuid(),//TODO: use quote correlationId AccountId = account.Id, CreationTime = _dateService.Now(), QuoteInfo = quote?.ToJson(), LiquidationType = liquidationType, OriginatorType = OriginatorType.System, }); _stopOutEventChannel.SendEvent(this, new StopOutEventArgs(account)); }