Example #1
0
        /// <summary>Keep the 'Trades' collection in sync with the live positions</summary>
        private void SyncTrades()
        {
            var live_positions = Positions.ToHashSet(x => x.Id);

            Trades.RemoveIf(x => !live_positions.Contains(x.Id));
        }
Example #2
0
        /// <summary>Refresh the set of trades</summary>
        private void UpdateTrades()
        {
            m_trades_invalidated = false;
            if (Model == null)
            {
                return;
            }

            // Create lists of trades per instrument.
            // Once complete, go through existing trades looking for matches.
            // Remove any trades not in 'trades', and any trades not in 'Trades'.
            var trades_map = new Dictionary <Instrument, List <Trade> >();

            // Remove orders from trades that are no longer in 'Orders'
            foreach (var trade in Trades)
            {
                trade.Orders.RemoveIf(x => !OrderLookup.ContainsKey(x.Id));
            }

            // Remove trades that contain no orders
            Trades.RemoveIf(x => x.Orders.Count == 0);

            // Group the orders into instruments
            foreach (var order_grp in Orders.GroupBy(x => x.Instrument))
            {
                // Create an ordered list of trades based on non-overlapping time ranges
                var instr  = order_grp.Key;
                var trades = trades_map.GetOrAdd(instr, k => new List <Trade>());
                foreach (var order in order_grp)
                {
                    // Look for a trade in the list that intersects the time range
                    var idx = trades.BinarySearch(r =>
                                                  order.ExitTimeUTC <r.EntryTimeUTC ? -1 :
                                                                     order.EntryTimeUTC> r.ExitTimeUTC  ? +1 : 0);

                    // If index is positive, then it's the index of a trade that intersects the time range.
                    // If not, then there is no trade yet that covers this order so create one.
                    var trade = (idx >= 0) ? trades[idx] : trades.Insert2(idx = ~idx, new Trade(Model, instr));
                    trade.Orders.Add(order);

                    // Sanity check that the trade time range overlaps the order time range
                    Debug.Assert(
                        order.EntryTimeUTC >= trade.EntryTimeUTC &&
                        order.ExitTimeUTC <= trade.ExitTimeUTC &&
                        trade.Orders.Count(x => x == order) == 1);

                    // Merge adjacent trades if their time ranges overlap
                    for (int i = idx; i-- != 0 && trades[i].ExitTimeUTC >= trades[idx].ExitTimeUTC;)
                    {
                        // Trade automatically updates its Entry/Exit time when its 'Orders' collection is changed
                        trades[i].Orders.AddRange(trades[idx].Orders);
                        trades[idx].Dispose();
                        trades.RemoveAt(idx);
                        idx = i;
                    }
                    for (int i = idx; ++i != trades.Count && trades[idx].ExitTimeUTC >= trades[i].EntryTimeUTC;)
                    {
                        // Trade automatically updates its Entry/Exit time when its 'Orders' collection is changed
                        trades[idx].Orders.AddRange(trades[i].Orders);
                        trades[i].Dispose();
                        trades.RemoveAt(i);
                        i = idx;
                    }
                }

                // Check the ranges are non-overlapping
                                #if DEBUG
                for (int i = 0; i != trades.Count; ++i)
                {
                    Debug.Assert(trades[i].EntryTimeUTC <= trades[i].ExitTimeUTC);
                    Debug.Assert(i == 0 || trades[i - 1].EntryTimeUTC < trades[i].EntryTimeUTC);
                    Debug.Assert(i == 0 || trades[i - 1].ExitTimeUTC < trades[i].EntryTimeUTC);
                }
                                #endif
            }

            // Remove trades from the trades map that match existing trades.
            // Want to avoid deleting existing trades where possible.
            foreach (var existing in Trades)
            {
                // There shouldn't be any existing trades for instruments that aren't in 'trades_map'
                var trades = trades_map[existing.Instrument];

                // Try to find a match in trades for 'existing'
                var idx = trades.IndexOf(trade => trade.Orders.SequenceEqualUnordered(existing.Orders));

                // Matching existing trade found, remove from trades list in the map
                if (idx != -1)
                {
                    trades[idx].Dispose();
                    trades.RemoveAt(idx);
                }

                // No more trades for this instrument? Remove the entry from the map
                if (trades.Count == 0)
                {
                    trades_map.Remove(existing.Instrument);
                }
            }

            // Add all trades left in the trades map to the current trades list
            foreach (var trades in trades_map.Values)
            {
                Trades.AddRange(trades);
            }
        }