Beispiel #1
0
        /// <summary>
        /// Populates the Items array naming and assigning margin requirements to each item
        /// </summary>
        public void Optimize(TWEAKS tweaks)
        {
            List <PortfolioItem> results = new List <PortfolioItem>();

            // group all positions by underlying security
            var underlyerBuckets = _input.GroupBy(x => ((OptionSymbol)x.Symbol).UnderlyingSecuritySymbol ?? x.Symbol).ToDictionary(x => x.Key, x => x.ToArray());

            // for all underlying securities
            foreach (var underlyer in underlyerBuckets.Keys)
            {
                // select all options
                var options = underlyerBuckets[underlyer]
                              .Where(x => ((OptionSymbol)x.Symbol).IsValid)
                              .Where(x => x.Quantity != 0)
                              .Select(x =>
                                      new OptionPosition
                {
                    Deliverables   = DELIVERABLES, Multiplier = MULTIPLIER,
                    Quantity       = x.Quantity, Symbol = (OptionSymbol)x.Symbol,
                    Price          = _prices[x.Symbol],
                    UnderlierPrice = _prices[underlyer]
                }).ToArray();

                // select the stock position
                var q     = underlyerBuckets[underlyer].Where(x => !((OptionSymbol)x.Symbol).IsValid).Sum(s => s.Quantity);
                var stock = new StockPosition {
                    Quantity = q, Price = _prices[underlyer], Symbol = underlyer
                };

                // group
                switch (tweaks)
                {
                case TWEAKS.SPREADS_NO_DIFFERENCE_IN_CREDIT_DEBIT:
                {
                    var strategies = Group_WithCommonSpreads(stock, options);
                    results.AddRange(strategies);
                    break;
                }

                case TWEAKS.CREDIT_DEBIT_SPREADS_SEPEARATELY:
                default:
                {
                    var strategies = Group_WithCreditDebitSpreads(stock, options);
                    results.AddRange(strategies);
                    break;
                }
                }


                // ASSERT
                if (stock.Quantity != 0 || options.Any(x => x.Quantity != 0))
                {
                    throw new Exception($"Pairing in {underlyer} failed. Some ungrouped quantity left");
                }
            }

            _items = results.ToArray();
        }
Beispiel #2
0
        private PortfolioItem[] GroupStock(StockPosition stockPosition)
        {
            List <PortfolioItem> list = new List <PortfolioItem>();

            if (stockPosition.Quantity == 0)
            {
                return(list.ToArray());
            }

            var item = new PortfolioItem
            {
                Name = $"{stockPosition.Symbol} Stock", Quantity = stockPosition.Quantity,
                MarginRequirement = STOCK_MARGIN_RATE * Math.Abs(stockPosition.Quantity) * stockPosition.Price
            };

            list.Add(item);
            stockPosition.Quantity = 0;

            return(list.ToArray());
        }
Beispiel #3
0
        private PortfolioItem[] GroupCoveredPuts(StockPosition stock, OptionPosition[] options)
        {
            List <PortfolioItem> list = new List <PortfolioItem>();

            if (stock.Quantity >= 0)
            {
                return(list.ToArray());
            }

            // grab all short puts ordered by in the money amount and expiration date
            var shortPuts = options
                            .Where(x => x.Symbol.OptionType == OptionType.Put && x.Quantity < 0)
                            .OrderByDescending(x => x.InTheMoneyAmount).ThenBy(x => x.Symbol.ExpirationDate).ToArray();

            // locate covered puts
            foreach (var o in shortPuts)
            {
                var coverage    = Math.Ceiling(Math.Min(stock.Quantity, 0) / o.Deliverables); // maximum number of puts we can cover
                var coveredPuts = Math.Min(Math.Abs(coverage), Math.Abs(o.Quantity));         // number of ut we actually cover
                if (coveredPuts > 0)
                {
                    list.Add(new PortfolioItem {
                        Name = $"Covered Put {o.Symbol.ToStringFriendly()}", Quantity = coveredPuts,
                        MarginRequirement = STOCK_MARGIN_RATE * stock.Price * coveredPuts * o.Deliverables + Math.Max(o.Symbol.StrikePrice - stock.Price, 0) * coveredPuts,
                        Items             = new []
                        {
                            new StrategyItem {
                                Symbol = o.Symbol.ToOsiString(), Quantity = -1 * coveredPuts
                            },
                            new StrategyItem {
                                Symbol = $"{stock.Symbol} Stock", Quantity = -1 * coveredPuts * o.Deliverables
                            }
                        }
                    });
                    stock.Quantity += coveredPuts * o.Deliverables;
                    o.Quantity     += coveredPuts;
                }
            }

            return(list.ToArray());
        }
Beispiel #4
0
        private PortfolioItem[] GroupCoveredCalls(StockPosition stock, OptionPosition[] options)
        {
            List <PortfolioItem> list = new List <PortfolioItem>();

            if (stock.Quantity == 0)
            {
                return(list.ToArray());
            }

            // grab all short calls starting with the most in-the-money
            var shortCalls = options
                             .Where(x => x.Symbol.OptionType == OptionType.Call && x.Quantity < 0)
                             .OrderByDescending(x => x.InTheMoneyAmount).ThenBy(x => x.Symbol.ExpirationDate).ToArray();

            // locate covered calls
            foreach (var o in shortCalls)
            {
                var coverage     = Math.Floor(Math.Max(stock.Quantity, 0) / o.Deliverables); // maximum number of calls we can cover
                var coveredCalls = Math.Min(coverage, Math.Abs(o.Quantity));                 // number of calls we actually cover
                if (coveredCalls > 0)
                {
                    list.Add(new PortfolioItem {
                        Name = $"Covered Call {o.Symbol.ToStringFriendly()}", Quantity = coveredCalls,
                        MarginRequirement = STOCK_MARGIN_RATE * stock.Price * coveredCalls * o.Deliverables,
                        Items             = new []
                        {
                            new StrategyItem {
                                Symbol = o.Symbol.ToOsiString(), Quantity = -1 * coveredCalls
                            },
                            new StrategyItem {
                                Symbol = $"{stock.Symbol} Stock", Quantity = coveredCalls * o.Deliverables
                            }
                        }
                    });
                    stock.Quantity -= coveredCalls * o.Deliverables;
                    o.Quantity     += coveredCalls;
                }
            }

            return(list.ToArray());
        }
Beispiel #5
0
        private PortfolioItem[] Group_WithCreditDebitSpreads(StockPosition stock, OptionPosition[] options)
        {
            // TODO: Apply different multipliers to credit and debit spreads

//            for each underlying in the portfolio:
//                grouping short calls, order by in-the-money desc, then by expiration date asc:
//                    try covered call
//                    try debit spread
//                    try credit spread
//                    leave what is left for further grouping
//                grouping short puts, order by in-the-money desc, then by expiration date asc:
//                    try debit spread
//                    try credit spread
//                    try covered put
//                    try short straddle
//                every short put left is naked
//                every short call left is naked as well
//                every option that is left is one legged long
//                every stock that is left is just a stock
//            end

            List <PortfolioItem> list = new List <PortfolioItem>();

            var coveredCalls = GroupCoveredCalls(stock, options.Where(x => x.Quantity != 0).ToArray());

            list.AddRange(coveredCalls);

            var debitCallSpreads = GroupDebitCallSpreads(options.Where(x => x.Quantity != 0).ToArray());

            list.AddRange(debitCallSpreads);

            var creditCallSpreads = GroupCreditCallSpreads(options.Where(x => x.Quantity != 0).ToArray());

            list.AddRange(creditCallSpreads);

            var debitPutSpreads = GroupDebitPutSpreads(options.Where(x => x.Quantity != 0).ToArray());

            list.AddRange(debitPutSpreads);

            var creditPutSpreads = GroupCreditPutSpreads(options.Where(x => x.Quantity != 0).ToArray());

            list.AddRange(creditPutSpreads);

            var coveredPuts = GroupCoveredPuts(stock, options.Where(x => x.Quantity != 0).ToArray());

            list.AddRange(coveredPuts);

            var shortStraddles = GroupShortStraddles(options.Where(x => x.Quantity != 0).ToArray());

            list.AddRange(shortStraddles);

            var nakedPuts = GroupNakedPuts(options.Where(x => x.Quantity != 0).ToArray());

            list.AddRange(nakedPuts);

            var nakedCalls = GroupNakedCalls(options.Where(x => x.Quantity != 0).ToArray());

            list.AddRange(nakedCalls);

            var optionsLong = GroupOneLeggedLong(options.Where(x => x.Quantity != 0).ToArray());

            list.AddRange(optionsLong);

            var stocksNoStrategy = GroupStock(stock);

            list.AddRange(stocksNoStrategy);

            return(list.ToArray());
        }