public async Task Optimize()
        {
            //Portfolio 1: 5d80d0587d2d4657d8e1fe8f
            //Portfolio 2: 5d83dbda7d2d4604505722e5
            var portfolio = await _portfolioDataAccess.GetPortfolio("5d83dbda7d2d4604505722e5");

            portfolio.Categories = portfolio.Categories.Where(c => !c.Name.Contains("Risk")).ToList();

            var symbols = portfolio.AllStocks.Select(p => p.Symbol).ToList();

            var getHistory = _alphaClient.GetPortfolioHistory(symbols);

            var result = await OptimizeCategory(portfolio);

            var portfolioSummary = string.Join("\n", result.AllStocks.Select(s => $"{s.Symbol}: {s.DesiredAmount}%"));

            Console.WriteLine(portfolioSummary);

            var finalSet = new RunScenarioDataSet
            {
                InitialInvestment = 10000,
                CashInfluxAmount  = 150,
                CashInfluxCadence = CadenceTypeEnum.Weekly,
                Portfolios        = new List <Portfolio> {
                    result
                },
                History = await getHistory
            };

            var finalResult = await _rebalanceLogic.Simulate(finalSet);

            var simulationResult = finalResult.First();

            var percents = simulationResult.Results.Select(r => Math.Round(r.PercentIncrease, 2) * 100).OrderBy(r => r).ToList();

            var min          = percents[0];
            var percentile25 = percents[(int)(.25 * percents.Count)];
            var percentile50 = percents[(int)(.50 * percents.Count)];
            var percentile75 = percents[(int)(.75 * percents.Count)];
            var max          = percents.Last();

            string percentilesString = $"{min}%, {percentile25}%, {percentile50}%, {percentile75}%, {max}%";

            var firstScenario  = simulationResult.Results.First();
            var averagePercent = Math.Round(percents.Average(), 2);

            //var maxPercent = Math.Round(simulationResult.Results.Max(r => r.PercentIncrease), 2);
            //var minPercent = Math.Round(simulationResult.Results.Min(r => r.PercentIncrease), 2);

            //Console.WriteLine($"Portfolio {result.IndexOf(simulationResult)} experienced growth between {minPercent * 100}% and {maxPercent * 100}% with an average of {averagePercent * 100}% from {firstScenario.StartDate.Date.ToShortDateString()} to {firstScenario.EndDate.Date.ToShortDateString()}");
            Console.WriteLine($"Optimal portfolio experienced an average growth of {averagePercent}% from {firstScenario.StartDate.Date.ToShortDateString()} to {firstScenario.EndDate.Date.ToShortDateString()} with the following percentiles: {percentilesString}.");
        }
        public async Task <List <ScenarioResult> > RunScenario(RunScenarioDataSet dataSet)
        {
            if (dataSet.CashInfluxCadence != CadenceTypeEnum.Weekly)
            {
                throw new Exception($"{dataSet.CashInfluxCadence} cadence not yet supported.");
            }

            ConcurrentQueue <ScenarioResult> results = new ConcurrentQueue <ScenarioResult>();

            await Task.WhenAll(dataSet.Portfolios.Select(p => Task.Run(() =>
            {
                var portfolio = p.Copy();

                var symbols = portfolio.AllStocks.Select(a => a.Symbol).ToList();

                var currentAllocations = symbols.Select(s => new StockAllocation
                {
                    Symbol            = s,
                    DesiredAmount     = 0,
                    DesiredAmountType = AllocationTypeEnum.StockAmount
                }).ToList();

                decimal cashOnHand = dataSet.InitialInvestment;
                foreach (var period in dataSet.History)
                {
                    var quotes = period.Stocks.Select(s => new Quote
                    {
                        Symbol    = s.Symbol,
                        Price     = s.PeriodData.AdjustedClose,
                        QuoteDate = period.ClosingDate
                    }).ToList();

                    portfolio.CashOnHand = cashOnHand;
                    foreach (var allocation in portfolio.AllStocks)
                    {
                        var currentAllocation    = currentAllocations.Find(s => s.Symbol == allocation.Symbol);
                        allocation.CurrentShares = currentAllocation.CurrentShares;
                    }

                    var rebalanceDataSet = new RebalanceDataSet
                    {
                        Portfolio = portfolio,
                        Quotes    = quotes
                    };

                    var rebalanceResult = Rebalance(rebalanceDataSet);

                    cashOnHand = rebalanceResult.RemainingCashOnHand + dataSet.CashInfluxAmount;

                    foreach (var action in rebalanceResult.Actions)
                    {
                        var actualAllocation = currentAllocations.Find(a => a.Symbol == action.Symbol);
                        if (action.ActionType == RebalanceActionTypeEnum.Buy)
                        {
                            actualAllocation.CurrentShares += action.Amount;
                        }
                        else
                        {
                            actualAllocation.CurrentShares -= action.Amount;
                        }
                    }
                }

                decimal finalPortfolioValue = cashOnHand - dataSet.CashInfluxAmount;
                var lastPeriod = dataSet.History.Last();

                foreach (var allocation in currentAllocations)
                {
                    finalPortfolioValue += allocation.CurrentShares *lastPeriod.Stocks.Find(s => s.Symbol == allocation.Symbol).PeriodData.AdjustedClose;
                }

                var totalCashInvested = (dataSet.InitialInvestment + dataSet.CashInfluxAmount * (dataSet.History.Count - 1));
                var result            = new ScenarioResult
                {
                    PercentIncrease     = (finalPortfolioValue / totalCashInvested) - 1,
                    StartDate           = dataSet.History.First().ClosingDate,
                    EndDate             = lastPeriod.ClosingDate,
                    FinalPortfolioValue = finalPortfolioValue,
                    TotalCashInvested   = totalCashInvested
                };

                results.Enqueue(result);
            })));

            return(results.ToList());
        }
        public async Task <List <SimulationResult> > Simulate(RunScenarioDataSet dataSet)
        {
            for (int d = 0; d < dataSet.Portfolios.Count; d++)
            {
                dataSet.Portfolios[d].Id = d.ToString();
            }

            ConcurrentDictionary <string, ConcurrentQueue <ScenarioResult> > mapping = new ConcurrentDictionary <string, ConcurrentQueue <ScenarioResult> >();

            foreach (var portfolio in dataSet.Portfolios)
            {
                mapping[portfolio.Id] = new ConcurrentQueue <ScenarioResult>();
            }

            var realScenario = RunScenario(dataSet);

            var tasks = new List <Task>();

            int stockCount = (int)dataSet.Portfolios.Average(p => p.AllStocks.Count);
            //int iterationCount = 16000000 / (dataSet.Portfolios.Count * dataSet.History.Count);
            int iterationCount = 5000000 / (int)(Math.Pow(dataSet.Portfolios.Count * 2, 1.1) * (int)Math.Pow(dataSet.History.Count, .5) * (int)Math.Pow(stockCount, .9));
            //int iterationCount = 500000 / (int)(Math.Pow(dataSet.Portfolios.Count, .9) * (int)Math.Pow(dataSet.History.Count, .5) * (int)Math.Pow(stockCount, .9));
            //int iterationCount = 4000 / (dataSet.Portfolios.Count * dataSet.History.Count);

            var start = DateTime.Now;

            for (var i = 0; i < iterationCount; i++)
            {
                tasks.Add(Task.Run(async() =>
                {
                    var revisedHistory = dataSet.History.ShuffleCopy();
                    var revisedDataSet = new RunScenarioDataSet
                    {
                        CashInfluxAmount  = dataSet.CashInfluxAmount,
                        CashInfluxCadence = dataSet.CashInfluxCadence,
                        History           = revisedHistory,
                        InitialInvestment = dataSet.InitialInvestment,
                        Portfolios        = dataSet.Portfolios.Copy()
                    };

                    var nextResults = await RunScenario(revisedDataSet);
                    foreach (var scenarioResult in nextResults)
                    {
                        var index     = nextResults.IndexOf(scenarioResult);
                        var portfolio = dataSet.Portfolios[index];

                        mapping[portfolio.Id].Enqueue(scenarioResult);
                    }
                }));
            }

            await Task.WhenAll(tasks);

            var results = await realScenario;

            foreach (var scenarioResult in results)
            {
                var index     = results.IndexOf(scenarioResult);
                var portfolio = dataSet.Portfolios[index].Copy();

                mapping[portfolio.Id].Enqueue(scenarioResult);
            }

            var duration = DateTime.Now.Subtract(start).TotalSeconds;

            Console.WriteLine($"{dataSet.Portfolios.Count} portfolios, {dataSet.History.Count} weeks, {stockCount} stocks took {duration}s");

            return(mapping.Select(kp => new SimulationResult
            {
                Portfolio = dataSet.Portfolios.First(p => kp.Key == p.Id),
                Results = kp.Value.ToList()
            }).ToList());
        }
        public async Task <T> OptimizeCategoryv2 <T>(T cat, DateTimeOffset?fromDate = null) where T : OptimizableCategory
        {
            var categorySymbols = cat.AllSymbols;
            var categoryPeriods = await _alphaClient.GetPortfolioHistory(categorySymbols);

            if (fromDate != null)
            {
                categoryPeriods = categoryPeriods.Where(p => p.ClosingDate > fromDate).ToList();
            }

            var getCategories = Task.Run(async() =>
            {
                if (cat.Categories == null || !cat.Categories.Any())
                {
                    return(cat.Categories);
                }

                var categories         = new List <OptimizableCategory>();
                var optimizeCategories = cat.Categories.Select(c => OptimizeCategoryv2(c, fromDate));

                foreach (var optimizeCategory in optimizeCategories)
                {
                    var optimized = await optimizeCategory;
                    if (optimized != null)
                    {
                        categories.Add(optimized.Copy());
                    }
                }

                return(categories);
            });

            var optimizedCategories = await getCategories;

            if (cat.Stocks == null || !cat.Stocks.Any())
            {
                cat.Categories = optimizedCategories;
                return(cat);
            }

            var stockCombinations = GetPossibleStockCombinationsv2(cat.AllocatedAmount, cat.Stocks);

            var portfolios = stockCombinations.Select((allocations, i) => new Portfolio
            {
                Id         = i.ToString(),
                Name       = i.ToString(),
                CashOnHand = 0,
                Stocks     = allocations?.Where(a => a.AllocatedAmount > 0).Select(a => a.Create()).ToList(),
                Categories = optimizedCategories?.Select(c => c.Create()).ToList()
            }).ToList();

            RunScenarioDataSet dataSet = new RunScenarioDataSet
            {
                InitialInvestment = 10000,
                CashInfluxAmount  = 150,
                CashInfluxCadence = CadenceTypeEnum.Weekly,
                Portfolios        = portfolios,
                History           = categoryPeriods
            };

            var result = await _rebalanceLogic.Simulate(dataSet);

            var winner = result.Aggregate((r1, r2) => r1.Results.Average(r => r.PercentIncrease) > r2.Results.Average(r => r.PercentIncrease) ? r1 : r2);

            cat.Categories = optimizedCategories;
            cat.Stocks     = winner.Portfolio.Stocks.Select(s => new OptimizableStock {
                Symbol = s.Symbol, AllocatedAmount = (int)s.DesiredAmount
            }).ToList();

            return(cat);
        }
        public async Task Scenarios()
        {
            //SHY,TLT,IEF,LQD,AGG
            var stocks = new[]
            {
                new[]
                {
                    new { Symbol = "SOXL", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6m },
                    new { Symbol = "CIBR", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m },
                    new { Symbol = "MJ", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m },
                    new { Symbol = "TAN", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m },
                    new { Symbol = "ICLN", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m },
                    new { Symbol = "XBI", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m },
                    new { Symbol = "ARKG", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m },
                    new { Symbol = "XLF", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6m },
                    new { Symbol = "SPY", Type = AllocationTypeEnum.Percentage, DesiredAmount = 14m },
                    new { Symbol = "QQQ", Type = AllocationTypeEnum.Percentage, DesiredAmount = 14m },
                    new { Symbol = "DIA", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6m },
                    new { Symbol = "^RUT", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6m },
                    new { Symbol = "TLT", Type = AllocationTypeEnum.Percentage, DesiredAmount = 17m },
                    new { Symbol = "VIXY", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m }
                },
                new[]
                {
                    new { Symbol = "SOXL", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6m },
                    new { Symbol = "CIBR", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m },
                    new { Symbol = "MJ", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m },
                    new { Symbol = "TAN", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m },
                    new { Symbol = "ICLN", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m },
                    new { Symbol = "XBI", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m },
                    new { Symbol = "ARKG", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m },
                    new { Symbol = "XLF", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6m },
                    new { Symbol = "SPY", Type = AllocationTypeEnum.Percentage, DesiredAmount = 14m },
                    new { Symbol = "QQQ", Type = AllocationTypeEnum.Percentage, DesiredAmount = 15m },
                    new { Symbol = "DIA", Type = AllocationTypeEnum.Percentage, DesiredAmount = 12m },
                    new { Symbol = "TLT", Type = AllocationTypeEnum.Percentage, DesiredAmount = 16m },
                    new { Symbol = "VIXY", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m }
                },
                new[]
                {
                    new { Symbol = "SOXL", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6m },
                    new { Symbol = "CIBR", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m },
                    new { Symbol = "MJ", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m },
                    new { Symbol = "TAN", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m },
                    new { Symbol = "ICLN", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m },
                    new { Symbol = "XBI", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m },
                    new { Symbol = "ARKG", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m },
                    new { Symbol = "XLF", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6m },
                    new { Symbol = "SPY", Type = AllocationTypeEnum.Percentage, DesiredAmount = 14m },
                    new { Symbol = "QQQ", Type = AllocationTypeEnum.Percentage, DesiredAmount = 15m },
                    new { Symbol = "DIA", Type = AllocationTypeEnum.Percentage, DesiredAmount = 12m },
                    new { Symbol = "TLT", Type = AllocationTypeEnum.Percentage, DesiredAmount = 16m },
                    new { Symbol = "VIXY", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3m }
                }
            }.ToList();

            var symbols         = stocks.SelectMany(s => s.Select(st => st.Symbol)).Distinct().ToList();
            var relevantPeriods = await _alphaClient.GetPortfolioHistory(symbols);

            var portfolios = stocks.Select(stockSet => new Portfolio
            {
                Stocks = stockSet.Select(s => new StockAllocation
                {
                    Symbol            = s.Symbol,
                    DesiredAmountType = s.Type,
                    DesiredAmount     = s.DesiredAmount
                }).ToList()
            }).ToList();

            RunScenarioDataSet dataSet = new RunScenarioDataSet
            {
                InitialInvestment = 10000,
                CashInfluxAmount  = 150,
                CashInfluxCadence = CadenceTypeEnum.Weekly,
                Portfolios        = portfolios,
                History           = relevantPeriods
            };

            var results = await _rebalanceLogic.RunScenario(dataSet);

            //foreach (var action in result.Actions)
            //{
            //    Console.WriteLine($"{action.ActionType} {action.Amount} shares of {action.Symbol}");
            //}

            foreach (var result in results)
            {
                Console.WriteLine($"Portfolio {results.IndexOf(result)} grew by {result.PercentIncrease * 100} percent from {result.StartDate.Date.ToShortDateString()} to {result.EndDate.Date.ToShortDateString()}");
            }
        }
        public async Task Simulate()
        {
            var stocks = new[]
            {
                new[]
                {
                    new { Symbol = "SOXL", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6 },
                    new { Symbol = "CIBR", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "MJ", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "TAN", Type = AllocationTypeEnum.Percentage, DesiredAmount = 5 },
                    new { Symbol = "ICLN", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "XBI", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "ARKG", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6 },
                    new { Symbol = "XLF", Type = AllocationTypeEnum.Percentage, DesiredAmount = 5 },
                    new { Symbol = "SPY", Type = AllocationTypeEnum.Percentage, DesiredAmount = 14 },
                    new { Symbol = "QQQ", Type = AllocationTypeEnum.Percentage, DesiredAmount = 13 },
                    new { Symbol = "DIA", Type = AllocationTypeEnum.Percentage, DesiredAmount = 12 },
                    new { Symbol = "VIXY", Type = AllocationTypeEnum.Percentage, DesiredAmount = 4 },
                    new { Symbol = "TLT", Type = AllocationTypeEnum.Percentage, DesiredAmount = 14 }
                },
                new[]
                {
                    new { Symbol = "SOXL", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6 },
                    new { Symbol = "CIBR", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "MJ", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "TAN", Type = AllocationTypeEnum.Percentage, DesiredAmount = 5 },
                    new { Symbol = "ICLN", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "XBI", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "ARKG", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6 },
                    new { Symbol = "XLF", Type = AllocationTypeEnum.Percentage, DesiredAmount = 5 },
                    new { Symbol = "SPY", Type = AllocationTypeEnum.Percentage, DesiredAmount = 14 },
                    new { Symbol = "QQQ", Type = AllocationTypeEnum.Percentage, DesiredAmount = 12 },
                    new { Symbol = "DIA", Type = AllocationTypeEnum.Percentage, DesiredAmount = 13 },
                    new { Symbol = "VIXY", Type = AllocationTypeEnum.Percentage, DesiredAmount = 4 },
                    new { Symbol = "TLT", Type = AllocationTypeEnum.Percentage, DesiredAmount = 14 }
                },
                new[]
                {
                    new { Symbol = "SOXL", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6 },
                    new { Symbol = "CIBR", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "MJ", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "TAN", Type = AllocationTypeEnum.Percentage, DesiredAmount = 5 },
                    new { Symbol = "ICLN", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "XBI", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "ARKG", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6 },
                    new { Symbol = "XLF", Type = AllocationTypeEnum.Percentage, DesiredAmount = 5 },
                    new { Symbol = "SPY", Type = AllocationTypeEnum.Percentage, DesiredAmount = 13 },
                    new { Symbol = "QQQ", Type = AllocationTypeEnum.Percentage, DesiredAmount = 14 },
                    new { Symbol = "DIA", Type = AllocationTypeEnum.Percentage, DesiredAmount = 12 },
                    new { Symbol = "VIXY", Type = AllocationTypeEnum.Percentage, DesiredAmount = 4 },
                    new { Symbol = "TLT", Type = AllocationTypeEnum.Percentage, DesiredAmount = 14 }
                },
                new[]
                {
                    new { Symbol = "SOXL", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6 },
                    new { Symbol = "CIBR", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "MJ", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "TAN", Type = AllocationTypeEnum.Percentage, DesiredAmount = 5 },
                    new { Symbol = "ICLN", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "XBI", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "ARKG", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6 },
                    new { Symbol = "XLF", Type = AllocationTypeEnum.Percentage, DesiredAmount = 5 },
                    new { Symbol = "SPY", Type = AllocationTypeEnum.Percentage, DesiredAmount = 13 },
                    new { Symbol = "QQQ", Type = AllocationTypeEnum.Percentage, DesiredAmount = 12 },
                    new { Symbol = "DIA", Type = AllocationTypeEnum.Percentage, DesiredAmount = 14 },
                    new { Symbol = "VIXY", Type = AllocationTypeEnum.Percentage, DesiredAmount = 4 },
                    new { Symbol = "TLT", Type = AllocationTypeEnum.Percentage, DesiredAmount = 14 }
                },
                new[]
                {
                    new { Symbol = "SOXL", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6 },
                    new { Symbol = "CIBR", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "MJ", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "TAN", Type = AllocationTypeEnum.Percentage, DesiredAmount = 5 },
                    new { Symbol = "ICLN", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "XBI", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "ARKG", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6 },
                    new { Symbol = "XLF", Type = AllocationTypeEnum.Percentage, DesiredAmount = 5 },
                    new { Symbol = "SPY", Type = AllocationTypeEnum.Percentage, DesiredAmount = 12 },
                    new { Symbol = "QQQ", Type = AllocationTypeEnum.Percentage, DesiredAmount = 13 },
                    new { Symbol = "DIA", Type = AllocationTypeEnum.Percentage, DesiredAmount = 14 },
                    new { Symbol = "VIXY", Type = AllocationTypeEnum.Percentage, DesiredAmount = 4 },
                    new { Symbol = "TLT", Type = AllocationTypeEnum.Percentage, DesiredAmount = 14 }
                },
                new[]
                {
                    new { Symbol = "SOXL", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6 },
                    new { Symbol = "CIBR", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "MJ", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "TAN", Type = AllocationTypeEnum.Percentage, DesiredAmount = 5 },
                    new { Symbol = "ICLN", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "XBI", Type = AllocationTypeEnum.Percentage, DesiredAmount = 3 },
                    new { Symbol = "ARKG", Type = AllocationTypeEnum.Percentage, DesiredAmount = 6 },
                    new { Symbol = "XLF", Type = AllocationTypeEnum.Percentage, DesiredAmount = 5 },
                    new { Symbol = "SPY", Type = AllocationTypeEnum.Percentage, DesiredAmount = 12 },
                    new { Symbol = "QQQ", Type = AllocationTypeEnum.Percentage, DesiredAmount = 14 },
                    new { Symbol = "DIA", Type = AllocationTypeEnum.Percentage, DesiredAmount = 13 },
                    new { Symbol = "VIXY", Type = AllocationTypeEnum.Percentage, DesiredAmount = 4 },
                    new { Symbol = "TLT", Type = AllocationTypeEnum.Percentage, DesiredAmount = 14 }
                }
            }.ToList();

            var symbols         = stocks.SelectMany(s => s.Select(st => st.Symbol)).Distinct().ToList();
            var relevantPeriods = await _alphaClient.GetPortfolioHistory(symbols);

            var portfolios = stocks.Select(stockSet => new Portfolio
            {
                Stocks = stockSet.Select(s => new StockAllocation
                {
                    Symbol            = s.Symbol,
                    DesiredAmountType = s.Type,
                    DesiredAmount     = s.DesiredAmount
                }).ToList()
            }).ToList();

            RunScenarioDataSet dataSet = new RunScenarioDataSet
            {
                InitialInvestment = 10000,
                CashInfluxAmount  = 150,
                CashInfluxCadence = CadenceTypeEnum.Weekly,
                Portfolios        = portfolios,
                History           = relevantPeriods
            };

            var result = await _rebalanceLogic.Simulate(dataSet);

            foreach (var simulationResult in result)
            {
                var percents = simulationResult.Results.Select(r => Math.Round(r.PercentIncrease, 2) * 100).OrderBy(r => r).ToList();

                var min          = percents[0];
                var percentile25 = percents[(int)(.25 * percents.Count)];
                var percentile50 = percents[(int)(.50 * percents.Count)];
                var percentile75 = percents[(int)(.75 * percents.Count)];
                var max          = percents.Last();

                string percentilesString = $"{min}%, {percentile25}%, {percentile50}%, {percentile75}%, {max}%";

                var firstScenario  = simulationResult.Results.First();
                var averagePercent = Math.Round(percents.Average(), 2);

                //var maxPercent = Math.Round(simulationResult.Results.Max(r => r.PercentIncrease), 2);
                //var minPercent = Math.Round(simulationResult.Results.Min(r => r.PercentIncrease), 2);

                //Console.WriteLine($"Portfolio {result.IndexOf(simulationResult)} experienced growth between {minPercent * 100}% and {maxPercent * 100}% with an average of {averagePercent * 100}% from {firstScenario.StartDate.Date.ToShortDateString()} to {firstScenario.EndDate.Date.ToShortDateString()}");
                Console.WriteLine($"Portfolio {result.IndexOf(simulationResult)} experienced an average growth of {averagePercent}% from {firstScenario.StartDate.Date.ToShortDateString()} to {firstScenario.EndDate.Date.ToShortDateString()} with the following percentiles: {percentilesString}.");
            }
        }
        public async Task <T> OptimizeCategory <T>(T cat) where T : Category
        {
            var categorySymbols    = cat.AllStocks.Select(st => st.Symbol).Distinct().ToList();
            var getCategoryPeriods = _alphaClient.GetPortfolioHistory(categorySymbols);

            var getCategories = Task.Run(async() =>
            {
                if (cat.Categories == null || !cat.Categories.Any())
                {
                    return(cat.Categories);
                }

                var categories         = new List <Category>();
                var optimizeCategories = cat.Categories.Select(OptimizeCategory);

                foreach (var optimizeCategory in optimizeCategories)
                {
                    var optimized = await optimizeCategory;

                    Clean(optimized);

                    categories.Add(optimized);
                }

                return(categories);
            });

            var optimizedCategories = await getCategories;

            if (cat.Stocks == null || !cat.Stocks.Any())
            {
                cat.Categories = optimizedCategories;
                return(cat);
            }

            var stockPercent      = (int)cat.Stocks.Sum(s => s.DesiredAmount);
            var stockCombinations = GetPossibleStockCombinations(stockPercent, cat.Stocks.Copy(), 3, 6);

            var portfolios = stockCombinations.Select((allocations, i) => new Portfolio
            {
                Id         = i.ToString(),
                Name       = i.ToString(),
                CashOnHand = 0,
                Stocks     = allocations.Where(a => a.DesiredAmount > 0).Copy(),
                Categories = optimizedCategories
            }).ToList();

            RunScenarioDataSet dataSet = new RunScenarioDataSet
            {
                InitialInvestment = 10000,
                CashInfluxAmount  = 150,
                CashInfluxCadence = CadenceTypeEnum.Weekly,
                Portfolios        = portfolios,
                History           = await getCategoryPeriods
            };

            var result = await _rebalanceLogic.Simulate(dataSet);

            var winner = result.Aggregate((r1, r2) => r1.Results.Average(r => r.PercentIncrease) > r2.Results.Average(r => r.PercentIncrease) ? r1 : r2);

            cat.Categories = winner.Portfolio.Categories.Copy();
            cat.Stocks     = winner.Portfolio.Stocks.Copy();

            return(cat);
        }