Пример #1
0
        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);
            }
        }
Пример #2
0
        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);
            }
        }
Пример #3
0
        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;
            }
        }
        /// <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);
        }
Пример #5
0
        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);
        }
Пример #6
0
        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);
        }
Пример #7
0
        public void OrderbookRemoveTest(bool isOrderedByPrice)
        {
            var orders = new List <Order>();
            var random = new Random();
            var price  = 1D;
            var side   = OrderSide.Buy;
            var asset  = 1;

            var orderbook   = Global.Exchange.GetOrderbook(asset, side);
            var ordersCount = 1000;

            for (var i = 1; i <= ordersCount; i++)
            {
                if (isOrderedByPrice)
                {
                    price = price * 1.01;
                }
                else
                {
                    price = 1 + random.NextDouble();
                }
                var orderId = OrderIdConverter.Encode((ulong)i, asset, side);
                var amount  = 1000;
                var order   = new Order {
                    OrderId = orderId, Amount = 1000, Price = price, QuoteAmount = OrderMatcher.EstimateQuoteAmount(amount, price, side)
                };
                orders.Add(order);
                orderbook.InsertOrder(order);
            }

            Func <int> getOrdersCount = () =>
            {
                var ordersCounter = 0;
                foreach (var o in orderbook)
                {
                    ordersCounter++;
                }
                return(ordersCounter);
            };

            var count = getOrdersCount();

            Assert.AreEqual(orderbook.Count, count, "Orderbook.Count and order-book items count are not equal.");
            Assert.AreEqual(ordersCount, count);

            foreach (var order in orders)
            {
                orderbook.RemoveOrder(order.OrderId);
                ordersCount--;
                Assert.AreEqual(ordersCount, orderbook.Count);
                Assert.AreEqual(ordersCount, getOrdersCount());
            }
            Assert.IsNull(orderbook.Head);
            Assert.IsNull(orderbook.Tail);
        }
Пример #8
0
        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;
            }
        }
Пример #10
0
 /// <summary>
 /// Sends envelope and all effects to specified callback
 /// </summary>
 public void Complete()
 {
     //TODO: find more elegant way to handle this scenario
     //we mustn't save orders that were closed immediately without adding to order-book
     if (Quantum is RequestQuantum request &&
         request.RequestMessage is OrderRequest orderRequest &&
         !OrderWasPlaced)
     {
         PendingDiffObject.Orders.Remove(OrderIdConverter.FromRequest(orderRequest, Quantum.Apex));
     }
     QuantumModel.Complete(QuantumModelExtensions.FromQuantum(Envelope));
     PendingDiffObject.Quanta.Add(QuantumModel);
 }
        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));
        }
Пример #12
0
        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);
            }
        }
Пример #13
0
        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
            });
        }
Пример #14
0
        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);
        }
Пример #15
0
        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);
        }
Пример #16
0
        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 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);
        }
Пример #18
0
        /// <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);
                }
            }
        }
Пример #19
0
        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));
        }
Пример #21
0
        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;
            }
        }
Пример #22
0
        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);
        }
Пример #23
0
        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);
                }
            }
        }
        public async Task OrderCancellationQuantumTest(int asset, int amount, OrderSide side, int apexMod, bool useFakeSigner, Type excpectedException)
        {
            var acc = context.AccountStorage.GetAccount(TestEnvironment.Client1KeyPair);

            var order = new OrderRequest
            {
                Account        = acc.Account.Id,
                RequestId      = 1,
                Amount         = amount,
                Asset          = asset,
                Price          = 100,
                Side           = side,
                AccountWrapper = acc
            };

            var envelope = order.CreateEnvelope().Sign(useFakeSigner ? TestEnvironment.Client2KeyPair : TestEnvironment.Client1KeyPair);

            if (!context.IsAlpha)
            {
                var quantum = new RequestQuantum {
                    Apex = context.QuantumStorage.CurrentApex + 1, RequestEnvelope = envelope, Timestamp = DateTime.UtcNow.Ticks
                };
                envelope = quantum.CreateEnvelope().Sign(TestEnvironment.AlphaKeyPair);
            }

            var submitResult = await AssertQuantumHandling(envelope, excpectedException);

            if (excpectedException != null)
            {
                return;
            }

            var apex = ((Quantum)submitResult.OriginalMessage.Message).Apex + apexMod;

            var orderCancellation = new OrderCancellationRequest
            {
                Account        = acc.Account.Id,
                RequestId      = 2,
                OrderId        = OrderIdConverter.Encode((ulong)apex, asset, side),
                AccountWrapper = context.AccountStorage.GetAccount(TestEnvironment.Client1KeyPair)
            };

            envelope = orderCancellation.CreateEnvelope().Sign(TestEnvironment.Client1KeyPair);

            if (!context.IsAlpha)
            {
                var quantum = new RequestQuantum {
                    Apex = context.QuantumStorage.CurrentApex + 1, RequestEnvelope = envelope, Timestamp = DateTime.UtcNow.Ticks
                };
                envelope = quantum.CreateEnvelope().Sign(TestEnvironment.AlphaKeyPair);
            }

            var cancelResult = await AssertQuantumHandling(envelope, excpectedException);

            if (excpectedException != null)
            {
                return;
            }
            var currentMarket = context.Exchange.GetMarket(asset);

            Assert.IsTrue(currentMarket != null);

            var requests = side == OrderSide.Buy ? currentMarket.Bids : currentMarket.Asks;

            Assert.AreEqual(requests.Count, 0);
        }