/// <summary> /// Fetch current account state from the constellation. /// </summary> /// <returns>Current account state</returns> public async Task <AccountState> GetAccountData() { var result = await connection.SendMessage(new AccountDataRequest().CreateEnvelope()); var adr = result.Result.Message as AccountDataResponse; foreach (var balance in adr.Balances) { AccountState.balances[balance.Asset] = new BalanceModel { AssetId = balance.Asset, Amount = balance.Amount, Liabilities = balance.Liabilities }; } foreach (var order in adr.Orders) { var decodedOrderId = OrderIdConverter.Decode(order.OrderId); AccountState.orders[order.OrderId] = new OrderModel { OrderId = order.OrderId, Price = order.Price, Amount = order.Amount, AssetId = decodedOrderId.Asset, Side = decodedOrderId.Side }; } return(AccountState); }
public override void RevertEffect() { MarkAsProcessed(); var decodedId = OrderIdConverter.Decode(Effect.OrderId); if (decodedId.Side == OrderSide.Buy) { Effect.AccountWrapper.Account.GetBalance(0).UpdateLiabilities(Effect.QuoteAmount); } else { Effect.AccountWrapper.Account.GetBalance(decodedId.Asset).UpdateLiabilities(Effect.Amount); } var order = new Order { OrderId = Effect.OrderId, Amount = Effect.Amount, QuoteAmount = Effect.QuoteAmount, Price = Effect.Price, AccountWrapper = Effect.AccountWrapper }; orderbook.InsertOrder(order); }
public override void CommitEffect() { MarkAsProcessed(); order.Amount += -Effect.AssetAmount; if (!Effect.IsNewOrder) //new order doesn't have this value yet { order.QuoteAmount += -Effect.QuoteAmount; } var decodedId = OrderIdConverter.Decode(Effect.OrderId); var quoteBalance = Effect.AccountWrapper.Account.GetBalance(0); var assetBalance = Effect.AccountWrapper.Account.GetBalance(decodedId.Asset); if (decodedId.Side == OrderSide.Buy) { if (!Effect.IsNewOrder) //liabilities wasn't locked for new order yet { quoteBalance.UpdateLiabilities(-Effect.QuoteAmount); } quoteBalance.UpdateBalance(-Effect.QuoteAmount); assetBalance.UpdateBalance(Effect.AssetAmount); } else { if (!Effect.IsNewOrder) //liabilities wasn't locked for new order yet { assetBalance.UpdateLiabilities(-Effect.AssetAmount); } assetBalance.UpdateBalance(-Effect.AssetAmount); quoteBalance.UpdateBalance(Effect.QuoteAmount); } }
public override void RevertEffect() { MarkAsProcessed(); var decodedId = OrderIdConverter.Decode(Effect.OrderId); var quoteBalance = Effect.AccountWrapper.Account.GetBalance(0); var assetBalance = Effect.AccountWrapper.Account.GetBalance(decodedId.Asset); if (decodedId.Side == OrderSide.Buy) { if (!Effect.IsNewOrder) { quoteBalance.UpdateLiabilities(Effect.QuoteAmount); } quoteBalance.UpdateBalance(Effect.QuoteAmount); assetBalance.UpdateBalance(-Effect.AssetAmount); } else { if (!Effect.IsNewOrder) { assetBalance.UpdateLiabilities(Effect.AssetAmount); } assetBalance.UpdateBalance(Effect.AssetAmount); quoteBalance.UpdateBalance(-Effect.QuoteAmount); } order.Amount += Effect.AssetAmount; if (!Effect.IsNewOrder) { order.QuoteAmount += Effect.QuoteAmount; } }
public OrderMatcher(OrderRequest orderRequest, EffectProcessorsContainer effectsContainer) { resultEffects = effectsContainer; takerOrder = new Order { OrderId = OrderIdConverter.FromRequest(orderRequest, effectsContainer.Apex), AccountWrapper = orderRequest.AccountWrapper, Amount = orderRequest.Amount, Price = orderRequest.Price }; timeInForce = orderRequest.TimeInForce; //parse data from the ID of the newly arrived order var orderData = OrderIdConverter.Decode(takerOrder.OrderId); asset = orderData.Asset; side = orderData.Side; //get asset orderbook market = effectsContainer.Context.Exchange.GetMarket(asset); orderbook = market.GetOrderbook(side.Inverse()); //fetch balances if (!takerOrder.AccountWrapper.Account.HasBalance(asset)) { resultEffects.AddBalanceCreate(orderRequest.AccountWrapper, asset); } }
public void OrderIdConverterSimpleTest(ulong apex, int market, OrderSide side) { var decoded = OrderIdConverter.Decode(OrderIdConverter.Encode(apex, market, side)); Assert.AreEqual(apex, decoded.Apex); Assert.AreEqual(market, decoded.Asset); Assert.AreEqual(side, decoded.Side); }
public override Task Validate(OrderCancellationProcessorContext context) { context.ValidateNonce(); var quantum = context.Envelope.Message as RequestQuantum; var orderRequest = (OrderCancellationRequest)quantum.RequestMessage; var orderData = OrderIdConverter.Decode(orderRequest.OrderId); if (!Global.Exchange.HasMarket(orderData.Asset)) { throw new BadRequestException("Asset is not supported."); } context.Orderbook = Global.Exchange.GetOrderbook(orderData.Asset, orderData.Side); context.Order = context.Orderbook.GetOrder(orderRequest.OrderId); if (context.Order is null) { throw new BadRequestException($"Order {orderRequest.OrderId} is not found.{(quantum.Apex != default ? $" Apex {quantum.Apex}" : "")}"); } //TODO: check that lot size is greater than minimum allowed lot if (context.Order.AccountWrapper.Account.Id != orderRequest.Account) { throw new ForbiddenException(); } if (context.OrderSide == OrderSide.Buy) { var balance = orderRequest.AccountWrapper.Account.GetBalance(0); if (balance.Liabilities < context.Order.QuoteAmount) { throw new BadRequestException("Quote liabilities is less than order size."); } } else { var balance = orderRequest.AccountWrapper.Account.GetBalance(orderData.Asset); if (balance == null) { throw new BadRequestException("Balance for asset not found."); } if (balance.Liabilities < context.Order.Amount) { throw new BadRequestException("Asset liabilities is less than order size."); } } return(Task.CompletedTask); }
private static void UpdateLiabilities(Dictionary <int, DiffObject.Account> accounts, Dictionary <BsonObjectId, DiffObject.Balance> balances, int accountId, ulong orderId, long quoteAmount, long assetAmount) { var decodedId = OrderIdConverter.Decode(orderId); if (decodedId.Side == OrderSide.Buy) { GetBalance(balances, BalanceModelIdConverter.EncodeId(accountId, 0)).LiabilitiesDiff += quoteAmount; } else { GetBalance(balances, BalanceModelIdConverter.EncodeId(accountId, decodedId.Asset)).LiabilitiesDiff += assetAmount; } }
public override void RevertEffect() { MarkAsProcessed(); orderBook.RemoveOrder(Effect.OrderId); var decodedId = OrderIdConverter.Decode(order.OrderId); if (decodedId.Side == OrderSide.Buy) { order.AccountWrapper.Account.GetBalance(0).UpdateLiabilities(-order.QuoteAmount); } else { order.AccountWrapper.Account.GetBalance(decodedId.Asset).UpdateLiabilities(-order.Amount); } }
public static void AddOrderPlaced(this EffectProcessorsContainer effectProcessors, Orderbook orderBook, Order order) { var decodedOrderId = OrderIdConverter.Decode(order.OrderId); var effect = new OrderPlacedEffect { Apex = effectProcessors.Apex, AccountWrapper = order.AccountWrapper, Asset = decodedOrderId.Asset, Amount = order.Amount, QuoteAmount = order.QuoteAmount, Price = order.Price, OrderId = order.OrderId, OrderSide = decodedOrderId.Side }; effectProcessors.Add(new OrderPlacedEffectProcessor(effect, orderBook, order)); }
public static Exchange RestoreExchange(List <AssetSettings> assets, List <Order> orders, bool observeTrades, bool useLegacyOrderbook = false) { var exchange = new Exchange(observeTrades); foreach (var asset in assets) { exchange.AddMarket(asset, useLegacyOrderbook); } foreach (var order in orders) { var orderData = OrderIdConverter.Decode(order.OrderId); var market = exchange.GetMarket(orderData.Asset); var orderbook = market.GetOrderbook(orderData.Side); orderbook.InsertOrder(order); } return(exchange); }
public static OrderInfo ToOrderInfo(this Order order) { if (order == null) { throw new ArgumentNullException(nameof(order)); } var decodedId = OrderIdConverter.Decode(order.OrderId); return(new OrderInfo { OrderId = order.OrderId, Amount = order.Amount, QuoteAmount = order.QuoteAmount, Market = decodedId.Asset, Price = order.Price, Side = decodedId.Side }); }
public override void CommitEffect() { MarkAsProcessed(); if (!orderbook.RemoveOrder(Effect.OrderId, out _)) { throw new Exception($"Unable to remove order with id {Effect.OrderId}"); } var decodedId = OrderIdConverter.Decode(Effect.OrderId); if (decodedId.Side == OrderSide.Buy) { Effect.AccountWrapper.Account.GetBalance(0).UpdateLiabilities(-Effect.QuoteAmount); } else { Effect.AccountWrapper.Account.GetBalance(decodedId.Asset).UpdateLiabilities(-Effect.Amount); } }
public override void CommitEffect() { MarkAsProcessed(); //lock order reserve var decodedId = OrderIdConverter.Decode(order.OrderId); if (decodedId.Side == OrderSide.Buy) { order.AccountWrapper.Account.GetBalance(0).UpdateLiabilities(order.QuoteAmount); } else { order.AccountWrapper.Account.GetBalance(decodedId.Asset).UpdateLiabilities(order.Amount); } //add order to the orderbook orderBook.InsertOrder(order); }
public static AnalyticsExchange RestoreExchange(List <int> assets, List <OrderInfo> orders) { var exchange = new AnalyticsExchange(); foreach (var asset in assets) { exchange.AddMarket(asset); } foreach (var order in orders) { var orderData = OrderIdConverter.Decode(order.OrderId); var market = exchange.GetMarket(orderData.Asset); var orderbook = market.GetOrderbook(orderData.Side); orderbook.InsertOrder(new OrderInfoWrapper { Order = order }); } return(exchange); }
/// <summary> /// </summary> /// <param name="currentOrderId">If equal to default, first order will be returned.</param> /// <returns>Next order</returns> public OrderInfo GetNextOrder(ulong currentOrderId) { lock (this) { var currentDecodedOrderId = OrderIdConverter.Decode(currentOrderId); var orderWrapper = currentOrderId > 0 ? GetOrder(currentOrderId)?.Next : map.Values.FirstOrDefault(); while (true) { if (orderWrapper == null) { return(null); } var decodedOrderId = OrderIdConverter.Decode(orderWrapper.Order.OrderId); if (decodedOrderId.Side != currentDecodedOrderId.Side) { orderWrapper = orderWrapper.Next; continue; } return(orderWrapper.Order); } } }
internal void ApplyAccountStateChanges(Effect effect) { switch (effect) { case NonceUpdateEffect nonceUpdateEffect: Nonce = nonceUpdateEffect.Nonce; break; case BalanceCreateEffect balanceCreateEffect: var assetId = balanceCreateEffect.Asset; balances.Add(assetId, new BalanceModel { AssetId = assetId }); break; case BalanceUpdateEffect balanceUpdateEffect: UpdateBalance(balanceUpdateEffect.Asset, balanceUpdateEffect.Amount); break; case OrderPlacedEffect orderPlacedEffect: { var orderModel = new OrderModel { Amount = orderPlacedEffect.Amount, Price = orderPlacedEffect.Price, OrderId = orderPlacedEffect.OrderId }; orderModel.Asset = constellationInfo.Assets.FirstOrDefault(a => a.Id == orderModel.AssetId)?.DisplayName ?? orderModel.AssetId.ToString(); orders.Add(orderPlacedEffect.OrderId, orderModel); var decodedId = OrderIdConverter.Decode(orderPlacedEffect.OrderId); if (decodedId.Side == OrderSide.Buy) { UpdateLiabilities(0, orderPlacedEffect.QuoteAmount); } else { UpdateLiabilities(decodedId.Asset, orderPlacedEffect.Amount); } } break; case OrderRemovedEffect orderRemoveEffect: { orders.Remove(orderRemoveEffect.OrderId); var decodedId = OrderIdConverter.Decode(orderRemoveEffect.OrderId); if (decodedId.Side == OrderSide.Buy) { UpdateLiabilities(0, -orderRemoveEffect.QuoteAmount); } else { UpdateLiabilities(decodedId.Asset, -orderRemoveEffect.Amount); } } break; case TradeEffect tradeEffect: { if (orders.TryGetValue(tradeEffect.OrderId, out var order)) //trade could occur without adding the order to orderbook { order.Amount -= tradeEffect.AssetAmount; } var decodedId = OrderIdConverter.Decode(tradeEffect.OrderId); if (decodedId.Side == OrderSide.Buy) { if (!tradeEffect.IsNewOrder) { UpdateLiabilities(0, -tradeEffect.QuoteAmount); } UpdateBalance(0, -tradeEffect.QuoteAmount); UpdateBalance(decodedId.Asset, tradeEffect.AssetAmount); } else { if (!tradeEffect.IsNewOrder) { UpdateLiabilities(decodedId.Asset, -tradeEffect.AssetAmount); } UpdateBalance(decodedId.Asset, -tradeEffect.AssetAmount); UpdateBalance(0, tradeEffect.QuoteAmount); } } break; case WithdrawalCreateEffect withdrawalCreateEffect: foreach (var withdrawalItem in withdrawalCreateEffect.Items) { UpdateLiabilities(withdrawalItem.Asset, withdrawalItem.Amount); } break; case WithdrawalRemoveEffect withdrawalRemoveEffect: foreach (var withdrawalItem in withdrawalRemoveEffect.Items) { if (withdrawalRemoveEffect.IsSuccessful) { UpdateBalance(withdrawalItem.Asset, -withdrawalItem.Amount); } UpdateLiabilities(withdrawalItem.Asset, -withdrawalItem.Amount); } break; default: break; } }
public AnalyticsOrderbook GetOrderbook(ulong offerId) { var parts = OrderIdConverter.Decode(offerId); return(GetOrderbook(parts.Asset, parts.Side)); }
public static void Aggregate(this EffectProcessorsContainer processorsContainer, MessageEnvelope quantumEnvelope, Effect quatumEffect, int effectIndex) { if (processorsContainer == null) { throw new ArgumentNullException(nameof(processorsContainer)); } var pendingDiffObject = processorsContainer.PendingDiffObject; if (pendingDiffObject == null) { throw new ArgumentNullException(nameof(pendingDiffObject)); } if (quantumEnvelope == null) { throw new ArgumentNullException(nameof(quantumEnvelope)); } if (quatumEffect == null) { throw new ArgumentNullException(nameof(quatumEffect)); } var accountWrapper = quatumEffect.AccountWrapper; var apex = processorsContainer.Apex; processorsContainer.QuantumModel.AddEffect(accountWrapper?.Account.Id ?? 0, quatumEffect.FromEffect(effectIndex)); switch (quatumEffect) { case ConstellationInitEffect constellationInit: pendingDiffObject.ConstellationSettings = GetConstellationSettings(constellationInit); pendingDiffObject.StellarInfoData = new DiffObject.ConstellationState { TxCursor = constellationInit.TxCursor, IsInserted = true }; pendingDiffObject.Assets = GetAssets(constellationInit, null); break; case ConstellationUpdateEffect constellationUpdate: throw new NotImplementedException(); pendingDiffObject.ConstellationSettings = GetConstellationSettings(constellationUpdate); pendingDiffObject.Assets = GetAssets(constellationUpdate, Global.PermanentStorage.LoadAssets(long.MaxValue).Result); break; case AccountCreateEffect accountCreateEffect: { var pubKey = accountCreateEffect.Pubkey; var accId = accountCreateEffect.AccountId; pendingDiffObject.Accounts.Add(accId, new DiffObject.Account { PubKey = pubKey, Id = accId, IsInserted = true }); } break; case NonceUpdateEffect nonceUpdateEffect: { var accId = nonceUpdateEffect.AccountWrapper.Account.Id; GetAccount(pendingDiffObject.Accounts, accId).Nonce = nonceUpdateEffect.Nonce; } break; case BalanceCreateEffect balanceCreateEffect: { var accId = balanceCreateEffect.AccountWrapper.Account.Id; var balanceId = BalanceModelIdConverter.EncodeId(accId, balanceCreateEffect.Asset); GetBalance(pendingDiffObject.Balances, balanceId).IsInserted = true; } break; case BalanceUpdateEffect balanceUpdateEffect: { var accId = balanceUpdateEffect.AccountWrapper.Account.Id; var balanceId = BalanceModelIdConverter.EncodeId(accId, balanceUpdateEffect.Asset); GetBalance(pendingDiffObject.Balances, balanceId).AmountDiff += balanceUpdateEffect.Amount; } break; case RequestRateLimitUpdateEffect requestRateLimitUpdateEffect: { var accId = requestRateLimitUpdateEffect.AccountWrapper.Account.Id; GetAccount(pendingDiffObject.Accounts, accId).RequestRateLimits = new RequestRateLimitsModel { HourLimit = requestRateLimitUpdateEffect.RequestRateLimits.HourLimit, MinuteLimit = requestRateLimitUpdateEffect.RequestRateLimits.MinuteLimit }; } break; case OrderPlacedEffect orderPlacedEffect: { var accId = orderPlacedEffect.AccountWrapper.Account.Id; var orderId = orderPlacedEffect.OrderId; pendingDiffObject.Orders[orderId] = new DiffObject.Order { AmountDiff = orderPlacedEffect.Amount, QuoteAmountDiff = orderPlacedEffect.QuoteAmount, IsInserted = true, OrderId = orderId, Price = orderPlacedEffect.Price, Account = accId }; //update liabilities var decodedId = OrderIdConverter.Decode(orderId); if (decodedId.Side == OrderSide.Buy) { GetBalance(pendingDiffObject.Balances, BalanceModelIdConverter.EncodeId(accId, 0)).LiabilitiesDiff += orderPlacedEffect.QuoteAmount; } else { GetBalance(pendingDiffObject.Balances, BalanceModelIdConverter.EncodeId(accId, decodedId.Asset)).LiabilitiesDiff += orderPlacedEffect.Amount; } } break; case OrderRemovedEffect orderRemovedEffect: { var accId = orderRemovedEffect.AccountWrapper.Account.Id; var orderId = orderRemovedEffect.OrderId; GetOrder(pendingDiffObject.Orders, orderId).IsDeleted = true; //update liabilities var decodedId = OrderIdConverter.Decode(orderId); if (decodedId.Side == OrderSide.Buy) { GetBalance(pendingDiffObject.Balances, BalanceModelIdConverter.EncodeId(accId, 0)).LiabilitiesDiff -= orderRemovedEffect.QuoteAmount; } else { GetBalance(pendingDiffObject.Balances, BalanceModelIdConverter.EncodeId(accId, decodedId.Asset)).LiabilitiesDiff -= orderRemovedEffect.Amount; } } break; case TradeEffect tradeEffect: { var order = GetOrder(pendingDiffObject.Orders, tradeEffect.OrderId); order.AmountDiff -= tradeEffect.AssetAmount; order.QuoteAmountDiff -= tradeEffect.QuoteAmount; } break; case TxCursorUpdateEffect cursorUpdateEffect: { if (pendingDiffObject.StellarInfoData == null) { pendingDiffObject.StellarInfoData = new DiffObject.ConstellationState { TxCursor = cursorUpdateEffect.Cursor } } ; else { pendingDiffObject.StellarInfoData.TxCursor = cursorUpdateEffect.Cursor; } } break; case WithdrawalCreateEffect withdrawalCreateEffect: { var accId = withdrawalCreateEffect.AccountWrapper.Account.Id; GetAccount(pendingDiffObject.Accounts, accId).Withdrawal = withdrawalCreateEffect.Apex; foreach (var withdrawalItem in withdrawalCreateEffect.Items) { GetBalance(pendingDiffObject.Balances, BalanceModelIdConverter.EncodeId(accId, withdrawalItem.Asset)).LiabilitiesDiff += withdrawalItem.Amount; } } break; case WithdrawalRemoveEffect withdrawalRemoveEffect: { var accId = withdrawalRemoveEffect.AccountWrapper.Account.Id; GetAccount(pendingDiffObject.Accounts, accId).Withdrawal = 0; foreach (var withdrawalItem in withdrawalRemoveEffect.Items) { var balance = GetBalance(pendingDiffObject.Balances, BalanceModelIdConverter.EncodeId(accId, withdrawalItem.Asset)); if (withdrawalRemoveEffect.IsSuccessful) { balance.AmountDiff -= withdrawalItem.Amount; } balance.LiabilitiesDiff -= withdrawalItem.Amount; } } break; default: break; } }
private void ResultMessageHandler(MessageEnvelope envelope) { lock (processedEffectsMessages) { if (AccountData == null || !(envelope.Message is IEffectsContainer effectsMessage) || processedEffectsMessages.Any(r => r == envelope.Message.MessageId)) { return; } RegisterNewEffectsMessage(envelope.Message.MessageId); try { foreach (var effect in effectsMessage.Effects) { switch (effect) { case NonceUpdateEffect nonceUpdateEffect: AccountData.Nonce = nonceUpdateEffect.Nonce; break; case BalanceCreateEffect balanceCreateEffect: AccountData.AddBalance(balanceCreateEffect.Asset, constellation); break; case BalanceUpdateEffect balanceUpdateEffect: AccountData.UpdateBalance(balanceUpdateEffect.Asset, balanceUpdateEffect.Amount); break; case OrderPlacedEffect orderPlacedEffect: { AccountData.AddOrder(orderPlacedEffect.OrderId, orderPlacedEffect.Amount, orderPlacedEffect.Price, constellation); var decodedId = OrderIdConverter.Decode(orderPlacedEffect.OrderId); if (decodedId.Side == OrderSide.Buy) { AccountData.UpdateLiabilities(0, orderPlacedEffect.QuoteAmount); } else { AccountData.UpdateLiabilities(decodedId.Asset, orderPlacedEffect.Amount); } } break; case OrderRemovedEffect orderRemoveEffect: { AccountData.RemoveOrder(orderRemoveEffect.OrderId); var decodedId = OrderIdConverter.Decode(orderRemoveEffect.OrderId); if (decodedId.Side == OrderSide.Buy) { AccountData.UpdateLiabilities(0, -orderRemoveEffect.QuoteAmount); } else { AccountData.UpdateLiabilities(decodedId.Asset, -orderRemoveEffect.Amount); } } break; case TradeEffect tradeEffect: { AccountData.UpdateOrder(tradeEffect.OrderId, tradeEffect.AssetAmount); var decodedId = OrderIdConverter.Decode(tradeEffect.OrderId); if (decodedId.Side == OrderSide.Buy) { if (!tradeEffect.IsNewOrder) { AccountData.UpdateLiabilities(0, -tradeEffect.QuoteAmount); } AccountData.UpdateBalance(0, -tradeEffect.QuoteAmount); AccountData.UpdateBalance(decodedId.Asset, tradeEffect.AssetAmount); } else { if (!tradeEffect.IsNewOrder) { AccountData.UpdateLiabilities(decodedId.Asset, -tradeEffect.AssetAmount); } AccountData.UpdateBalance(decodedId.Asset, -tradeEffect.AssetAmount); AccountData.UpdateBalance(0, tradeEffect.QuoteAmount); } } break; case WithdrawalCreateEffect withdrawalCreateEffect: foreach (var withdrawalItem in withdrawalCreateEffect.Items) { AccountData.UpdateLiabilities(withdrawalItem.Asset, withdrawalItem.Amount); } break; case WithdrawalRemoveEffect withdrawalRemoveEffect: foreach (var withdrawalItem in withdrawalRemoveEffect.Items) { if (withdrawalRemoveEffect.IsSuccessful) { AccountData.UpdateBalance(withdrawalItem.Asset, -withdrawalItem.Amount); } AccountData.UpdateLiabilities(withdrawalItem.Asset, -withdrawalItem.Amount); } break; default: break; } } OnAccountUpdate?.Invoke(AccountData); } catch (Exception exc) { OnException?.Invoke(exc); } } }
private void ExecuteWithOrderbook(int iterations, bool useNormalDistribution, Action <Action> executor) { var rnd = new Random(); var market = Global.Exchange.GetMarket(1); var testTradeResults = new Dictionary <RequestQuantum, EffectProcessorsContainer>(); for (var i = 1; i < iterations; i++) { var price = useNormalDistribution ? rnd.NextNormallyDistributed() + 50 : rnd.NextDouble() * 100; var request = new OrderRequest { RequestId = i, Amount = rnd.Next(1, 20), Asset = 1, Price = Math.Round(price * 27) / 13 }; if (rnd.NextDouble() >= 0.5) { request.Account = account1.Id; request.AccountWrapper = account1; request.Side = OrderSide.Buy; } else { request.Account = account2.Id; request.AccountWrapper = account2; request.Side = OrderSide.Sell; } var trade = new RequestQuantum { Apex = i, RequestEnvelope = new MessageEnvelope { Message = request, Signatures = new List <Ed25519Signature>() }, Timestamp = DateTime.UtcNow.Ticks }; var diffObject = new DiffObject(); var conterOrderEffectsContainer = new EffectProcessorsContainer(trade.CreateEnvelope(), diffObject); testTradeResults.Add(trade, conterOrderEffectsContainer); } var xlmStartBalance = account1.Account.GetBalance(0).Amount + account2.Account.GetBalance(0).Amount; var assetStartBalance = account1.Account.GetBalance(1).Amount + account2.Account.GetBalance(1).Amount; executor(() => { foreach (var trade in testTradeResults) { Global.Exchange.ExecuteOrder(trade.Value); } }); //cleanup orders foreach (var account in new[] { account1, account2 }) { var activeOrders = Global.Exchange.OrderMap.GetAllAccountOrders(account); foreach (var order in activeOrders) { var decodedOrderId = OrderIdConverter.Decode(order.OrderId); new OrderRemovedEffectProccessor(new OrderRemovedEffect { OrderId = order.OrderId, Amount = order.Amount, QuoteAmount = order.QuoteAmount, Price = order.Price, Asset = decodedOrderId.Asset, AccountWrapper = account }, market.GetOrderbook(decodedOrderId.Side)).CommitEffect(); } } Assert.AreEqual(xlmStartBalance, account1.Account.GetBalance(0).Amount + account2.Account.GetBalance(0).Amount); Assert.AreEqual(assetStartBalance, account1.Account.GetBalance(1).Amount + account2.Account.GetBalance(1).Amount); Assert.AreEqual(0, account1.Account.GetBalance(0).Liabilities); Assert.AreEqual(0, account1.Account.GetBalance(1).Liabilities); Assert.AreEqual(0, account2.Account.GetBalance(0).Liabilities); Assert.AreEqual(0, account2.Account.GetBalance(1).Liabilities); }