public DatabaseGenerator( VersionControl versionControl, EventHistoryService eventHistoryService, TradingRepository tradingRepository, AccountRepository accountRepository, TradeEventProcessor tradeEventProcessor, ILogger <DatabaseGenerator> logger) { _versionControl = versionControl; _eventHistoryService = eventHistoryService; _tradingRepository = tradingRepository; _accountRepository = accountRepository; _tradeEventProcessor = tradeEventProcessor; _logger = logger; }
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); }