Exemplo n.º 1
0
        /// <summary>
        /// Adjusts the amount of available units for every transaction
        /// </summary>
        /// <param name="newEntry">New entry</param>
        private void AdjustAvailableUnits(ITransactionBookEntry newEntry)
        {
            var id = newEntry.StockId;

            if (newEntry is ISellingTransactionBookEntry && newEntry.Shares > GetOrAddOpenPosition(id).Shares)
            {
                throw new InvalidOperationException("Cannot sell more units than available.");
            }

            _changedEntries.Clear(id);

            if (newEntry is IBuyingTransactionBookEntry)
            {
                UpdateOpenPosition(id);
                return;
            }

            if (newEntry is ISplitTransactionBookEntry split)
            {
                var buys = _entries.GetOrAdd(id).Where(e => e is IBuyingTransactionBookEntry).ToList();

                buys.ForEach(t => _entries.Delete(id, t));
                _entries.Add(id, split.CreatePositionAfterSplit(buys));

                UpdateOpenPosition(id);
                return;
            }

            var newUnitsToSell = newEntry.Shares;

            var deletes = new List <ITransactionBookEntry>();

            foreach (var entry in _entries.GetOrAdd(id).OrderBy(e => e.OrderDate))
            {
                if (entry is IBuyingTransactionBookEntry)
                {
                    var changed = entry.Copy();

                    //Complete buying transaction filled
                    if (entry.Shares <= newUnitsToSell)
                    {
                        newUnitsToSell -= entry.Shares;

                        if (!(newEntry is IDividendTransactionBookEntry))
                        {
                            entry.Shares = 0;
                        }
                    }
                    //Partly filled
                    else
                    {
                        changed.Shares     = newUnitsToSell;
                        changed.OrderCosts = (entry.OrderCosts / entry.Shares) * newUnitsToSell;

                        if (!(newEntry is IDividendTransactionBookEntry))
                        {
                            entry.OrderCosts = (entry.OrderCosts / entry.Shares) * (entry.Shares - newUnitsToSell);
                            entry.Shares    -= newUnitsToSell;
                        }

                        newUnitsToSell = 0;
                    }

                    _changedEntries.Add(id, changed);

                    if (entry.Shares == 0 && !(newEntry is IDividendTransactionBookEntry))
                    {
                        deletes.Add(entry);
                    }

                    if (newUnitsToSell == 0)
                    {
                        break;
                    }
                }
            }

            _changedEntries.Add(id, newEntry);
            _entries.Delete(id, newEntry);

            if (!(newEntry is IDividendTransactionBookEntry))
            {
                deletes.ForEach(t => _entries.Delete(id, t));
            }

            UpdateOpenPosition(id);
        }