private NewAndUpdatedFilledOrders IdentifyRecentNewAndUpdatedOrders(TimeSortedCollection <FilledOrder> liveOrders, DateTime lookAfterTime) { // After the matching process, any unmatched filled orders will be the new filled orders TimeSortedCollection <FilledOrder> unmatchedRecentLiveOrders = new TimeSortedCollection <FilledOrder>( liveOrders.Where(order => order.Time >= lookAfterTime)); IList <UpdatedFilledOrder> updatedFilledOrders = new List <UpdatedFilledOrder>(); // Only retrieve orders from the last "lookbackMinutes" minutes (call it X) for matching. This assumes that: // 1) Order times will not be updated AFTER X minutes // 2) It takes longer than X minutes for every visible order in the live portfolio to be pushed out of view TimeSortedCollection <FilledOrder> unmatchedRecentDbOrders = new TimeSortedCollection <FilledOrder>( GetTodaysFilledOrders().Where(order => order.Time >= lookAfterTime)); // 1st pass: Match with exact time. DO NOT use ToList(), since it creates a copy of each of the items! foreach (FilledOrder dbOrder in unmatchedRecentDbOrders) { FilledOrder?match = unmatchedRecentLiveOrders.FirstOrDefault(o => dbOrder.StrictEquals(o)); if (match != null) { unmatchedRecentLiveOrders.Remove(match); unmatchedRecentDbOrders.Remove(dbOrder); } } // 2nd pass: Match using closest time foreach (FilledOrder dbOrder in unmatchedRecentDbOrders) { FilledOrder?match = unmatchedRecentLiveOrders.Where(o => dbOrder.EqualsIgnoreTime(o) && o.Time > dbOrder.Time).FirstOrDefault(); if (match != null) { unmatchedRecentLiveOrders.Remove(match); UpdatedFilledOrder updated = new UpdatedFilledOrder(dbOrder, match); updatedFilledOrders.Add(updated); Log.Information("Updated order {@OldOrder} to {@NewOrder}", dbOrder, match); } else { PortfolioDatabaseException ex = new PortfolioDatabaseException("No live order matched to database order"); Log.Error(ex, "No live order matched to database order {@Order}- Symbol {Symbol}. Current live orders {@LiveOrders}", dbOrder, dbOrder.Symbol, liveOrders); throw ex; } } TimeSortedCollection <FilledOrder> newOrders = new TimeSortedCollection <FilledOrder>(unmatchedRecentLiveOrders); return(new NewAndUpdatedFilledOrders(newOrders, updatedFilledOrders)); }
public TimeSortedCollection <PositionDelta> ComputeDeltasAndUpdateTables(TimeSortedCollection <FilledOrder> newOrders) { TimeSortedCollection <PositionDelta> deltas = new TimeSortedCollection <PositionDelta>(); foreach (FilledOrder order in newOrders) { Position? oldPos = GetPosition(order.Symbol); PositionDelta delta; if (oldPos == null) // NEW { if (order.Instruction == InstructionType.SELL_TO_CLOSE) { PortfolioDatabaseException ex = new PortfolioDatabaseException("No existing position corresponding to sell order"); Log.Fatal(ex, "No existing position corresponding to sell order {@Order}- Symbol {Symbol}", order, order.Symbol); throw ex; } delta = new PositionDelta( DeltaType.NEW, order.Symbol, order.Quantity, order.Price, 0, order.Quote, order.Time); deltas.Add(delta); Position newPos = new Position(order.Symbol, order.Quantity, order.Price); InsertPosition(newPos); } else if (order.Instruction == InstructionType.BUY_TO_OPEN) // ADD { delta = new PositionDelta( DeltaType.ADD, order.Symbol, order.Quantity, order.Price, order.Quantity / oldPos.LongQuantity, order.Quote, order.Time); deltas.Add(delta); DeletePosition(oldPos); int newQuantity = (int)oldPos.LongQuantity + order.Quantity; float averagePrice = (oldPos.AveragePrice * oldPos.LongQuantity + order.Quantity * order.Price) / newQuantity; Position position = new Position(order.Symbol, newQuantity, averagePrice); InsertPosition(position); } else if (order.Instruction == InstructionType.SELL_TO_CLOSE) // SELL { delta = new PositionDelta( DeltaType.SELL, order.Symbol, order.Quantity, order.Price, order.Quantity / oldPos.LongQuantity, order.Quote, order.Time); deltas.Add(delta); DeletePosition(oldPos); int newQuantity = (int)oldPos.LongQuantity - order.Quantity; if (newQuantity > 0) { Position position = new Position(order.Symbol, newQuantity, oldPos.AveragePrice); InsertPosition(position); } } else { PortfolioDatabaseException ex = new PortfolioDatabaseException("Unexpected instruction type: " + order.Instruction); Log.Fatal(ex, "Unexpected instruction type"); throw ex; } InsertOrder(order); InsertDeltaAndUpsertUsedUnderlyingSymbol(delta); } return(deltas); }