示例#1
0
 public TradeEventProcessor(
     TradingOrderService tradingOrderService,
     EventHistoryService eventHistoryService,
     UserService userService,
     ILogger <TradeEventProcessor> logger)
 {
     _tradingOrderService = tradingOrderService;
     _eventHistoryService = eventHistoryService;
     _userService         = userService;
     _logger = logger;
 }
示例#2
0
 /// <summary>
 /// Created via ProcessorFactory.
 /// </summary>
 public TradeCommandProcessor(
     VersionControl versionControl,
     TradingOrderService tradingOrderService,
     UserService userService,
     EventHistoryService eventHistoryService,
     ILogger <TradeCommandProcessor> logger)
 {
     _logger             = logger;
     VersionControl      = versionControl;
     TradingOrderService = tradingOrderService;
     UserService         = userService;
     EventHistoryService = eventHistoryService;
 }
示例#3
0
 public ProcessorFactory(
     VersionControl versionControl,
     TradingOrderService tradingOrderService,
     UserService userService,
     EventHistoryService eventHistoryService,
     ILogger<TradeCommandProcessor> tradeOrderPersistenceProcessorLogger)
 {
     _tradeOrderPersistenceProcessorLogger = tradeOrderPersistenceProcessorLogger;
     VersionControl = versionControl;
     TradingOrderService = tradingOrderService;
     UserService = userService;
     EventHistoryService = eventHistoryService;
 }
示例#4
0
        private (List <MatchOrderEventEntry>, decimal) PlanMatchOrdersLocked(
            string user, string accountId, string instrument, OrderSide orderSide, decimal?limitPriceValue,
            decimal quantityRemaining, long lockedEventVersionNumber, string requestId,
            Func <string, Exception> reportInvalidMessage)
        {
            var plannedEvents = new List <MatchOrderEventEntry>();
            var baseCurrency  = instrument.Split("_")[0];
            var quoteCurrency = instrument.Split("_")[1];

            // Start the process of matching relevant offers
            var matchingOffers = orderSide == OrderSide.Buy
                ? TradingOrderService.MatchSellers(limitPriceValue, instrument).Result
                : TradingOrderService.MatchBuyers(limitPriceValue, instrument).Result;

            matchingOffers.MoveNext();
            var matchingOfferBatch = matchingOffers.Current.ToList();

            while (quantityRemaining > 0 && matchingOfferBatch.Count > 0)
            {
                _logger.LogInformation(
                    $"Request {requestId} matched a batch of {matchingOfferBatch.Count} {(orderSide == OrderSide.Buy ? "buyers" : "sellers")}");
                foreach (var other in matchingOfferBatch)
                {
                    var     otherRemaining = other.Qty - other.FilledQty;
                    decimal matchedQuantity;
                    if (otherRemaining >= quantityRemaining)
                    {
                        // Entire command order remainder is consumed by the seller offer
                        matchedQuantity = quantityRemaining;
                        _logger.LogInformation(
                            $"New {instrument} {(orderSide == OrderSide.Buy ? "buy" : "sell")} limit order planning entirely matched order id {other.Id}");
                    }
                    else
                    {
                        // Fraction of order will remain, but the seller offer will be consumed
                        matchedQuantity = otherRemaining;
                        _logger.LogInformation(
                            $"New {instrument} {(orderSide == OrderSide.Buy ? "buy" : "sell")} limit order planning partially matched order id {other.Id}");
                    }

                    quantityRemaining -= matchedQuantity;

                    var matchEvent = new MatchOrderEventEntry
                    {
                        VersionNumber              = lockedEventVersionNumber,
                        ActionUser                 = user,
                        ActionAccountId            = accountId,
                        TargetOrderOnVersionNumber = other.CreatedOnVersionId,
                        TargetUser                 = other.User,
                        TargetAccountId            = other.AccountId,
                        Instrument                 = instrument,
                        Qty        = matchedQuantity,
                        ActionSide = orderSide,
                        Price      = other.LimitPrice,
                        ActionOrderQtyRemaining = quantityRemaining,
                        TargetOrderQtyRemaining = other.Qty - other.FilledQty - matchedQuantity,
                    };

                    // Calculating new balances for double-check purposes
                    var(actionBaseMod, actionQuoteMod, targetBaseMod, targetQuoteMod) =
                        TradeEventProcessor.MatchOrderBalanceModifications(matchEvent);
                    try
                    {
                        matchEvent.ActionBaseNewBalance =
                            UserService.GetBalanceAndReservedBalance(
                                matchEvent.ActionUser, matchEvent.ActionAccountId, baseCurrency
                                ).Item1 + actionBaseMod;
                        matchEvent.ActionQuoteNewBalance =
                            UserService.GetBalanceAndReservedBalance(
                                matchEvent.ActionUser, matchEvent.ActionAccountId, quoteCurrency
                                ).Item1 + actionQuoteMod;
                        matchEvent.TargetBaseNewBalance =
                            UserService.GetBalanceAndReservedBalance(
                                matchEvent.TargetUser, matchEvent.TargetAccountId, baseCurrency
                                ).Item1 + targetBaseMod;
                        matchEvent.TargetQuoteNewBalance =
                            UserService.GetBalanceAndReservedBalance(
                                matchEvent.TargetUser, matchEvent.TargetAccountId, quoteCurrency
                                ).Item1 + targetQuoteMod;
                    }
                    catch (Exception e)
                    {
                        // This can happen if a user didn't generate his balances yet, so it's not a fatal error
                        throw reportInvalidMessage(
                                  $"There was a problem with your coin balances. {e.GetType().Name}: {e.Message}");
                    }

                    plannedEvents.Add(matchEvent);
                    if (quantityRemaining == 0)
                    {
                        break;
                    }
                }

                if (quantityRemaining == 0)
                {
                    break;
                }


                // Keep the iteration going in order to find further matching orders as long as remaining qty > 0
                if (!matchingOffers.MoveNext())
                {
                    break;
                }

                matchingOfferBatch = matchingOffers.Current.ToList();
            }

            return(plannedEvents, quantityRemaining);
        }
示例#5
0
        private async Task <IList <EventEntry> > PlanCancelOrder(
            string user, string accountId, long orderCreatedOnVersionNumber, string requestId,
            Func <string, Exception> reportInvalidMessage)
        {
            var plannedEvents = new List <EventEntry>();

            VersionControl.ExecuteUsingFixedVersion(currentVersionNumber =>
            {
                var eventVersionNumber = currentVersionNumber + 1;
                object openOrder;
                try
                {
                    openOrder = TradingOrderService.FindOpenOrderCreatedByVersionNumber(
                        orderCreatedOnVersionNumber
                        );
                }
                catch (InvalidOperationException)
                {
                    openOrder = null;
                }

                if (openOrder is OrderBookEntry limitOrder)
                {
                    if (!user.Equals(limitOrder.User) ||
                        !accountId.Equals(limitOrder.AccountId))
                    {
                        throw reportInvalidMessage("Couldn't cancel limit order, user or accountId differs");
                    }

                    plannedEvents.Add(new CancelOrderEventEntry
                    {
                        VersionNumber = eventVersionNumber,
                        User          = user,
                        AccountId     = accountId,
                        Instrument    = limitOrder.Instrument,
                        CancelOrderCreatedOnVersionNumber = orderCreatedOnVersionNumber,
                    });
                    return;
                }

                if (openOrder is HiddenOrderEntry stopOrder)
                {
                    if (!user.Equals(stopOrder.User) ||
                        !accountId.Equals(stopOrder.AccountId))
                    {
                        throw reportInvalidMessage("Couldn't cancel stop order, user or accountId differs");
                    }

                    plannedEvents.Add(new CancelOrderEventEntry
                    {
                        VersionNumber = eventVersionNumber,
                        User          = user,
                        AccountId     = accountId,
                        Instrument    = stopOrder.Instrument,
                        CancelOrderCreatedOnVersionNumber = orderCreatedOnVersionNumber,
                    });
                    return;
                }

                throw reportInvalidMessage("Couldn't find a matching limit or stop order to close");
            });
            return(plannedEvents);
        }