Overall Statistics
Total Trades
567
Average Win
0.03%
Average Loss
-0.04%
Compounding Annual Return
-2.166%
Drawdown
2.900%
Expectancy
-0.289
Net Profit
-2.152%
Sharpe Ratio
-7.355
Sortino Ratio
-10.223
Probabilistic Sharpe Ratio
0.130%
Loss Rate
63%
Win Rate
37%
Profit-Loss Ratio
0.94
Alpha
-0.065
Beta
-0.018
Annual Standard Deviation
0.009
Annual Variance
0
Information Ratio
-1.778
Tracking Error
0.111
Treynor Ratio
3.65
Total Fees
$9056.15
Estimated Strategy Capacity
$950000.00
Lowest Capacity Asset
SITM X9PDY4DEHQ3P
Portfolio Turnover
0.72%
#region imports
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using QuantConnect.Util;
using QuantConnect.Indicators;
using QuantConnect.Data;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Data.Consolidators;
using QuantConnect.Orders;
using QuantConnect.Securities;
using QuantConnect.Securities.Option;
using QuantConnect.Interfaces;

#endregion
namespace QuantConnect.Algorithm.CSharp
{
    public partial class CollarAlgorithm : QCAlgorithm
    {
        public class ColumnSD
        {
            public SSQRColumn col;
            public SymbolData sd;
            public LookupData ld;

            public ColumnSD(SSQRColumn c, ref SymbolData s, LookupData l)
            {
                this.col = c;
                this.sd = s;
                this.ld = l;
            }

        }

        /// Calculates number of business days, taking into account:
        ///  - weekends (Saturdays and Sundays)
        ///  - bank holidays in the middle of the week
        public int BusinessDaysUntil(DateTime firstDay, DateTime lastDay, params DateTime[] bankHolidays)
        {
            firstDay = firstDay.Date;
            lastDay = lastDay.Date;
            if (firstDay > lastDay)
                throw new ArgumentException("Incorrect last day " + lastDay);

            TimeSpan span = lastDay - firstDay;
            int businessDays = span.Days + 1;
            int fullWeekCount = businessDays / 7;
            // find out if there are weekends during the time exceedng the full weeks
            if (businessDays > fullWeekCount * 7)
            {
                // we are here to find out if there is a 1-day or 2-days weekend
                // in the time interval remaining after subtracting the complete weeks
                int firstDayOfWeek = (int)firstDay.DayOfWeek;
                int lastDayOfWeek = (int)lastDay.DayOfWeek;
                if (lastDayOfWeek < firstDayOfWeek)
                    lastDayOfWeek += 7;
                if (firstDayOfWeek <= 6)
                {
                    if (lastDayOfWeek >= 7)// Both Saturday and Sunday are in the remaining time interval
                        businessDays -= 2;
                    else if (lastDayOfWeek >= 6)// Only Saturday is in the remaining time interval
                        businessDays -= 1;
                }
                else if (firstDayOfWeek <= 7 && lastDayOfWeek >= 7)// Only Sunday is in the remaining time interval
                    businessDays -= 1;
            }

            // subtract the weekends during the full weeks in the interval
            businessDays -= fullWeekCount + fullWeekCount;

            // subtract the number of bank holidays during the time interval
            foreach (DateTime bankHoliday in bankHolidays)
            {
                DateTime bh = bankHoliday.Date;
                if (firstDay <= bh && bh <= lastDay)
                    --businessDays;
            }

            return businessDays;
        }



        public class LookupData
        {
            public struct DividendRecord
            {
                public DateTime VEDate;
                public string ticker;
                public decimal divAmt;
                public DateTime exDate;
                public string frequency;
                // public string MOS;
                public int VERating;
                public decimal marketPrice;
                public decimal momentum;
                public decimal oneMonthForecast;
                public decimal oneYearPriceTarget;
                public int momentumRank;

            }

            public struct IEXRecord
            {
                public string ticker;
                public decimal divAmt;
                public DateTime exDate;
                public string frequency;
            }

            public List<SSQRColumn> SSQRMatrix = new List<SSQRColumn>();

            public Symbol uSymbol;                  // underlying symbol in current processing
            public decimal stockPrice;
            public decimal unitRiskAmt;             // amount of risk per unit   
            public bool doTracing = false;
            public bool doDeepTracing = false;
            public bool haltProcessing = false;
            public decimal workingDays = 365M;
            public decimal thisFFRate = 0M;
            public decimal ibkrRateAdj = .006M;     // IBKR adds 60bps to FFR (blended over $3,000,000)
            public int maxPutOTM = 0;           // maximum Put OTM depth
            public int maxCallOTM = 0;          // maximum Put OTM depth
            public int intTPRIndex = 0;
            public string VECase = "";
            public int intType = 0;

            public DateTime lastUpdated;

            public List<DividendRecord> exDividendDates = new List<DividendRecord>();
            public List<IEXRecord> IExDates = new List<IEXRecord>();
            public Dictionary<DateTime, decimal> fedFundsRates = new Dictionary<DateTime, decimal>();

            public Dictionary<decimal, decimal> ibkrHairCuts = new Dictionary<decimal, decimal>();
            public Dictionary<int, string> tickers = new Dictionary<int, string>();

            public decimal divdndAmt = 0;
            public string divdnFrequency = "";
            public DateTime exDivdnDate;
            public DateTime dtTst;                      // used for current date time in methods
            public int tprCounter = 0;

            public int daysRemainingDiv;                    // use vars for checking days before expiration
            public int daysRemainingC;                  // use vars for checking days before expiration
            public int daysRemainingP;
            public int daysRemaining2P;
            public int daysRemainingWC;
            public int intVERating;
            public decimal decMomentum;
            public decimal decOneMonthForecast;
            public decimal decOneYearPriceTarget;
            public decimal decTotalGain;
            public int intMomentumRank;
            public DateTime initialTargetEndDate;



            public void InitializeData(QCAlgorithm algo)
            {
                this.exDividendDates = this.GetDividendDates(algo);
                if (exDividendDates == null) algo.Log("|||||||||||||||||| MISSING DIV DATES |||||||||||||||");

                this.IExDates = this.getIEXDivData(algo);
                if (IExDates == null) algo.Log("|||||||||||||||||| MISSING IEX DATES |||||||||||||||");

                this.fedFundsRates = this.GetFedFundsRates(algo);
                if (fedFundsRates == null) algo.Log("|||||||||||||||||| MISSING FED FUNDS |||||||||||||||");

                this.ibkrHairCuts = this.InitializeHaircuts(algo);
                //this.tickers = this.GetTickers(algo);				////  //// //// REMOVED CODE FROM THIS VERSION OF THE CODE

            }

            // **********************   loadVEData		**************************************
            // ***  			Use this to find and return the current month's VE Ranking
            // ***				and 1-Yr Price Target from using a Symbol and
            // ***				the list exDividendDates given a Slice.DateTime.
            // ***				Search for the nearest past VE record and retrieve the VE data
            // ***********************************************************************************
            public void loadVEData(QCAlgorithm algo)
            {
                //algo.Log(" ---- *^*^*^*^*^* loadVEData *^*^*^*^*^* ---- ");

                DateTime sliceTime = algo.CurrentSlice.Time;

                //DateTime sliceTime = algo.CurrentSlice.Time;

                string tickStr = this.uSymbol.Value;

                var biibDates = this.exDividendDates.Where(d => DateTime.Compare(sliceTime.Date, d.VEDate.Date) >= 0 &&
                                                d.ticker == tickStr)
                                                .OrderByDescending(d => d.VEDate);

                var DRs = this.exDividendDates.Where(d => DateTime.Compare(sliceTime.Date, d.VEDate.Date) >= 0 &&
                                                d.ticker == tickStr)
                                                .OrderByDescending(d => d.VEDate);
                DividendRecord nextExDateRec = DRs.FirstOrDefault();
                /*
                foreach(DividendRecord d in DRs.Take(10)){
                    algo.Log($" -- *** -- *** {d.ticker}: {d.VEDate.ToShortDateString()}.");
                }
                foreach (var divRec in biibDates){
                        algo.Log($" {divRec.ticker} : {divRec.VEDate.ToShortDateString()} : {divRec.oneYearPriceTarget.ToString()}");
                }
                */
                ///
                if (nextExDateRec.ticker == "")
                {
                    algo.Log("  -------------  MISSING TICKER IN VEData: " + tickStr);
                    return;
                }
                //algo.Debug($" --- ---- ---- *^*^*^*^*^*  Getting closest VE Entry for {this.uSymbol.Value} on {sliceTime.ToShortDateString()}, next VE-Date is {nextExDateRec.VEDate.ToShortDateString()}");

                // this.exDivdnDate = default(DateTime);
                // this.divdndAmt = 0m;
                // this.divdnFrequency = nextExDateRec.frequency;
                this.dtTst = nextExDateRec.VEDate;
                this.intVERating = nextExDateRec.VERating;
                this.decMomentum = nextExDateRec.momentum;
                this.decOneMonthForecast = nextExDateRec.oneMonthForecast;
                this.decOneYearPriceTarget = nextExDateRec.oneYearPriceTarget;
                this.intMomentumRank = nextExDateRec.momentumRank;
                this.initialTargetEndDate = nextExDateRec.VEDate.AddMonths(9);

                return;



            }

            // **********************   clearLD		**************************************
            // ***  			Use this to find and return the current month's VE Ranking
            // ***				and 1-Yr Price Target from using a Symbol and
            // ***				the list exDividendDates given a Slice.DateTime.
            // ***				Search for the nearest past VE record and retrieve the VE data
            // ***********************************************************************************
            public void clearLD(QCAlgorithm algo)
            {
                if (haltProcessing)
                {
                    //algo.Log("       HALTED IN clearLD");
                }
                this.uSymbol = null;
                this.intVERating = 0;
                this.decMomentum = 0;
                this.decOneMonthForecast = 0;
                this.decOneYearPriceTarget = 0;
                this.intMomentumRank = 0;
                this.initialTargetEndDate = default(DateTime);
                this.dtTst = default(DateTime);
                this.exDivdnDate = default(DateTime);           /// 
                this.divdndAmt = 0m;
                this.decTotalGain = 0m;
                this.intType = 0;                               /// reset to 0 ... Stock
                this.daysRemaining2P = 100;
                this.daysRemainingC = 100;
                this.daysRemainingDiv = 100;
                this.daysRemainingP = 100;
                this.daysRemainingWC = 100;
                //this.SSQRMatrix.Clear();
                //this.divdnFrequency = nextExDateRec.frequency;
            }
            //DateTime sliceTime = algo.CurrentSlice.Time;


            public List<IEXRecord> getIEXDivData(QCAlgorithm algo)
            {
                //  SevenYearDividends.csv :: //2023-11-19
                var csvFile = algo.Download("https://hyperionhedgefundcom-my.sharepoint.com/:x:/g/personal/crmcwilliams_hldfund_com/EZ5Nh1XZwyhIj_jaPTgbvFYBRx1cl8HdtbjksKSNVI09Sg?e=M2AQa7&download=1");

                if (csvFile == null) return null;

                decimal lastDiv = 0;
                bool parsed;
                DateTime exDateResult;

                List<IEXRecord> exRecords = new List<IEXRecord>();

                // want to use Microsoft.VisualBasic.FileIO csv parser but is not available
                // use the system's /cr /lf to parse the file string into lines
                string[] csvLines = csvFile.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
                int i = 0;

                foreach (string csvLine in csvLines)
                {
                    if (i == 0)
                    {
                        i++;
                        continue;       //discard the header row
                    }

                    var values = csvLine.Split(',');   // this file is comma delimited

                    IEXRecord IEXRec = new IEXRecord();

                    try
                    {

                        IEXRec.ticker = values[0];

                        if (values[1] == "Null" | values[1] == "")
                        {
                            IEXRec.divAmt = 0;
                        }
                        else
                        {
                            IEXRec.divAmt = Convert.ToDecimal(values[1]);
                        }

                        parsed = DateTime.TryParse(values[2], out exDateResult);
                        if (!parsed)
                        {
                            IEXRec.exDate = default(DateTime);
                            // continue;
                        }
                        else
                        {
                            IEXRec.exDate = exDateResult;
                        }

                        IEXRec.frequency = values[3];

                        exRecords.Add(IEXRec);
                        i++;
                        //algo.Log("i: " + i.ToString());
                    }
                    catch (Exception e)
                    {
                        algo.Debug($" --- ---- ERROR IN PARSING IEX-VE DATA FILE {e.Message}");
                    }
                }

                return exRecords;

            }

            // **********************   getNextIExDate		**************************************
            // ***  			Use this to find and return the next ex-dividend date from 
            // ***				the list exDividendDates given a Slice.DateTime
            // ***********************************************************************************

            public void getNxtIExDt(string tickStr, QCAlgorithm algo)
            {
                if (haltProcessing)
                {
                    algo.Log("       HALTED IN getNxtIExDt");
                }

                DateTime sliceTime = algo.CurrentSlice.Time;

                bool missingIEXRec = this.IExDates.Any(d => d.ticker == tickStr);

                IEXRecord nextIExDateRec = this.IExDates.Where(d => DateTime.Compare(sliceTime.Date, d.exDate.Date) <= 0 &&
                                                d.ticker == tickStr)
                                                .OrderByDescending(d => d.exDate)
                                                .FirstOrDefault();

                //algo.Debug($" --- ---- ---- *^*^*^*^*^*  Getting Next Ex-Date for {this.uSymbol.Value} on {sliceTime.ToShortDateString()}, next Ex-Date is {nextExDateRec.exDate.ToShortDateString()}");

                if (nextIExDateRec.ticker == "")
                {
                    algo.Log("  -------------  MISSING DIVIDEND TICKER: " + tickStr);
                    return;
                }

                this.exDivdnDate = nextIExDateRec.exDate;
                this.divdndAmt = nextIExDateRec.divAmt;
                this.divdnFrequency = nextIExDateRec.frequency;
                // this.intVERating = nextExDateRec.VERating;
                // this.decMomentum = nextExDateRec.momentum;
                // this.decOneYearPriceTarget = nextExDateRec.oneYearPriceTarget;
                // this.intMomentumRank = nextExDateRec.momentumRank;

                return;
            }


            // **********************   GetFedFundsRates()		 **************************************
            // ***  This function downloads the DFF.csv file from Dropbox and loads it into 
            // ***	a Dictionary<DateTime, interest rate> for each day 
            // ***	this dictionary is used when making a trading decision to calculate the interest
            private Dictionary<DateTime, decimal> GetFedFundsRates(QCAlgorithm algo)
            {
                //var ffFile = algo.Download("https://www.dropbox.com/s/s25jzi5ng47wv4k/DFF.csv?dl=1");
                var ffFile = algo.Download("https://hyperionhedgefundcom-my.sharepoint.com/:x:/g/personal/crmcwilliams_hldfund_com/EfUwqG_Y-etNpNTJCtvhW9QBv20Edx88CTUOUF57gAkPAw?e=cEAOvh&download=1");
                if (ffFile == null) return null;

                Dictionary<DateTime, decimal> ffDict = new Dictionary<DateTime, decimal>();
                string[] ffLines = ffFile.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
                int h = 0;
                foreach (string ffLine in ffLines)
                {
                    if (h == 0) // discard header row
                    {
                        h++;
                        continue;
                    }
                    var vals = ffLine.Split(',');
                    ffDict.Add(DateTime.Parse(vals[0]), Convert.ToDecimal(vals[1]) / 100M);   // convert percentage to decimal
                    h++;
                }
                // these next 2 lines are for debugging only -- 
                //DateTime testFind = DateTime.Parse("02/02/2015 16:30:00");
                //var justDate = testFind.Date;
                return ffDict;
            }

            // **********************   GetDividendDates()		 **************************************
            // ***  This function downloads the DividendDates.csv file from Dropbox and loads it into 
            // ***	a List<DividendRecord>.  The List is used to lookup the next ex-dividend date
            // ***	this list  is used when making a trading decision to calculate the dividend payout
            private List<DividendRecord> GetDividendDates(QCAlgorithm algo)
            {
                // 2023-07-02  -- FIRST BACK TEST WITH TRUELY COMPLETE VE DATA -- ROWS MISSING DIVIDENDS HAVE MOST RECENT PREVIOUS DIVIDEND PULLED FORWARD -- MS SQL SERVER 
                // 2023-08-09  -- FIRST BACK TEST WITH TRUELY COMPLETE VE DATA -- ROWS MISSING DIVIDENDS HAVE MOST RECENT PREVIOUS DIVIDEND PULLED FORWARD -- MS SQL SERVER 
                // var csvFile = algo.Download("https://hyperionhedgefundcom-my.sharepoint.com/:x:/g/personal/crmcwilliams_hldfund_com/ERxfG-pnpPJMgqNnxn5dEMkBCU41MvJhspCc9hnBQYeltg?e=rIUaoE&download=1");
                // 2023-11-19 :: REAL 2016-2023 LOOKUP DATA with dividends pulled forward to get accurately updated TotalGain values
                var csvFile = algo.Download("https://hyperionhedgefundcom-my.sharepoint.com/:x:/g/personal/crmcwilliams_hldfund_com/EZ7RJcMfBUtGpum1dLHo37oBXQBr9FHTS7hT69Ml-HYajA?e=tb2n8G&download=1");
                //algo.Debug("theis");
                if (csvFile == null) return null;

                decimal lastDiv = 0;
                bool parsed;
                decimal VERateResult;
                DateTime exDateResult;
                DateTime VEDateResult;

                List<DividendRecord> dividendDates = new List<DividendRecord>();

                // want to use Microsoft.VisualBasic.FileIO csv parser but is not available
                // use the system's /cr /lf to parse the file string into lines
                string[] csvLines = csvFile.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
                int i = 0;

                foreach (string csvLine in csvLines)
                {
                    if (i == 0)
                    {
                        i++;
                        continue;       //discard the header row
                    }

                    var values = csvLine.Split(',');   // this file is comma delimited

                    DividendRecord divRec = new DividendRecord();


                    try
                    {
                        /*
                        parsed = DateTime.TryParse(values[0], out exDateResult);
                        if (!parsed) {
                            divRec.exDate = default(DateTime);
                            // continue;
                        } else {
                            divRec.exDate = exDateResult;
                        }
                        */

                        parsed = DateTime.TryParse(values[0], out VEDateResult);
                        if (!parsed)
                        {
                            continue;
                        }
                        else
                        {
                            divRec.VEDate = VEDateResult;
                        }

                        divRec.ticker = values[1];

                        if (values[2] == "NULL" | values[2] == "")
                        {
                            divRec.divAmt = 0;
                        }
                        else
                        {
                            divRec.divAmt = Convert.ToDecimal(values[2]);
                        }

   
                        if (values[3] == "NULL" | values[3] == "")
                        {
                            divRec.marketPrice = 0;
                        }
                        else
                        {
                            divRec.marketPrice = Convert.ToDecimal(values[3]);
                        }

                        if (values[4] == "NULL" | values[4] == "")
                        {
                            divRec.VERating = 0;
                        }
                        else
                        {
                            divRec.VERating = Convert.ToInt32(values[4]);
                            //divRec.VERating = Int32.TryParse(values[5], out VERateResult);
                        }

                        if (values[5] == "NULL" | values[5] == "")
                        {
                            divRec.momentum = 0;
                        }
                        else
                        {
                            parsed = Decimal.TryParse(values[5], out VERateResult);
                            if (!parsed)
                            {
                                divRec.momentum = 0;
                            }
                            else
                            {
                                divRec.momentum = VERateResult;
                            }
                        }

                        if (values[6] == "NULL" | values[6] == "")
                        {
                            divRec.momentumRank = 0;
                        }
                        else
                        {
                            divRec.momentumRank = Convert.ToInt32(values[6]);
                        }

                        /*if (string.IsNullOrEmpty(values[7])) {
                                divRec.oneMonthForecat = 0;
                        } else {
                            divRec.oneMonthForecat = Convert.ToDecimal(values[7]);
                        } */

                        if (values[8] == "NULL" | values[8] == "")
                        {
                            divRec.oneYearPriceTarget = 0;
                        }
                        else
                        {
                            divRec.oneYearPriceTarget = Convert.ToDecimal(values[8]);
                        }

                        dividendDates.Add(divRec);
                        /*
                        if (i == 3 | i == 2) {
                            algo.Debug("RealDate  |Ticker|DIV |Mrkt Prc|VE Rat| MOM |Mom Rnk|1 M |1 Yr |1yrFor|Mrkt Cap|TotalGain}");
                            algo.Debug( $"{divRec.VEDate.ToShortDateString()} | {divRec.ticker}  | {divRec.divAmt.ToString()} |  {divRec.marketPrice.ToString()} | {divRec.VERating.ToString()} | {divRec.momentum.ToString()} | {divRec.momentumRank.ToString()} | {divRec.oneMonthForecast.ToString()} | {divRec.oneYearPriceTarget.ToString()}");
                        }
                        */
                        i++;
                        //algo.Log("i: " + i.ToString());

                    }
                    catch (Exception e)
                    {
                        algo.Debug($" --- ---- ERROR IN PARSING IEX-VE DATA FILE {e.Message}");
                    }
                }

                return dividendDates;
            }

            private Dictionary<decimal, decimal> InitializeHaircuts(QCAlgorithm algo)
            {
                Dictionary<decimal, decimal> ibkrHC = new Dictionary<decimal, decimal>();

                ibkrHC.Add(0M, .75M);
                ibkrHC.Add(0.5M, .75M);
                ibkrHC.Add(1M, .75M);
                ibkrHC.Add(1.5M, .75M);
                ibkrHC.Add(2M, .75M);
                ibkrHC.Add(2.5M, .85M);
                ibkrHC.Add(3M, 1M);
                ibkrHC.Add(3.5M, 1.15M);
                ibkrHC.Add(4M, 1.3M);
                ibkrHC.Add(4.5M, 1.65M);
                ibkrHC.Add(5M, 2M);
                ibkrHC.Add(5.5M, 2.2M);
                ibkrHC.Add(6M, 2.4M);
                ibkrHC.Add(6.5M, 2.6M);
                ibkrHC.Add(7M, 2.8M);
                ibkrHC.Add(7.5M, 3M);
                ibkrHC.Add(8M, 3.5M);
                ibkrHC.Add(8.5M, 3.8M);
                ibkrHC.Add(9M, 4M);
                ibkrHC.Add(9.5M, 4.3M);
                ibkrHC.Add(10M, 4.5M);
                ibkrHC.Add(10.5M, 4.8M);
                ibkrHC.Add(11M, 5M);
                ibkrHC.Add(11.5M, 5.3M);
                ibkrHC.Add(12M, 5.5M);
                ibkrHC.Add(12.5M, 5.7M);
                ibkrHC.Add(13M, 6M);
                ibkrHC.Add(13.5M, 6.2M);
                ibkrHC.Add(14M, 6.6M);
                ibkrHC.Add(14.5M, 6.8M);
                ibkrHC.Add(15M, 7M);
                ibkrHC.Add(15.5M, 7.2M);
                ibkrHC.Add(16M, 7.4M);
                ibkrHC.Add(16.5M, 7.6M);
                ibkrHC.Add(17M, 7.8M);
                ibkrHC.Add(17.5M, 8.1M);
                ibkrHC.Add(18M, 8.2M);
                ibkrHC.Add(18.5M, 8.4M);
                ibkrHC.Add(19M, 8.6M);
                ibkrHC.Add(19.5M, 8.8M);
                ibkrHC.Add(20M, 9M);
                ibkrHC.Add(20.5M, 9.2M);
                ibkrHC.Add(21M, 9.4M);
                ibkrHC.Add(21.5M, 9.6M);
                ibkrHC.Add(22M, 9.8M);
                ibkrHC.Add(22.5M, 10.1M);
                ibkrHC.Add(23M, 10.4M);
                ibkrHC.Add(23.5M, 10.7M);
                ibkrHC.Add(24M, 11M);
                ibkrHC.Add(24.5M, 11.4M);
                ibkrHC.Add(25M, 11.8M);
                ibkrHC.Add(25.5M, 12.3M);
                ibkrHC.Add(26M, 12.8M);
                ibkrHC.Add(26.5M, 13.2M);
                ibkrHC.Add(27M, 13.7M);
                ibkrHC.Add(27.5M, 14.2M);
                ibkrHC.Add(28M, 14.7M);
                ibkrHC.Add(28.5M, 15.2M);
                ibkrHC.Add(29M, 15.6M);
                ibkrHC.Add(29.5M, 16.1M);
                ibkrHC.Add(30M, 16.6M);
                ibkrHC.Add(30.5M, 17M);
                ibkrHC.Add(31M, 17.4M);
                ibkrHC.Add(31.5M, 17.8M);
                ibkrHC.Add(32M, 13.4M);
                ibkrHC.Add(32.5M, 18.2M);
                ibkrHC.Add(33M, 18.6M);
                ibkrHC.Add(33.5M, 19M);
                ibkrHC.Add(34M, 19.4M);
                ibkrHC.Add(34.5M, 19.8M);
                ibkrHC.Add(35M, 20.2M);

                return ibkrHC;

            } // end initializeIBKR

            // **********************   getNextExDate		**************************************
            // ***  			Use this to find and return the next ex-dividend date from 
            // ***				the list exDividendDates given a Slice.DateTime
            // ***********************************************************************************

            public void GetNextExDate(QCAlgorithm algo)
            {
                // // /// /// NOTE:  Adjusted this to .Compare(sliceTime, d.exDate <=0) to accommondate BND ex-dates on 1st of month
                // // /// /// NOTE:  This should work because most stocks will be traded before progressing through the month to their ex-div dates
                if (haltProcessing)
                {
                    algo.Log("       HALTED IN getNextExDate");
                }

                DateTime sliceTime = algo.CurrentSlice.Time;

                string tickStr = this.uSymbol.Value;

                DividendRecord nextExDateRec = this.exDividendDates.Where(d => DateTime.Compare(sliceTime.Date, d.VEDate.Date) <= 0 &&
                                                d.ticker == tickStr)
                                                .OrderByDescending(d => d.VEDate)
                                                .FirstOrDefault();

                //algo.Debug($" --- ---- ---- *^*^*^*^*^*  Getting Next Ex-Date for {this.uSymbol.Value} on {sliceTime.ToShortDateString()}, next Ex-Date is {nextExDateRec.exDate.ToShortDateString()}");

                if (nextExDateRec.ticker == "")
                {
                    algo.Log("  -------------  MISSING DIVIDEND TICKER: " + tickStr);
                    return;
                }

                this.exDivdnDate = nextExDateRec.exDate;
                this.divdndAmt = nextExDateRec.divAmt;
                this.divdnFrequency = nextExDateRec.frequency;
                // this.intVERating = nextExDateRec.VERating;
                // this.decMomentum = nextExDateRec.momentum;
                // this.decOneYearPriceTarget = nextExDateRec.oneYearPriceTarget;
                // this.intMomentumRank = nextExDateRec.momentumRank;

                return;
            }

            public void GetNextExDate(string tickStr, QCAlgorithm algo)
            {
                // // /// /// NOTE:  Adjusted this to .Compare(sliceTime, d.exDate <=0) to accommondate BND ex-dates on 1st of month
                // // /// /// NOTE:  This should work because most stocks will be traded before progressing through the month to their ex-div dates
                if (haltProcessing)
                {
                    algo.Log("       HALTED IN getNextExDate");
                }

                DateTime sliceTime = algo.CurrentSlice.Time;

                DividendRecord nextExDateRec = exDividendDates.Where(d => DateTime.Compare(sliceTime.Date, d.VEDate.Date) >= 0 &&
                                                d.ticker == tickStr)
                                                .OrderByDescending(d => d.VEDate)
                                                .FirstOrDefault();

                if (nextExDateRec.ticker == "")
                {
                    algo.Log("  -------------  MISSING DIVIDEND TICKER: " + tickStr);
                    return;
                }

                this.exDivdnDate = nextExDateRec.exDate;
                this.divdndAmt = nextExDateRec.divAmt;
                this.divdnFrequency = nextExDateRec.frequency;

                return;
            }

            public void GetTotalGain(decimal mltplr, decimal stkPrce)
            {
                this.decTotalGain = (this.divdndAmt * mltplr) + this.decOneYearPriceTarget - stkPrce;
                
                return;              /// formula for shorts

            }

        } /// end class lookupData


        public class SymbolData
        {

            public readonly Symbol symbol;
            public Decimal lastEODPrice;
            private QCAlgorithm _algo;

            public SecurityExchangeHours SEH;

            public MomentumPercent thisMOMP_1;
            public MomentumPercent thisMOMP_3;
            public MomentumPercent thisMOMP_6;

            public Dictionary<String, RollingWindow<IndicatorDataPoint>> IndicatorsWindows = new Dictionary<String, RollingWindow<IndicatorDataPoint>>();

            public SimpleMovingAverage smaVolume;                       // Volume Divergence Indicator Inputs
            public StandardDeviation stdVolume;                         //				""
            public BollingerBands bb;                                   // Bollinger Bands Indicator
            public ParabolicStopAndReverse psar;                        // Parabolic Stop and Reverse indicator
            public TrueStrengthIndex tsi;                               // True Strength Index
            public ArnaudLegouxMovingAverage ALMA_fast;                 // ALMA 9 period
            public ArnaudLegouxMovingAverage ALMA_slow;                 // ALMA 18 period

            public decimal bbSignal = 0;                                // running bbSignal for SuperBollingerBand
            public int bbTrigger = 0;                                   // episodic/transient bb trigger for trading
            public decimal psarSignal = 0;
            public decimal tsi_trigger = 0;
            public decimal tsi_weak_trigger = 0;
            public decimal tsi_strong_trigger = 0;
            public decimal almas_crossing = 0;

            public bool smaVolumeWasUpdated = false;
            public bool stdVolumeWasUpdated = false;

            public RollingWindow<IndicatorDataPoint> Closes;            // Rolling window of closes used for BB_Crossover and Trigger Calculation

            public int VDI_Divergence = 0;                              // Volume Divergence Indicator Signals
            public int VDI_Signal = 0;                                  // record last non-zero Bearish or Bullish Divergence
            public bool VDI_VolumeSpike = false;                        //				""
            public bool VDI_VolumeCollapse = false;                     //				""
            public bool VDI_IncreasingVolumeTrend = false;              //				""
            public bool VDI_DecreasingVolumeTrend = false;              //				""
            public decimal VDI_Volume_Bar_0 = 0;                        // 	for diagnostics

            public OnBalanceVolume obv;                                 // OBV Osc Indicator Inputs
            public bool OBVOsc_Bear = false;                            // OBV Osc Indicator Signals
            public bool OBVOsc_Bull = false;                            // 				""
            public bool OBVOsc_HiddenBear = false;                      //				""
            public bool OBVOsc_HiddenBull = false;                      //				""
            public bool OBVOsc_PivotLowFound = false;                   //				""
            public bool OBVOsc_PivotHighFound = false;                  //				""
            public decimal obv_osc = 0;

            public ExponentialMovingAverage ema_obv_short;              //				""
            public ExponentialMovingAverage ema_obv_long;               //				""
            public PivotPointsHighLow pphl_lbR;                         //				""
            public RelativeMovingAverage rma_max_change_close;          // 				""
            public RelativeMovingAverage rma_min_change_close;          //				""
                                                                        //public RateOfChange roc_close;								// 				""

            public Beta thisBeta;
            public TradeBarConsolidator consolidator = new TradeBarConsolidator(390);
            private RollingWindow<TradeBar> _window;

            public decimal decMOMP;
            public bool isRollable;
            public bool isTriggered;
            public bool continueTrade;
            public bool fadeShort;
            public bool killShort;
            public bool targetReversalKill;
            public bool currentPosition;
            public Symbol optSymbol;
            public int intTPRCntr = 0;
            public int intType = 0;                     /// 0=stock, 1=ETF, 2=Bond
            public bool openInterestCheck = false;
            //public List<Symbol> currentOptions = new List<Symbol>();
            public int SSQRFailCnt = 0;
            public decimal divdndAmt = 0;
            public decimal decOneYearPriceTarget_Initial = 0;
            public decimal decOneYearPriceTarget_Current = 0;
            public decimal decOneMonthForecast = 0;
            public DateTime initialTargetEndDate;
            public string VECase = "";
            public System.Data.DataTable VDIData;

            public bool IsTriggered(QCAlgorithm algo)
            {
                
                bool Type1 = (this.bbTrigger == -1 | this.VDI_Signal == -1) & (this.tsi_trigger == -1 & this.almas_crossing == -1);
                bool Type2 = (this.bbTrigger == -1 & this.VDI_Signal == -1) & (this.tsi_trigger == -1 | this.almas_crossing == -1);
                bool Type3 = (this.tsi > 0 & this.tsi_trigger == -1 & this.almas_crossing == -1);
                this.isTriggered = (this.psarSignal == -1 && (Type1 | Type2 | Type3));
                //algo.Log($" -- -- -- -- Check Triggered PSAR: {this.psarSignal} TSI: {this.tsi}  TSITrigger: {this.tsi_trigger} SBB: {this.bbTrigger} VDI: {this.VDI_Signal} ALMA-X {this.almas_crossing} Triggered: {this.isTriggered}");
                return this.isTriggered;
            }

            public bool ContinueTrade(QCAlgorithm algo)
            {
                this.continueTrade = (this.tsi_trigger == -1 && (this.almas_crossing == -1 | this.psarSignal == -1));
                return this.continueTrade;
            }

            public bool FadeShort(QCAlgorithm algo, decimal stkPrc)
            {
                this.fadeShort = stkPrc < 100 || (this.psarSignal == 1 | (this.bbTrigger == 1 && this.VDI_Signal == 1));                    /// 2024-01-14 stkPrice < 120
                return this.fadeShort; 

            }

            public bool TargetReversalKill(QCAlgorithm algo, LookupData thisLD) {
                decimal stkPrc = algo.Securities[this.symbol].Price;
                decimal calc = 0;
                thisLD.uSymbol = this.symbol;
                thisLD.loadVEData(algo);
                this.decOneYearPriceTarget_Current = thisLD.decOneYearPriceTarget;
                if (stkPrc == null || stkPrc == 0) return false;
                calc = ((this.decOneYearPriceTarget_Current-this.decOneYearPriceTarget_Initial)/algo.Securities[this.symbol].Price);
                if ( calc >= .1m) this.targetReversalKill = true; else this.targetReversalKill = false;
                if(thisLD.doDeepTracing) algo.Log($" -- trgtrt -- trgtrt -- trgtrt -- at {algo.Time.ToString("hh:mm:ss.ffffff")} CurrentTrgt: {this.decOneYearPriceTarget_Current.ToString("0.00")} InitialTrgt: {this.decOneYearPriceTarget_Initial.ToString("0.00")}  Percent: {calc.ToString("0.00")}");
                return this.targetReversalKill;
            }

            public bool KillShort(QCAlgorithm algo)
            {
                // algo.Log($" --- KS--- KS at {algo.Time.ToString("hh:mm:ss.ffffff")} The Target Reversal Kill is {this.targetReversalKill}");
                if (this.targetReversalKill) {
                    this.killShort = true;
                } else if ((this.bbTrigger == 1 && !this.continueTrade)) {
                    this.killShort = true;
                } else { 
                    this.killShort = false;
                }
                
                return this.killShort;
            }

            public RollingWindow<TradeBar> Bars;

            public SymbolData(Symbol passedSymbol, bool rollable, bool position, Symbol symbOpt, Symbol spy)
            {
                symbol = passedSymbol;

                isRollable = rollable;
                currentPosition = position;
                optSymbol = symbOpt;
                intTPRCntr = 0;
                thisBeta = new Beta("wee", 20, symbol, spy);

                this.consolidator = new TradeBarConsolidator(390);      // 390 minutes per day
                this.Bars = new RollingWindow<TradeBar>(100);
            }

            public void PostOptionsFilteringCreateIndicators()
            {
                this.Bars = new RollingWindow<TradeBar>(100);
                this.Closes = new RollingWindow<IndicatorDataPoint>(270);
                //this.thisMOMP_1 = new MomentumPercent(21);
                //this.thisMOMP_3 = new MomentumPercent(63);
                //this.thisMOMP_6 = new MomentumPercent(126);
                //this.smaVolume = new SimpleMovingAverage(20);
                //this.stdVolume = new StandardDeviation(20);

                this.VDIData = new System.Data.DataTable();

                this.VDIData.Columns.Add("DateTime", typeof(DateTime));
                this.VDIData.Columns.Add("LowPrice0", typeof(double));
                this.VDIData.Columns.Add("LowPrice1", typeof(double));
                this.VDIData.Columns.Add("HighPrice0", typeof(double));
                this.VDIData.Columns.Add("HighPrice1", typeof(double));
                this.VDIData.Columns.Add("LowVolume0", typeof(double));
                this.VDIData.Columns.Add("LowVolume1", typeof(double));
                this.VDIData.Columns.Add("HighVolume0", typeof(double));
                this.VDIData.Columns.Add("HighVolume1", typeof(double));
                this.VDIData.Columns.Add("LowerLowPrice", typeof(String));
                this.VDIData.Columns.Add("HigherLowVolume", typeof(String));
                this.VDIData.Columns.Add("HigherHighPrice", typeof(String));
                this.VDIData.Columns.Add("LowerHighVolume", typeof(String));
                this.VDIData.Columns.Add("BearishDivergence", typeof(String));
                this.VDIData.Columns.Add("BullishDivergence", typeof(String));

                this.IndicatorsWindows.Add("SMA_VOL", new RollingWindow<IndicatorDataPoint>(270));
                this.IndicatorsWindows.Add("SMA_STD_VOL", new RollingWindow<IndicatorDataPoint>(270));

                this.IndicatorsWindows.Add("OBV_Osc", new RollingWindow<IndicatorDataPoint>(270));

                this.IndicatorsWindows.Add("VDI_Divergence", new RollingWindow<IndicatorDataPoint>(270));
                this.IndicatorsWindows.Add("VDI_Signals", new RollingWindow<IndicatorDataPoint>(270));
 
                //this.IndicatorsWindows.Add("VDI_Volume_Bar_0", new RollingWindow<IndicatorDataPoint>(270));
                this.IndicatorsWindows.Add("BB_Crossover", new RollingWindow<IndicatorDataPoint>(270));
                this.IndicatorsWindows.Add("BB_Signals", new RollingWindow<IndicatorDataPoint>(270));
                this.IndicatorsWindows.Add("BB_UpperValues", new RollingWindow<IndicatorDataPoint>(270));
                this.IndicatorsWindows.Add("BB_LowerValues", new RollingWindow<IndicatorDataPoint>(270));
                this.IndicatorsWindows.Add("BB_KillShorts", new RollingWindow<IndicatorDataPoint>(270));

                this.IndicatorsWindows.Add("PSAR", new RollingWindow<IndicatorDataPoint>(270));

                this.IndicatorsWindows.Add("TSI", new RollingWindow<IndicatorDataPoint>(270));
                this.IndicatorsWindows.Add("TSI_Signal", new RollingWindow<IndicatorDataPoint>(270));
                this.IndicatorsWindows.Add("TSI_Weak_Trigger", new RollingWindow<IndicatorDataPoint>(270));
                this.IndicatorsWindows.Add("TSI_Strong_Trigger", new RollingWindow<IndicatorDataPoint>(270));
                this.IndicatorsWindows.Add("ALMA_fast", new RollingWindow<IndicatorDataPoint>(270));
                this.IndicatorsWindows.Add("ALMA_slow", new RollingWindow<IndicatorDataPoint>(270));
                this.IndicatorsWindows.Add("ALMAS_crosses", new RollingWindow<IndicatorDataPoint>(270));
 
                return;
            }

            public void CheckSMAs(DateTime slcTime, QCAlgorithm algo)
            {
                //DateTime slcTime = algo.CurrentSlice.Time;
                this.IndicatorsWindows["SMA_VOL"].Add(new IndicatorDataPoint(slcTime, this.smaVolume));
                this.IndicatorsWindows["SMA_STD_VOL"].Add(new IndicatorDataPoint(slcTime, this.stdVolume));
                this.Closes.Add(new IndicatorDataPoint(slcTime, this.Bars[0].Close));

                this.CalculateVDI_Stats(slcTime, algo, false);
                this.stdVolumeWasUpdated = false;
                this.stdVolumeWasUpdated = false;

                return;

            }

            public void CheckOBV(DateTime slcTime, QCAlgorithm algo)
            {
                //    DateTime slcTime = algo.CurrentSlice.Time;
                try
                {
                    this.ema_obv_long.Update(this.obv.Current);
                    this.ema_obv_short.Update(this.obv.Current);
                    this.obv_osc = this.ema_obv_short.Current - this.ema_obv_long;
                    this.IndicatorsWindows["OBV_Osc"].Add(new IndicatorDataPoint(slcTime, this.obv_osc));
                }
                catch (Exception e)
                {
                    algo.Log($" CheckOBV Error:  {e}");
                }
            }


            public void Calculate_BB_Crossovers(DateTime barTime, QCAlgorithm algo)
            {
                //DateTime slcTime = algo.CurrentSlice.Time;
                bool crosses = false;

                if (this.IndicatorsWindows["BB_Signals"].Count() < 2)
                {
                    this.IndicatorsWindows["BB_Crossover"].Add(new IndicatorDataPoint(barTime, 0));
                    this.IndicatorsWindows["BB_KillShorts"].Add(new IndicatorDataPoint(barTime, 0));

                    this.bbSignal = (this.bb.UpperBand + this.bb.LowerBand) / 2m;
                    this.IndicatorsWindows["BB_Signals"].Add(new IndicatorDataPoint(barTime, this.bbSignal));

                    this.IndicatorsWindows["BB_UpperValues"].Add(new IndicatorDataPoint(barTime, 0));
                    this.IndicatorsWindows["BB_LowerValues"].Add(new IndicatorDataPoint(barTime, 0));
                    return;
                }

                try
                {
                    if (this.Bars[0].Close > this.bbSignal)
                    {
                        this.bbSignal = Math.Max(this.bbSignal, this.bb.LowerBand);
                    }
                    else if (this.Bars[0].Close < this.bbSignal)
                    {
                        this.bbSignal = Math.Min(this.bbSignal, this.bb.UpperBand);
                    }
                    this.IndicatorsWindows["BB_Signals"].Add(new IndicatorDataPoint(barTime, this.bbSignal));


                    if (this.Bars[1].Close > this.IndicatorsWindows["BB_Signals"][1] & this.Bars[0].Close < this.bbSignal)
                    {
                        this.bbTrigger = -1;
                        //algo.Log($" -- BBB -- BBB -- BBB -- BBB -- BBTrigger: {this.bbTrigger} Close: {this.Bars[0].Close.ToString("0.00")} Signal: {this.bbSignal.ToString("0.00")} ");
                        this.IndicatorsWindows["BB_Crossover"].Add(new IndicatorDataPoint(barTime, this.bbTrigger));
                    }
                    else if ( this.Bars[1].Close < this.IndicatorsWindows["BB_Signals"][1] & this.Bars[0].Close > this.bbSignal)
                    {
                        this.bbTrigger = 1;
                        //algo.Log($" -- BBB -- BBB -- BBB -- BBB -- BBTrigger: {this.bbTrigger} Close: {this.Bars[0].Close.ToString("0.00")} Signal: {this.bbSignal.ToString("0.00")} ");
                        this.IndicatorsWindows["BB_Crossover"].Add(new IndicatorDataPoint(barTime, this.bbTrigger));
                    }
                    else {
                        //this.bbTrigger = 0;
                        //algo.Log($" -- BBB -- BBB -- BBB -- BBB -- BBTrigger: {this.bbTrigger} Close: {this.Bars[0].Close.ToString("0.00")} Signal: {this.bbSignal.ToString("0.00")} ");
                        this.IndicatorsWindows["BB_Crossover"].Add(new IndicatorDataPoint(barTime, 0));
                    }                    

                    this.IndicatorsWindows["BB_UpperValues"].Add(new IndicatorDataPoint(barTime, this.bb.UpperBand));
                    this.IndicatorsWindows["BB_LowerValues"].Add(new IndicatorDataPoint(barTime, this.bb.LowerBand));

                    bool foo = this.ContinueTrade(algo);
                    foo = this.KillShort(algo);
                
                    this.IndicatorsWindows["BB_KillShorts"].Add(new IndicatorDataPoint(barTime, this.killShort ? 1 : 0 ));

                }
                catch (Exception e)
                {
                    algo.Debug($" Calculate_BB_Crossover Error {e}");
                    return;
                }
            }

            public void Calculate_TSI_Crossovers(DateTime slcTime, QCAlgorithm algo)
            {
                try
                {

                    this.IndicatorsWindows["TSI"].Add(new IndicatorDataPoint(slcTime, this.tsi.Current));
                    this.IndicatorsWindows["TSI_Signal"].Add(new IndicatorDataPoint(slcTime, this.tsi.Signal));

                    if (this.IndicatorsWindows["TSI_Signal"].Count < 2)
                    {
                        this.IndicatorsWindows["TSI_Weak_Trigger"].Add(new IndicatorDataPoint(slcTime, 0m));
                        this.IndicatorsWindows["TSI_Strong_Trigger"].Add(new IndicatorDataPoint(slcTime, 0m));
                        return;
                    }
                    RollingWindow<IndicatorDataPoint> TSIWin = this.IndicatorsWindows["TSI"];
                    RollingWindow<IndicatorDataPoint> TSISigWin = this.IndicatorsWindows["TSI_Signal"];

                    var TSI_Sig_Crossings = TSIWin.Skip(1).Zip(TSIWin, (aPrev, a) => new { Prev = aPrev, Current = a })
                        .Zip(TSISigWin.Skip(1).Zip(TSISigWin, (bPrev, b) => new { Prev = bPrev, Current = b }),
                            (a, b) => new { TSIWin = a, TSISigWin = b })
                        .Where(pair => (pair.TSIWin.Current > pair.TSISigWin.Current && pair.TSIWin.Prev < pair.TSISigWin.Prev) ||
                            (pair.TSIWin.Current < pair.TSISigWin.Current && pair.TSIWin.Prev > pair.TSISigWin.Prev))
                        .Select((pair, index) => 
                        {
                            int crossingValue = (pair.TSIWin.Current > pair.TSISigWin.Current) ? 1 : -1;
                            return new { Index = index, CrossingMSValues = crossingValue, IndyValue = pair.TSIWin.Current };
                        })
                        .OrderBy(item => item.Index);

                    var mostRecentTSI_SigCrossing = TSI_Sig_Crossings.FirstOrDefault();
                    if (!TSI_Sig_Crossings.IsNullOrEmpty()) {
                        int TSI_Sig_CrossingValue = mostRecentTSI_SigCrossing.CrossingMSValues;
                        decimal TSI_SIG_IndyValue = mostRecentTSI_SigCrossing.IndyValue;

                        this.tsi_trigger = TSI_Sig_CrossingValue == -1 ? -1 : TSI_Sig_CrossingValue == 1 ? 1 : 0;
                    }

                    if (this.IndicatorsWindows["TSI"][1] > 0)
                    {
                        if (this.IndicatorsWindows["TSI"][1] > this.IndicatorsWindows["TSI_Signal"][1] & this.IndicatorsWindows["TSI"][0] < this.IndicatorsWindows["TSI_Signal"][0]) this.tsi_weak_trigger = -1; else this.tsi_weak_trigger = 0;
                        if (this.IndicatorsWindows["TSI"][0] < 0) this.tsi_strong_trigger = -2; else this.tsi_strong_trigger = 0;

                    }
                    else if (this.IndicatorsWindows["TSI"][1] < 0)
                    {
                        if (this.IndicatorsWindows["TSI"][1] < this.IndicatorsWindows["TSI_Signal"][1] & this.IndicatorsWindows["TSI"][0] > this.IndicatorsWindows["TSI_Signal"][0]) this.tsi_weak_trigger = 1; else this.tsi_weak_trigger = 0;
                        if (this.IndicatorsWindows["TSI"][0] > 0) this.tsi_strong_trigger = 2; else this.tsi_strong_trigger = 0;

                    }

                    this.IndicatorsWindows["TSI_Weak_Trigger"].Add(new IndicatorDataPoint(slcTime, this.tsi_weak_trigger));
                    this.IndicatorsWindows["TSI_Strong_Trigger"].Add(new IndicatorDataPoint(slcTime, this.tsi_strong_trigger));
                }
                catch (Exception e)
                {
                    algo.Debug($" Calculate_TSI_Crossover Error {e}");
                    return;
                }
            }

           public void Calculate_ALMA_Crossovers(DateTime slcTime, QCAlgorithm algo)
            {
                try
                {

                    this.IndicatorsWindows["ALMA_fast"].Add(new IndicatorDataPoint(slcTime, this.ALMA_fast.Current.Price));
                    this.IndicatorsWindows["ALMA_slow"].Add(new IndicatorDataPoint(slcTime, this.ALMA_slow.Current.Price));

                    if (this.IndicatorsWindows["ALMA_slow"].Count < 2)
                    {
                        this.IndicatorsWindows["ALMAS_crosses"].Add(new IndicatorDataPoint(slcTime, 0m));
                        return;
                    }
                    RollingWindow<IndicatorDataPoint> ALMAFastWin = this.IndicatorsWindows["ALMA_fast"];
                    RollingWindow<IndicatorDataPoint> ALMASlowWin = this.IndicatorsWindows["ALMA_slow"];

                    var ALMA_fast_slow_Crossings = ALMAFastWin.Skip(1).Zip(ALMAFastWin, (aPrev, a) => new { Prev = aPrev, Current = a })
                        .Zip(ALMASlowWin.Skip(1).Zip(ALMASlowWin, (bPrev, b) => new { Prev = bPrev, Current = b }),
                            (a, b) => new { ALMAFastWin = a, ALMASlowWin = b })
                        .Where(pair => (pair.ALMAFastWin.Current > pair.ALMASlowWin.Current && pair.ALMAFastWin.Prev < pair.ALMASlowWin.Prev) ||
                            (pair.ALMAFastWin.Current < pair.ALMASlowWin.Current && pair.ALMAFastWin.Prev > pair.ALMASlowWin.Prev))
                        .Select((pair, index) => 
                        {
                            int crossingValue = (pair.ALMAFastWin.Current > pair.ALMASlowWin.Current) ? 1 : -1;
                            return new { Index = index, CrossingMSValues = crossingValue, IndyValue = pair.ALMAFastWin.Current };
                        })
                        .OrderBy(item => item.Index);

                    var mostRecentALMA_fast_slow_Crossing = ALMA_fast_slow_Crossings.FirstOrDefault();
                    if (!ALMA_fast_slow_Crossings.IsNullOrEmpty()) {
                        int ALMA_fast_slow_CrossingValue = mostRecentALMA_fast_slow_Crossing.CrossingMSValues;
                        this.almas_crossing = ALMA_fast_slow_CrossingValue;
                    }

                    int ALMACross = 0;
                    if(ALMAFastWin[1] < ALMASlowWin[1] && ALMAFastWin[0] > ALMASlowWin[0]) ALMACross = 1;
                    if(ALMAFastWin[1] > ALMASlowWin[1] && ALMAFastWin[0] < ALMASlowWin[0]) ALMACross = -1;
                    
                    this.IndicatorsWindows["ALMAS_crosses"].Add(new IndicatorDataPoint(slcTime, (decimal)ALMACross));
                    this.continueTrade = this.ContinueTrade(algo);
                }
                catch (Exception e)
                {
                    algo.Debug($" Calculate_ALMA_Crossover Error {e}");
                    return;
                }
            }

            public void Calculate_PSAR(DateTime slcTime, QCAlgorithm algo)
            {
                try
                {
                    decimal avgLast5Closes = this.Bars.Take(5).Average(b => b.Close);
                    this.psarSignal = this.psar > avgLast5Closes ? -1 : this.psar < avgLast5Closes ? 1 : 0;             /// PSAR > avgLast5Closes == Bearish 
                    this.IndicatorsWindows["PSAR"].Add(new IndicatorDataPoint(slcTime, this.psar));
                }
                catch (Exception e)
                {
                    algo.Debug($" Calculate_PSAR Error {3}");
                    return;
                }

            }

            public void CalculateVDI_Stats(DateTime slcTime, QCAlgorithm algo, bool halt)
            {
                if (halt)
                {
                    int i = 0;
                }

                bool BullishDivergence = false;
                bool BearishDivergence = false;
                decimal VDI_Value = 0;

                int cntr = 0;
                bool VolIncreasingTrend = false;
                bool VolDecreasingTrend = false;
                int _bullSensitivity = 3;
                int _bearSensitivity = 4;
                int _maLen = 20;
                decimal _spikeMultiplier = 3m;
                decimal _contractionMultiplier = 1m;
                int _trndSensitivity = 5;


                if (this.smaVolume != null & this.stdVolume != null)
                {
                    decimal upperDeviationVolume = this.smaVolume + this.stdVolume;
                    decimal lowerDeviationVolume = this.smaVolume - this.stdVolume;
                }
                else
                {
                    return;
                }

                int priceLookback = 20;

                if (this.Bars.Count < priceLookback * _bullSensitivity)
                {
                    return;
                }

                decimal LowPrice0 = this.Bars.Take(priceLookback).Min(p => p.Low);
                decimal LowPrice1 = this.Bars.Take(priceLookback * _bullSensitivity).Min(p => p.Low);
                bool LowerLowPrice_1 = this.Bars.Take(priceLookback + 1).Skip(1).Min(p => p.Low) == this.Bars.Take((priceLookback * _bullSensitivity) + 1).Skip(1).Min(p => p.Low);
                bool LowerLowPrice = LowPrice0 == LowPrice1;

                decimal HighPrice0 = this.Bars.Take(priceLookback).Max(p => p.High);
                decimal HighPrice1 = this.Bars.Take(priceLookback * _bearSensitivity).Max(p => p.High);
                bool HigherHighPrice = HighPrice0 == HighPrice1;
                bool HigherHighPrice_1 = this.Bars.Take(priceLookback + 1).Skip(1).Max(p => p.High) == this.Bars.Take((priceLookback * _bearSensitivity) + 1).Skip(1).Max(p => p.High);

                try
                {
                    decimal highVol0 = this.Bars.Take(priceLookback).Skip(0).Max(p => p.Volume);
                    decimal highVol1 = this.Bars.Take(priceLookback * _bearSensitivity).Skip(0).Max(p => p.Volume);

                    decimal highVol0_1 = this.Bars.Take(priceLookback + 1).Skip(1).Max(p => p.Volume);
                    decimal highVol1_1 = this.Bars.Take((priceLookback * _bearSensitivity) + 1).Skip(1).Max(p => p.Volume);

                    bool volumeLowerHighs = highVol0 < highVol1;
                    bool volumeLowerHighs_1 = highVol0_1 < highVol1_1;

                    BearishDivergence = HigherHighPrice & volumeLowerHighs & !(HigherHighPrice_1 & volumeLowerHighs_1);

                    decimal lowVol0 = this.Bars.Take(priceLookback).Skip(0).Min(p => p.Volume);
                    decimal lowVol1 = this.Bars.Take(priceLookback * _bullSensitivity).Skip(0).Min(p => p.Volume);

                    decimal lowVol0_1 = this.Bars.Take(priceLookback + 1).Skip(1).Min(p => p.Volume);
                    decimal lowVol1_1 = this.Bars.Take((priceLookback * _bullSensitivity) + 1).Skip(1).Min(p => p.Volume);

                    bool volumeHigherLows = lowVol0 > lowVol1;
                    bool volumeHigherLows_1 = lowVol0_1 > lowVol1_1;

                    BullishDivergence = LowerLowPrice & volumeHigherLows & !(LowerLowPrice_1 & volumeHigherLows_1);

                    this.VDI_Divergence = BullishDivergence ? 1 : BearishDivergence ? -1 : 0;
                    this.IndicatorsWindows["VDI_Divergence"].Add(new IndicatorDataPoint(slcTime, this.VDI_Divergence));
                    
                    if (this.VDI_Divergence != 0)
                    {
                        if (BullishDivergence) VDI_Value = this.bb.UpperBand; this.VDI_Signal = 1;
                        if (BearishDivergence) VDI_Value = this.bb.LowerBand; this.VDI_Signal = -1;                     //  maintain VDI_Signal as either 1 or -1
                    }
                    this.IndicatorsWindows["VDI_Signals"].Add(new IndicatorDataPoint(slcTime, VDI_Value));              /// record when VDI_Signals occur 

                    this.VDI_Volume_Bar_0 = this.Bars[0].Volume;

                    /*
                    cntr = 0;
                    this.VDI_IncreasingVolumeTrend = false;
                    if (this.Bars.Any(tb => tb.Volume < this.smaVolume)) {
                        foreach (TradeBar tb in this.Bars) {
                            if (tb.Volume > this.smaVolume) {
                                cntr += 1;
                                if (cntr > _trndSensitivity) {
                                    this.VDI_IncreasingVolumeTrend = true;
                                    break;
                                }
                            }
                        }
                    }

                    this.VDI_DecreasingVolumeTrend = false;
                    if (!VolIncreasingTrend & this.Bars.Any(tb => tb.Volume > this.smaVolume) ) {
                        cntr = 0;
                        foreach (TradeBar tb in this.Bars) {
                            if (tb.Volume < this.smaVolume) {
                                cntr += 1;
                                if(cntr < _trndSensitivity) {
                                    this.VDI_DecreasingVolumeTrend = true;
                                    break;
                                }
                            }
                        }

                    }
                    */
                    System.Data.DataRow dr = this.VDIData.NewRow();

                    dr["DateTime"] = slcTime;
                    dr["LowPrice0"] = LowPrice0;
                    dr["LowPrice1"] = LowPrice1;
                    dr["HighPrice0"] = HighPrice0;
                    dr["HighPrice1"] = HighPrice1;
                    dr["LowVolume0"] = lowVol0;
                    dr["LowVolume1"] = lowVol1;
                    dr["HighVolume0"] = highVol0;
                    dr["HighVolume1"] = highVol1;
                    dr["LowerLowPrice"] = LowerLowPrice ? "Yes" : "No"; ;            /// bullish
                    dr["HigherLowVolume"] = HigherHighPrice ? "Yes" : "No";         /// bearish
                    dr["HigherHighPrice"] = volumeHigherLows ? "Yes" : "No"; ;       /// bullish
                    dr["LowerHighVolume"] = volumeLowerHighs ? "Yes" : "No"; ;       /// bearish

                    dr["BullishDivergence"] = BullishDivergence ? "YES" : "NO";
                    dr["BearishDivergence"] = BearishDivergence ? "YES" : "NO";

                    this.VDIData.Rows.Add(dr);
                }
                catch (Exception e)
                {
                    algo.Log($" CalculateVDI_Stats Error {e}");
                    return;
                }
            }

            public decimal GetAvgMomP(decimal stkPrc)
            {
                this.decMOMP = (this.thisMOMP_1.Current + this.thisMOMP_3.Current + this.thisMOMP_6.Current) / 3m;
                this.decOneMonthForecast = this.decOneYearPriceTarget_Current = this.decOneYearPriceTarget_Initial = (1m + this.decMOMP / 100m) * stkPrc;
                return this.decMOMP;
            }

            public void UpdateBeta(TradeBar symbolTradeBar, TradeBar spyTradeBar)
            {
                thisBeta.Update(symbolTradeBar);
                thisBeta.Update(spyTradeBar);
                //_window.Add(symbolTradeBar);
            }
        }  // end class SymbolData
    }
}
#region imports
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Globalization;
    using System.Drawing;
    using QuantConnect;
    using QuantConnect.Algorithm.Framework;
    using QuantConnect.Algorithm.Framework.Selection;
    using QuantConnect.Algorithm.Framework.Alphas;
    using QuantConnect.Algorithm.Framework.Portfolio;
    using QuantConnect.Algorithm.Framework.Execution;
    using QuantConnect.Algorithm.Framework.Risk;
    using QuantConnect.Parameters;
    using QuantConnect.Benchmarks;
    using QuantConnect.Brokerages;
    using QuantConnect.Util;
    using QuantConnect.Interfaces;
    using QuantConnect.Algorithm;
    using QuantConnect.Indicators;
    using QuantConnect.Data;
    using QuantConnect.Data.Consolidators;
    using QuantConnect.Data.Custom;
    using QuantConnect.DataSource;
    using QuantConnect.Data.Fundamental;
    using QuantConnect.Data.Market;
    using QuantConnect.Data.UniverseSelection;
    using QuantConnect.Notifications;
    using QuantConnect.Orders;
    using QuantConnect.Orders.Fees;
    using QuantConnect.Orders.Fills;
    using QuantConnect.Orders.Slippage;
    using QuantConnect.Scheduling;
    using QuantConnect.Securities;
    using QuantConnect.Securities.Equity;
    using QuantConnect.Securities.Future;
    using QuantConnect.Securities.Option;
    using QuantConnect.Securities.Forex;
    using QuantConnect.Securities.Crypto;
    using QuantConnect.Securities.Interfaces;
    using QuantConnect.Storage;
    using QuantConnect.Data.Custom.AlphaStreams;
    using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
    using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
#endregion


namespace QuantConnect.Algorithm.CSharp {
	public partial class CollarAlgorithm : QCAlgorithm
	{
   		// **********************   GetETFMatrix	  *******************************************
	    // ***  			This code will buy the ETF and the 10-15%OTM Put and Sell the Call 
	    // ***				closest to the current Stock Price plus the Avgerage MOMP
	    // ******************************************************************************************
		public bool GetETFMatrix(ref LookupData LUD, bool firstTime){
            try{
			LUD.clearLD(this);
			LUD.SSQRMatrix.Clear();
            LUD.intType = 1;
			//LUD.uSymbol = SD.symbol;

            OrderTicket etfTicket;
            OrderTicket putTicket;
            OrderTicket callTicket;

			Slice slc = CurrentSlice;
			if (LUD.doTracing) Log($" -- PUTETFON PUTETFON {LUD.uSymbol.Value} PUTETFON PUTETFON --");

			decimal stockPrice = LUD.stockPrice;
			
			OptionChain allUnderlyingOptions = null;	// chain opbjec to get all contracts

			OptionContract putContract;					// contract object to collect put greeks
			OptionContract callContract;				// contract object to collect call greeks
			//OptionContract wcContract;				// contract object to collect wing call greeks

			Greeks putGreeks;
			Greeks callGreeks;
			Greeks wcGreeks;

        	DateTime tradeDate = slc.Time;		// current date, presumed date of trade
			//DateTime targetExpiration = GetLastOptionsExpiry(tradeDate.AddMonths(1));
            DateTime targetExpiration = FindDay(tradeDate.AddMonths(1).Year, tradeDate.AddMonths(1).Month, DayOfWeek.Friday, 4);
            if (targetExpiration.Month == targetExpiration.AddDays(7).Month) targetExpiration = targetExpiration.AddDays(7);

            LUD.decOneYearPriceTarget = etfDataBySymbol[LUD.uSymbol].decOneYearPriceTarget_Initial;
            decimal decMOMP = etfDataBySymbol[LUD.uSymbol].decMOMP;

			Debug($" ETF ETF ETF -- The 1Yr Price for {LUD.uSymbol.Value} is {stockPrice.ToString("0.00")} with MOMP: {decMOMP.ToString("0.00")} yielding a target of {LUD.decOneYearPriceTarget.ToString("0.00")}");

			decimal estTrgtPutStrk = .95M * stockPrice;
			decimal estTrgtCallStrk = .95M * LUD.decOneYearPriceTarget;

			foreach(var chain in slc.OptionChains.Values){
        		if (!chain.Underlying.Symbol.Equals(LUD.uSymbol)) { continue; }
        		allUnderlyingOptions = chain;
        		break;
        	}
        	
        	
            if (allUnderlyingOptions == null) {
				if (LUD.doTracing) Debug($" -- PUTETFON PUTETFON PUTETFON No options returned at {slc.Time} for {LUD.uSymbol.Value} ");
				return false;        	// return null SSQRMatrix and pass control back to OnData()
        	}
            
			var putChains = allUnderlyingOptions.Where(o=>o.Right == OptionRight.Put &
                                                    DateTime.Compare(o.Expiry,targetExpiration)<=0 &
													o.Strike <= estTrgtPutStrk)
                                                    .OrderByDescending(o=>o.Expiry)
													.ThenByDescending(o=>Math.Abs(estTrgtPutStrk-o.Strike));
													

			var callChains = allUnderlyingOptions.Where(o=>o.Right == OptionRight.Call &
                                                    DateTime.Compare(o.Expiry,targetExpiration)<=0 &
													o.Strike >= estTrgtCallStrk &
                                                    o.BidPrice >= 0.20m)
													.OrderByDescending(o=>o.Expiry)
                                                    .ThenByDescending(o=>Math.Abs(estTrgtCallStrk-o.Strike));

            //Debug($" -- PUTETFON PUTETFON -- TARGET EXPIRATION IS {targetExpiration.ToString()}.   There are {putChains.Count()} puts and {callChains.Count()} calls.");
           
            //foreach( var chain in allUnderlyingOptions){
            //    Debug($" -- PUTETFON PUTETFON -- OPTION IS: {chain.Symbol.Value}");
            //}
            
            putContract = putChains.FirstOrDefault();
            callContract = callChains.FirstOrDefault();

            //if (putContract == null | callContract == null) {
            if (putContract == null) {
                if(LUD.doTracing) Debug($" -- PUTETFON Failed to get PUT for {LUD.uSymbol.Value} @ {stockPrice.ToString()}  with Put Target={estTrgtPutStrk.ToString()}");                  //and Call Target={estTrgtCallStrk.ToString()}.");
                return false;
            }

            if (LUD.uSymbol.Value=="SH") {
                foreach( var chain in putChains){
                    if(LUD.doTracing) Debug($" -- PUTETFON PUTETFON -- PUTETFON PUTETFON PUT IS: {chain.Symbol.Value}");
                }
                if(LUD.doTracing) Debug("-- PUTETFON PUTETFON -- PUTETFON PUTETFON -- PUTETFON PUTETFON -- PUTETFON PUTETFON ");
                foreach( var chain in callChains){
                    if(LUD.doTracing) Debug($" -- PUTETFON PUTETFON -- PUTETFON PUTETFON CALL IS: {chain.Symbol.Value}");
                }
            }
            if(LUD.doTracing) Debug($" -- PUTETFON PUTETFON -- PUTETFON PUTETFON PUT: {putContract.Symbol.Value}.");
            if(LUD.doTracing && callContract!=null) Debug($" -- PUTETFON PUTETFON -- PUTETFON PUTETFON CALL: {callContract.Symbol.Value}.");

			SSQRColumn thisSSQRColumn = buildSSQRColumn(putContract, callContract, this, LUD);
            
			if (thisSSQRColumn != null) 
			{
				LUD.SSQRMatrix.Add(thisSSQRColumn);
			} else if(LUD.doTracing) Debug($" -- PUTETFON PUTETFON PUTETFON Failed to get ETF SSSQRColumn for {LUD.uSymbol.Value}.");

			var orderedSSQRMatrix = LUD.SSQRMatrix.OrderBy(s => s.stockPrice);
			IterateOrderedSSQRMatrix(orderedSSQRMatrix);

			if (!firstTime) return !firstTime;                                          // if not first time, then return TRUE so the LUD.SSQRMatrix will be used in subsequent procdessing
            
            TradePerfRec thisNewCollar = new TradePerfRec();							// create a tradePerfRec #1 for the puts sold, solely to log their P/L (including underlying unrealized P/L).
			
			etfTicket = MarketOrder(LUD.uSymbol, 1000);
            if (etfTicket.Status == OrderStatus.Filled)
            {
                didTheTrade = true;
                
                thisNewCollar.strtngCndtn = "INITIAL COLLAR";
                thisNewCollar.isOpen = true;
                thisNewCollar.isInitializer = true;
                thisNewCollar.tradeRecCount = 0;;
                thisNewCollar.index = etfDataBySymbol[LUD.uSymbol].intTPRCntr += 1;
                thisNewCollar.startDate = slc.Time;
                thisNewCollar.expDate = putContract.Expiry;
                thisNewCollar.thetaExpiration = callContract!=null ? callContract.Expiry : default;
                thisNewCollar.uSymbol = LUD.uSymbol;
                thisNewCollar.cSymbol = callContract.Symbol;
                thisNewCollar.pSymbol = putContract.Symbol;		
                //thisNewCollar.wcSymbol = tradableWCall;
                thisNewCollar.uStartPrice = etfTicket.AverageFillPrice;
                thisNewCollar.pStrike = putContract.Strike;
                thisNewCollar.cStrike = callContract!=null ? callContract.Strike : 0;
                //thisNewCollar.wcStrike = thisSSQRColumn.wCallStrike;
                thisNewCollar.uQty = (int)etfTicket.QuantityFilled;
                thisNewCollar.ROR = thisSSQRColumn.ROR;
                thisNewCollar.ROC = thisSSQRColumn.ROC;
                thisNewCollar.CCOR = thisSSQRColumn.CCOR;
                thisNewCollar.RORThresh = RORThresh;
                thisNewCollar.ROCThresh = ROCThresh;
                thisNewCollar.CCORThresh = CCORThresh;
                thisNewCollar.tradeCriteria = "ETF";
                //thisNewCollar.stockADX  = 0;						//lastAdx;
                //thisNewCollar.stockADXR = 0;						//lastAdxr;
                //thisNewCollar.stockOBV = 0;						//lastObv;
                //thisNewCollar.stockAD = lastAd;
                //thisNewCollar.stockADOSC = lastAdOsc;
                //thisNewCollar.stockSTO = lastSto;
                //thisNewCollar.stockVariance = lastVariance;
                thisNewCollar.SSQRnetProfit = etfTicket.QuantityFilled * thisSSQRColumn.netIncome;
                //thisNewCollar.VERating = LUD.intVERating;
                //thisNewCollar.momentum = LUD.decMomentum;
                //thisNewCollar.oneYearPriceTarget = LUD.decOneYearPriceTarget; 
                //thisNewCollar.momentumRank = LUD.intMomentumRank;


                doTheTrade = true;
                /*if (thisNewCollar.cStrike < thisNewCollar.uStartPrice) {
                    
                    var limitPrice = (Securities[tradableCall].AskPrice - Securities[tradableCall].BidPrice) / 2M;	// get the mid point for the limit price
                    var callTicket = LimitOrder(tradableCall, -optionsToTrade, limitPrice);							// sell limit order
                    thisNewCollar.cQty = -(int)optionsToTrade;
                    OpenLimitOrder oLO = new OpenLimitOrder();
                    oLO.oTicket = callTicket;
                    oLO.tpr = thisNewCollar;
                    oLO.oRight = OptionRight.Call;
                    oLOs.Add(oLO);
                    //if (closePutTicket.Status == OrderStatus.Submitted) oldTradeRec.pEndPrice = limitPrice;
                } else { */
                if (callContract!=null ) {
                    callTicket = MarketOrder(callContract.Symbol, -10);
                    if (callTicket.Status == OrderStatus.Filled)
                    {
                        thisNewCollar.cStartPrice = callTicket.AverageFillPrice;
                        thisNewCollar.cQty = (int)callTicket.QuantityFilled;
                    }
                }

                putTicket = MarketOrder(putContract.Symbol, 10);
                if (putTicket.Status == OrderStatus.Filled)
                {
                    thisNewCollar.pStartPrice = putTicket.AverageFillPrice;
                    thisNewCollar.pQty = (int)putTicket.QuantityFilled;
                }
                
                doTheTrade = true;
                
                ETFRecs.Add(thisNewCollar);
                if (doTracing) Log("-- PUTETFON PUTETFON PUTETFON --  ADDING AN ETFPR ");
                return true;
            }     
            } catch (Exception excp){
                Debug($" PUTETFON ERROR {excp}");
            }
			return false;
		}
		


    }
}
#region imports
    using System;
	using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Drawing;
    using QuantConnect.Util;
    using QuantConnect.Indicators;
    using QuantConnect.Data;
    using QuantConnect.Data.Fundamental;
    using QuantConnect.Data.Market;
    using QuantConnect.Data.UniverseSelection;
	using QuantConnect.Data.Consolidators;
    using QuantConnect.Orders;
    using QuantConnect.Securities;
    using QuantConnect.Securities.Option;
#endregion

namespace QuantConnect.Algorithm.CSharp
{
    public partial class CollarAlgorithm : QCAlgorithm
    {
		int SecAddedCnt = 0;
		int SecRemvdCnt = 0;
		
		static Universe ourUniverse;
		public bool badDtParameter;			// get this from the parameters for debugging
		public bool haltProcessing = false;	// use this to trap ERROR
		public bool doTracing = false;		// turn //if (doTracing) Log() process tracing on/off
		public bool logPortfolio = false;	// for tracking portfolio changes
		public bool doDeepTracing = false;		// turn //if (doDeepTracing) Log() process tracing on/off

		public bool addedStocks = false;
		
		OrderTicket closeCallTicket;		// use this to track and manage collar rolling and killing trades
		OrderTicket closePutTicket;			// use this to track and manage collar rolling and killing trades
		OrderTicket closeWCallTicket;		// use this to track and manage collar rolling and killing trades
		List<OpenLimitOrder> oLOs = new List<OpenLimitOrder>();			// maintain a list of open limit orders to manage
		
		List<Symbol> SymbolsToRemove = new List<Symbol>();	 
		
		
		decimal stockDollarValue;			// get the dollar value for the position
		decimal sharesToBuy = 1000;				// Get the number shares to achieve the value
		bool hasDividends = true;			// Bool set (unset=false) to determine whether to add security to portfolio
		decimal optionsToTrade;				// Get the initial Options to trade (1/10th the sharesToBuy)		
		decimal decOIThresh = 0;			// Threshold for Open Interest used to eliminate stocks
		decimal decThisOI = 0;				// this underlying's options OI for ATM front month
		
		decimal vix;						// used to track and log vix values
	
		bool doTheTrade = false;			// Used to allow trades the algorithm initiates
		bool didTheTrade = false;			// used to toggle iterating SSQRMatrix
		bool useDeltas = false;				// used to turn use of deltas in trade determination on or off

		public decimal ROCThresh;			// return on (risk/margin-committed) capital
		public decimal RORThresh;			// return on risk (= net collar cost - put strike)
		public decimal CCORThresh;			// call coverage ratio / risk for 0 cost collar (risk = stockPrice - putStrike)
		bool goodThresh = false;			// used to determine go/no-go on trade
		public bool switchROC = true;
		public Dictionary<Symbol, decimal> lastClosePrice;

		LookupData LUD = new LookupData();	// repository of system-wide and common data

		List<TradePerfRec> tradeRecs = new List<TradePerfRec>();	// used to track P&L of trades
		List<TradePerfRec> tprsToClose = new List<TradePerfRec>();	// List of TPRs to Close.					// use this in OnData TPR-driven position updating
		List<TradePerfRec> tprsToOpen = new List<TradePerfRec>();	// List of TPRs to Open.
		List<TradePerfRec> secTPRs = new List<TradePerfRec>();
		List<TradePerfRec> thetaTPRs = new List<TradePerfRec>();
		List<TradePerfRec> ETFRecs = new List<TradePerfRec>();		// LIst of ETF Packages 
		
		int tradeRecCount = 0;										// track the trade count
		int secndRecCount = 0;										// loop counter for processing 2nd Recs
		int collarIndex = 0;

		bool hasPrimaryRec = false;
		bool hasSecondaryRec = false;
		bool hasThetaRec = false;
		int curr2ndTPR = 0;											// Used to store index
		int curr1stTPR = 0;											// used to store index of 1st TPR
		int CountTPRs = 0;

		// Use this to filter FineFilterSelection to 1 stock as specified by Algorithm Parameter.
		static string strFilterTkr = "";
		Symbol thisSymbol;							// Initialize Symbol as class variable

		//decimal incrPrice = 0;						// check for underlying price appreciation
		decimal currSellPnL = 0;					// for calculating potential roll P&L
		decimal currExrcsPutPnL = 0;				// for calculating potential roll P&L
		decimal currExrcsCallPnL = 0;				// for calculating potential roll P&L
		
		decimal callStrike;
		decimal putStrike;
		decimal sTPRPutStrike;						// strike of 2nd TPR Put Strike
		decimal wcStrike;							// strike of wing call for evaluating sale
		
		Symbol debugSymbol;							// general purpose debugging variable
		OptionChain debugChain;						// special purpose debugging variable
		decimal stockPrice = 0;	decimal fTPRPutPrice = 0;					// used when rolling up stop losses or deciding to exercise ITM positions
		decimal sTPRPutPrice = 0;					// used when evaluating sTPRs for rolling or extinguishing
		decimal thisROC = 0;
		decimal thisROR = 0;
		decimal thisCCOR = 0;
		decimal heldValue = 0;						// value of thisSymbol held
		bool buyMoreShares = false;					// decision to buy more shares of thisSymbol or keep managing inventory

		SSQRColumn bestSSQRColumn = new SSQRColumn();
		List<ColumnSD> CSDsToDo = new List<ColumnSD>();
		List<SymbolData> SDsThatCanRoll = new List<SymbolData>();
		decimal stockDividendAmount = 0M;
		string divFrequency = "Quarterly";
		decimal divPlotValue = 0M;
		
		DateTime lastDate = DateTime.MinValue;
		DateTime newDate = DateTime.MinValue;
		
		Symbol symbSPY;

		bool sellThePut = false;					// ORDER MANAGEMENT CONTROL -- SET sellThePut whenever calls are exercised by LEAN
		bool buyTheCall = false;					// ORDER MANAGEMENT CONTROL -- SET buyThePut whenever puts are exercised by LEAN

		
		// Holds multi ticker data
		Dictionary<Symbol, SymbolData> symbolDataBySymbol = new Dictionary<Symbol, SymbolData>();
		Dictionary<Symbol, SymbolData> etfDataBySymbol = new Dictionary<Symbol, SymbolData>();					/// SDBS for ETFs
		
		bool doETF;									// flag to limit ETF processing to 1X on last day
		
		public override void Initialize()
        {
			var keys = ObjectStore.Keys;
			foreach (var key in keys){
				ObjectStore.Delete(key);
			}
			
			ObjectStore.Save("Test Entry", "nothing to see here");

    		DateTime startDate = DateTime.Parse(GetParameter("StartDate"));
    		DateTime endDate = DateTime.Parse(GetParameter("EndDate"));
    
    		SetStartDate(startDate.Year, startDate.Month, startDate.Day);		//Set Start Date
		    SetEndDate(endDate.Year, endDate.Month, endDate.Day);				// Set End Date
		    
            SetCash(30000000);           /// Set Strategy Cash   $100,000,000   for ~50 positions @ $30,000 risk per

 			strFilterTkr = GetParameter("stockTicker");
			if (strFilterTkr == "-"){ 
				strFilterTkr = "";
				ourUniverse = AddUniverse<StockDataSource>("VE-IEX-Combo", stockDataSource =>
				{
					return stockDataSource.SelectMany(x => x.Symbols).Take(35);				// take 30 for 2022	for 2023
				});
			} else {
				AddEquity(strFilterTkr, Resolution.Minute, Market.USA, true, 3, false, DataNormalizationMode.Raw);
			}
			
			badDtParameter = GetParameter("CheckBadDate")  == "true" ? true : false; // get this from parameters      

			stockDollarValue = Convert.ToDecimal(GetParameter("StockDollarValue"));
			LUD.unitRiskAmt = stockDollarValue;									// package dollar amount of risk per package at start
			useDeltas = GetParameter("UseDeltas")  == "true" ? true : false;	// get this from parameters
			decOIThresh = Convert.ToDecimal(GetParameter("OpenIntrstThresh"));	// get the threshold of open interest to eliminate thinly-traded securities
 
			LUD.InitializeData(this);
			LUD.maxPutOTM = Convert.ToInt16(GetParameter("MaxOTMPutDepth"));	// get and set the Maximum OTM Put Depth
			LUD.maxCallOTM = Convert.ToInt16(GetParameter("MaxOTMCallDepth"));	// get and set the Maximum OTM Put Depth

			doTracing = LUD.doTracing = GetParameter("LogTrace") == "true" ? true : false;	// get this from paramters to turn //if (doTracing) Log() tracing on/off
			doDeepTracing = LUD.doDeepTracing = GetParameter("LogTraceDeeper") == "true" ? true : false;	// get this from paramters to turn //if (doTracing) Log() tracing on/off

			//symbSPY = AddEquity("SPY", Resolution.Daily, Market.USA, true, 0, false, DataNormalizationMode.Adjusted).Symbol;
			
			//BuildETFDBS();

        }  // // // /// /// /// /// /// Initialize()
        

        public void OnData(Dividends dData)	
        {
	        try{
	        	if (Portfolio.Invested) 
	        	{
					int k = 0;												// counter for updates
					
					if (tradeRecs.Any(tpr=> tpr!=null && tpr.isOpen & !tpr.isSecondary & !tpr.isTheta)) {
						foreach(var tprec in tradeRecs.Where(tpr=> tpr.isOpen && !tpr.isSecondary && !tpr.isTheta)) {
							if (tprec.uSymbol != null) {
								if(dData.ContainsKey(tprec.uSymbol)) {
									var paymentAmount = dData[tprec.uSymbol].Distribution;
									tprec.numDividends = tprec.numDividends + 1;
									tprec.divIncome = tprec.divIncome + paymentAmount;
									k = k + 1;
										//if (doDeepTracing) Log(" DDDDDDDDDD DDDDDDDDDDD DIVIDENDS FOR " + tprec.uSymbol + " ARE " + paymentAmount);
								}
							}
						}
					}
				}
			} catch (Exception errMsg)
	        {
	        	if (doTracing)  Log(" DIV ERROR DIV ERROR DIV ERROR " + errMsg);
	        	return;
	        }
        }
        
        public override void OnData(Slice data)
        {
        	bool processClosedTPRs = false;
			logPortfolio = true;
			SecAddedCnt = SecRemvdCnt = 0;
			haltProcessing = false;
			
			if (CheckBadDate(data.Time)) {
				haltProcessing = true;
				Log($" --- --- === HALT PROCESSING");
			} 

			if (IsMarketOpen("IBM") & data.Time.Hour == 9 & data.Time.Minute == 41) {
				if (tradeRecs.Any(tpr=> tpr!=null && tpr.isOpen && (Portfolio[tpr.uSymbol].Invested && tpr.cSymbol!=null && !Portfolio[tpr.cSymbol].Invested)) ) {									//// careful here.   only primary and secondary (non-theta) tprs have uSymbols
					foreach(TradePerfRec tprec in tradeRecs.Where(tpr=> tpr.isOpen && (Portfolio[tpr.uSymbol].Invested && tpr.cSymbol!=null))) {
						if (IsOrphaned(tprec.uSymbol)) {
							LUD.clearLD(this);
							LUD.uSymbol = tprec.uSymbol;
							tprec.CleanUp(this);
						}
					}
				}
				if (tprsToClose.Count != 0) {
					foreach (TradePerfRec rec in tprsToClose) {
						if (Portfolio[rec.uSymbol].Invested){
							OrderTicket liquidTix = MarketOrder(rec.uSymbol, -rec.uQty);
							if (liquidTix.Status == OrderStatus.Filled) rec.uEndPrice = liquidTix.AverageFillPrice;
							if (doTracing) Log($"  |||| ||||  LIQUIDATING ORPHAN STOCK {rec.uSymbol.Value} ON {data.Time.ToShortDateString()} AT {data.Time.ToShortTimeString()} SETTING PRICE TO {liquidTix.AverageFillPrice.ToString("0.00")}");
						}
						rec.isOpen = false;
						if (symbolDataBySymbol.ContainsKey(rec.uSymbol)) {
							symbolDataBySymbol.Remove(rec.uSymbol);
						}
					}
					tprsToClose.Clear();
				}

				if(symbolDataBySymbol.Any(sdbs =>  !Portfolio[sdbs.Key].Invested  && sdbs.Value.openInterestCheck && (!sdbs.Value.isRollable | !sdbs.Value.isTriggered)))
				{
					foreach(var pair in symbolDataBySymbol.Where(sdbs=> !Portfolio[sdbs.Key].Invested  && sdbs.Value.openInterestCheck && (!sdbs.Value.isRollable | !sdbs.Value.isTriggered))){
						LUD.clearLD(this);
						LUD.uSymbol = pair.Key;
						LUD.getNxtIExDt(pair.Key.Value, this);
						LUD.loadVEData(this);
						LUD.intType = 0;							/// stock 
						LUD.GetTotalGain(0, Securities[pair.Key].Price);
						if (LUD.decTotalGain <= -.08M * Securities[pair.Key].Price )  {			/// **** **** 2024-01 ... ensure 10% loss or greater  2023-11-23 adjust filter to total yield > 8 or 10%
							SDsThatCanRoll.Add(pair.Value);
						}
					}


				}

				if (SDsThatCanRoll.Count!=0){
					foreach(SymbolData sd in SDsThatCanRoll){
						if(doDeepTracing) Log($" -- --- --- recapture  prospects that may now be tradable {sd.symbol.Value}");
						sd.isRollable = true;
						bool placeholder =sd.IsTriggered(this);
					}
					SDsThatCanRoll.Clear();
				}

				foreach(var sd in symbolDataBySymbol){
					if (tradeRecs.Any(tpr => tpr!=null && tpr.uSymbol.Equals(sd.Key) /*&& tpr.isOpen && Portfolio[sd.Key].Invested*/)) {
						if (haltProcessing) Log($" -- %%%%%%%%%% => ASSESS KILL SHORT");
						if (IsFirstTradingDay(data.Time, sd.Key)) sd.Value.TargetReversalKill(this, LUD);			///| sd.Value.killShort) {				// on FDOM check if target increased by 10& or more or if on last consolidted bar, killShort triggere
						bool dummy = sd.Value.KillShort(this);
						if(doDeepTracing) Log($" tprtprtprtprtprtprtpr --- ContTrad: {sd.Value.continueTrade} KillShort: {sd.Value.killShort} SBB: {sd.Value.bbTrigger} VDI: {sd.Value.VDI_Signal}  TSI: {sd.Value.tsi_trigger} ALMA-X: {sd.Value.almas_crossing}  ");
						if (sd.Value.killShort) {
							LUD.clearLD(this);
							LUD.uSymbol = sd.Key;
							LUD.stockPrice = Securities[sd.Key].Price;
							LUD.loadVEData(this);
							TradePerfRec thistpr = tradeRecs.Where(tpr => tpr!=null && tpr.uSymbol.Equals(sd.Key) && tpr.isOpen).FirstOrDefault();
							if (thistpr != null) {
								if (doDeepTracing) Log($" tprtprtprtprtprtprtpr --- ---- Called KillTheCollar for {thistpr.uSymbol.Value} because TargetReversalKill: {sd.Value.targetReversalKill.ToString()}");
								if (!KillTheCollar(thistpr, ref LUD, "GREEN SBB or VDI", true, true)  ) continue;
								tprsToClose.Add(thistpr);
							}
						}
					}
				}
			}

			if (data.Time.Hour < 10) return;

			/*if( doDeepTracing){
				if (data.Time.Hour == 10 && data.Time.Minute == 1) {
					string sSBB = "";
					string sVDI = "";
					string sKill = "";
					string sCont = "";
					foreach(var pair in symbolDataBySymbol){
						if (doDeepTracing) {
							sSBB = pair.Value.bbTrigger.ToString();
							sVDI = pair.Value.VDI_Signal.ToString();
							sKill = pair.Value.killShort.ToString();
							sCont = pair.Value.continueTrade.ToString();
							// Log($" -- TRiGGERS -- TRiGGERS -- TRiGGERS -- TRiGGERS SBB: {sSBB} VDI: {sVDI} Kill: {sKill}  Cont: {sCont} ");
						}
					}
				}
			}
			*/
			if (IsMarketOpen("IBM")) {
				if (oLOs != null && oLOs.Count > 0) ProcessOpenLimitOrders(data);
				if(SymbolsToRemove.Count != 0) ProcessRemoveSecurities();
				if (data.Time.DayOfYear != newDate.DayOfYear) 
				{
					newDate = data.Time;
					if (lastDate.Equals(DateTime.MinValue)){
 						lastDate = newDate.AddDays(-1);
					}
				}
			}

			
			//////////////////////////////////////////////////////////////////////
			//								FIRST -- CHECK FOR ADDED STOCKS 
			//										 UNIVERSE IS EVALUATED/LOADED MONTHLY AT MIDNIGHT ON 1ST DAY OF MONTH.    
			
			if (IsMarketOpen("IBM") && addedStocks) {
        		addedStocks = false;
				
			    if (doTracing) Log($"- --- --- Got to addedstocks");

        		foreach(var sdbs in symbolDataBySymbol) {
	    			Symbol symbOIChk = sdbs.Key;
					//if(!data.ContainsKey(symbOIChk)) continue;				// **** **** 2023-03-15  good candidates may not be traded every minute
	    			SymbolData symbDat = sdbs.Value;

					LUD.clearLD(this);
	    			LUD.uSymbol = symbOIChk;
					LUD.loadVEData(this);										/// **** **** 2023-03-15  commented out because LUD is not used in processing CheckOI
					//LUD.GetNextExDate(this);
	    			LUD.getNxtIExDt(symbOIChk.Value, this);
	    			OptionChain chnOICheck;
	    			
					if (!symbDat.openInterestCheck & (data.OptionChains.TryGetValue(symbDat.optSymbol, out chnOICheck))) {
						//if(LUD.decOneYearPriceTarget < chnOICheck.Underlying.Price)			/// **** **** 2023-03-19 Try using total yield
						
						var atmPutContract_r = chnOICheck.Where(x => x.Right == OptionRight.Put)
		        			.OrderBy(x => x.Expiry)
					        .ThenBy(x => Math.Abs(chnOICheck.Underlying.Price - x.Strike))
					        .FirstOrDefault();
					        
					    var atmCallContract_r = chnOICheck.Where(x => x.Right == OptionRight.Call)
					        .OrderBy(x => x.Expiry)
					        .ThenBy(x => Math.Abs(chnOICheck.Underlying.Price - x.Strike))
					        .FirstOrDefault();
					    
						if(atmCallContract_r!=null) decThisOI = atmCallContract_r.OpenInterest;
					   	if(atmPutContract_r!=null) decThisOI = decThisOI + atmPutContract_r.OpenInterest;
					    LUD.GetTotalGain(0, chnOICheck.Underlying.Price);
					    if (doTracing) Debug($"--- {symbOIChk.Value} has Open Interest of: {decThisOI.ToString()} contracts. At {chnOICheck.Underlying.Price.ToString("0.00")} and projected total annual gain of {LUD.decTotalGain.ToString("0.00")} with a target price of {LUD.decOneYearPriceTarget.ToString()}");
					    
					    
					    if((decThisOI < decOIThresh)) {
					    	SymbolsToRemove.Add(atmCallContract_r.UnderlyingSymbol);
							continue;
					    }
					    symbDat.openInterestCheck = true;
						//if(!(Math.Abs(LUD.decTotalGain) >= Securities[symbOIChk].Price * .1m | LUD.decTotalGain <= -8m))		/// Total gain should be greater than 10% of current stock price (i.e. price target is less than 90% of current price)
						if(LUD.decTotalGain > -.08m * Securities[symbOIChk].Price)
						{ 
							symbDat.isRollable = false;							/// eliminate VECase1 trades
							if(doDeepTracing) Log($" --- ---- MAIN --- ---- {symbDat.symbol.Value} has total gain of {LUD.decTotalGain.ToString("0.00")} and yield of {(LUD.decTotalGain/Securities[symbOIChk].Price).ToString("P2")} and is faded");
						} else symbDat.isRollable = true;
			
						symbDat.PostOptionsFilteringCreateIndicators();

						int daysSinceBeginning = data.Time.DayOfYear - StartDate.DayOfYear;
						if (daysSinceBeginning < 160) daysSinceBeginning = 160;

						var historyBars = History<TradeBar>(symbDat.symbol, daysSinceBeginning, Resolution.Daily);
						symbDat.smaVolume = new SimpleMovingAverage(20);		
						symbDat.stdVolume = new StandardDeviation(20);			
						symbDat.bb = new BollingerBands(12, 2);					// using 12,2 (period, stdDev Multiplier) from SuperBollingerTrend (Expo) in TradingView
						symbDat.psar = new ParabolicStopAndReverse();			// PSAR signal
						symbDat.tsi = new TrueStrengthIndex(40, 10, 10, MovingAverageType.Exponential);		// using 40, 10, 10 from Charley's TradingView observations
						symbDat.ALMA_fast = new ArnaudLegouxMovingAverage("afast", 9, 6, 0.85m);
						symbDat.ALMA_slow = new ArnaudLegouxMovingAverage("aslow", 18, 6, 0.85m);
						symbDat.obv = new OnBalanceVolume();					 // Obv Osc component
						symbDat.ema_obv_long = new ExponentialMovingAverage(20); // Obv Osc component
						symbDat.ema_obv_short = new ExponentialMovingAverage(1); // OBV Osc component	

						foreach(TradeBar tb in historyBars){															/// warm up historyBars and Indicator components
							symbDat.Bars.Add(tb);
							symbDat.smaVolume.Update(tb.Time, tb.Volume);
							symbDat.stdVolume.Update(tb.Time, tb.Volume);
							symbDat.CheckSMAs(tb.Time, this);
						
							symbDat.bb.Update(tb.Time, tb.Close);
							symbDat.Calculate_BB_Crossovers(tb.Time, this);
						
							symbDat.psar.Update(tb);
							symbDat.Calculate_PSAR(tb.Time, this);
						
							symbDat.tsi.Update(tb.Time, tb.Close);
							symbDat.Calculate_TSI_Crossovers(tb.Time, this);

							symbDat.ALMA_fast.Update(tb.Time, tb.Close);
							symbDat.ALMA_slow.Update(tb.Time, tb.Close);
							symbDat.Calculate_ALMA_Crossovers(tb.Time, this);

							symbDat.obv.Update(tb);
							symbDat.ema_obv_long.Update(tb.Time, tb.Close);
							symbDat.ema_obv_short.Update(tb.Time, tb.Close);
							symbDat.CheckOBV(tb.Time, this);
						}
						
						// wire up consolidator to update the indicator
						try{
							symbDat.consolidator.DataConsolidated += (sender, baseData) =>
							{
								// 'bar' here is our newly consolidated data
								var bar = (TradeBar)baseData;
								// update indicator
								//sd.thisMOMP_1.Update(bar.Time, bar.Close);
								//sd.thisMOMP_3.Update(bar.Time, bar.Close);
								//sd.thisMOMP_6.Update(bar.Time, bar.Close);
								symbDat.Bars.Add(bar);
								symbDat.smaVolume.Update(bar.Time, bar.Volume);
								symbDat.stdVolume.Update(bar.Time, bar.Volume);

								symbDat.bb.Update(bar.Time, bar.Close);

								symbDat.psar.Update(bar);

								symbDat.tsi.Update(bar.Time, bar.Close);
								
								symbDat.ALMA_fast.Update(bar.Time, bar.Close);
								symbDat.ALMA_slow.Update(bar.Time, bar.Close);

								symbDat.ema_obv_long.Update(bar.Time, bar.Close);
								symbDat.ema_obv_short.Update(bar.Time, bar.Close);
								symbDat.obv.Update(bar);
							};
							SubscriptionManager.AddConsolidator(symbDat.symbol, symbDat.consolidator);

							symbDat.smaVolume.Updated += (sender, baseData) =>
							{
								symbDat.smaVolumeWasUpdated = true;
								//symbDat.CheckSMAs(this);
								//symbDat.CalculateVDI_Stats(this, false);
							};
							
							symbDat.stdVolume.Updated += (sender, baseData) =>
							{
								symbDat.stdVolumeWasUpdated = true;
								symbDat.CheckSMAs(baseData.Time, this);
								//Log($" -- -- VDI:    {symbDat.VDI_Signal.ToString()} | VDI Divergence: {symbDat.VDI_Divergence.ToString()} ");
								//Log($" ----- VDIwin: {symbDat.IndicatorsWindows["VDI_Signals"][0].ToString()} | VDI_divergence: {symbDat.IndicatorsWindows["VDI_Divergence"][0].ToString()} ");
							};
							
							symbDat.bb.Updated += (sender, baseData) =>
							{
								symbDat.Calculate_BB_Crossovers(baseData.Time, this);
								//Log($" -- -- SBB:    {symbDat.bb.Current.ToString()} |  with trigger = {symbDat.bbTrigger.ToString()}");
							};

							symbDat.psar.Updated += (sender, baseData) =>
							{
								symbDat.Calculate_PSAR(baseData.Time, this);
							};

							symbDat.tsi.Updated += (sender, baseData) =>
							{
								symbDat.Calculate_TSI_Crossovers(baseData.Time, this);
							};
							
							symbDat.ALMA_slow.Updated += (sender, baseData) =>
							{
								symbDat.Calculate_ALMA_Crossovers(baseData.Time, this);
								bool placeHolder = symbDat.IsTriggered(this);				/// ESSENTIAL UPDATE
								placeHolder = symbDat.ContinueTrade(this);					/// 
							};
							
							symbDat.obv.Updated += (sender, baseData) =>
							{
								//symbDat.OBVWasUpdated = true;
								symbDat.CheckOBV(baseData.Time, this);

							};
							
							symbDat.isTriggered = symbDat.IsTriggered(this);
							

							} catch (Exception excp) {
								Log($"Error wiring up Consolidator for {symbDat.symbol} is {excp.Message}");
							}
					}
        		}       
			} 
        	
			if (SymbolsToRemove.Count != 0 ) ProcessRemoveSecurities();
			
			if (data.Time.Minute % 10 !=0) return;


			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			//								THIRD --	CHECK TPRS TO MANAGE EXISTING PACKAGES
			//											ITERATE THROUGH TPRS AND EVALUATE OPPORTUNITIES EVERY 5 MINUTES AFTER 10:00 EST
			//								THIRD A --	PROCESS STOCK MELTUP.   Will not roll package, only kill it
			int k = 0;
			
			if (tradeRecs.Any(tpr=> tpr!=null && tpr.isOpen && (Securities[tpr.uSymbol].Price > 1.15M * tpr.cStrike) ) ){									//// careful here.   only primary and secondary (non-theta) tprs have uSymbols
				foreach(var tprec in tradeRecs.Where(tpr=> tpr.isOpen && (Securities[tpr.uSymbol].Price > 1.15M * tpr.cStrike ))) {

					if(!symbolDataBySymbol.ContainsKey(tprec.uSymbol)) {
						tprsToClose.Add(tprec);
						processClosedTPRs = true;
						continue;
					}

					LUD.clearLD(this);
			    	LUD.uSymbol = tprec.uSymbol;
					LUD.stockPrice = Securities[tprec.uSymbol].Price;
					if (haltProcessing) Log($" -- %%%%%%%%%% => ASSESSING HANDLE COLLAPSE");
		
					if (doDeepTracing) Log($" tprtprtprtprtprtprtpr --- ---- Calling HandleCollapse for {tprec.uSymbol.Value} because the current price {Securities[tprec.uSymbol].Price.ToString()} is more than 115% Callstrike: {tprec.cStrike.ToString()}");
					SymbolData sdThis = symbolDataBySymbol[tprec.uSymbol];
					if (!tprec.HandleCollapse(this, LUD, sdThis)) continue;				
			        k = k + 1;														
		        }
		    }

			if (SymbolsToRemove.Count != 0 ) ProcessRemoveSecurities();

			if (tprsToClose.Any(tpr=> tpr!=null)) {								/// 2021-10-18 -- close TPRs here
        		tprsToClose.ForEach(tpr=>tpr.CloseTPR());
        	}
        	

        	tprsToClose.Clear();
			LUD.clearLD(this);
			k = 0;
			
			////////////////////////////////////////////////////////////////////
			//								THIRD B --	PROCESS ROLL DOWNS
			k = 0;

			if (tradeRecs.Any(tpr=> tpr!=null && tpr.isOpen && tpr.expDate.Subtract(data.Time).Days>1 && RollCriterium(tpr.uStartPrice, Securities[tpr.uSymbol].Price))) {							//// careful here.   only primary and secondary (non-theta) tprs have uSymbols
				foreach(var tprec in tradeRecs.Where(tpr=> tpr.isOpen && RollCriterium(tpr.uStartPrice, Securities[tpr.uSymbol].Price) )) {

					if(!symbolDataBySymbol.ContainsKey(tprec.uSymbol)) {
						tprsToClose.Add(tprec);
						processClosedTPRs = true;
						continue;
					}
					
					LUD.clearLD(this);
			    	LUD.uSymbol = tprec.uSymbol;
					LUD.loadVEData(this);
					LUD.SSQRMatrix.Clear();

					if (haltProcessing) Log($" -- %%%%%%%%%% => ASSESSING ROLLING DOWN");
					
					if (Securities[tprec.uSymbol].Price <= 1.03M * symbolDataBySymbol[tprec.uSymbol].decOneYearPriceTarget_Current && !symbolDataBySymbol[tprec.uSymbol].continueTrade) {
						if (doDeepTracing) Log($" tprtprtprtprtprtprtpr --- ---- Calling KillTheCollar for {tprec.uSymbol.Value} because SBB is GREEN. SBB: {symbolDataBySymbol[tprec.uSymbol].bbTrigger.ToString()} VDI: {symbolDataBySymbol[tprec.uSymbol].VDI_Signal.ToString()}");
						SymbolData sdThis = symbolDataBySymbol[tprec.uSymbol];
						if (!KillTheCollar(tprec, ref LUD, " GREEN SBB", true, true)) continue;				
						tprsToClose.Add(tprec);
					}

					if (!tprec.CheckRollingDown(this, LUD)){
						processClosedTPRs = true;
						continue;
					}
			        k = k + 1;														
		        }
		    }

			if(tprsToClose.Count!=0) {
        		tprsToClose.ForEach(tpr=>tpr.CloseTPR());
        	}
        	tprsToClose.Clear();
			processClosedTPRs = false;
        	
			if (tprsToOpen.Count!=0){
				foreach(TradePerfRec tpr in tprsToOpen) {
					if (doTracing) Debug(" --- ---- ---- Adding new TPR " + tpr.uSymbol.Value);
					if (symbolDataBySymbol.ContainsKey(tpr.uSymbol)) {
						tpr.OpenTPR();
						tradeRecs.Add(tpr);
					}
				}
        	}
			tprsToOpen.Clear();

			LUD.clearLD(this);

			k = 0;
 			//								THIRD C --	PROCESS EXPIRATION AND DIVIDEND ROLLS

			if (tradeRecs.Any(tpr=> tpr!=null && tpr.isOpen && data.Time.Subtract(tpr.startDate).Days >=10)) {
				foreach(var tprec in tradeRecs.Where(tpr=> tpr.isOpen && data.Time.Subtract(tpr.startDate).Days >= 10)) {

				if (haltProcessing) Log($" -- %%%%%%%%%% => ASSESSING EXPIRATION ROLLS");

					if(!symbolDataBySymbol.ContainsKey(tprec.uSymbol)) {
						tprsToClose.Add(tprec);
						processClosedTPRs = true;
						continue;
					}
					
					LUD.clearLD(this);
			    	LUD.uSymbol = tprec.uSymbol;
					LUD.loadVEData(this);
					LUD.SSQRMatrix.Clear();
			    	if (!tprec.CheckRolling(this, LUD)){		
						processClosedTPRs = true;
						continue;
					}
			        k = k + 1;
		        }
		    }
			
			if(tprsToClose.Count!=0) {
        		tprsToClose.ForEach(tpr=>tpr.CloseTPR());
        	}
        	tprsToClose.Clear();
			processClosedTPRs = false;
        	
			if (tprsToOpen.Count!=0){
				foreach(TradePerfRec tpr in tprsToOpen) {
					if (doTracing) Debug($" --- ---- ---- ---- -----  ---  Adding new TPR {tpr.uSymbol.Value} in CheckRolling");
					if (symbolDataBySymbol.ContainsKey(tpr.uSymbol)) {
						tpr.OpenTPR();
						tradeRecs.Add(tpr);
					} 
				}
        	}
			tprsToOpen.Clear();

			LUD.clearLD(this);

			k = 0;
		///////////////////////////////////////////////////////////////////////////////////////////////////////
		//								FOURTH --	CHECK SDBS PROSPECTS FOR OPENING POSITIONS
		//											ITERATE THROUGH SDBS AND EVALUATE OPPORTUNITIES EVERY 15 MINUTES IN 10:00 EST HOUR
		//											DO NOT PROCESS BEFORE 10:00 EST AS OPTIONS MARKETS TOO THIN AND SPREADS TOO WIDE
			if (data.Time.Minute % 20 !=0) return;

			/*
			if(symbolDataBySymbol.Any(sdbs => !Portfolio[sdbs.Key].Invested && (!sdbs.Value.isRollable | !sdbs.Value.isTriggered)) ) {
				foreach(var pair in symbolDataBySymbol.Where(sdbs => !Portfolio[sdbs.Key].Invested && (!sdbs.Value.isRollable | !sdbs.Value.isTriggered ))){
					if (Securities[pair.Key].Price < .97m * pair.Value.decOneYearPriceTarget_Current ) {
						SymbolsToRemove.Add(pair.Key);
						if (doDeepTracing) Log($" -- -- Removing Dormant SDBS's {pair.Key.Value}: Price: {Securities[pair.Key].Price} Target: {pair.Value.decOneYearPriceTarget_Current}  SBB: {pair.Value.bbTrigger} VDI: {pair.Value.VDI_Signal} PSAR: {pair.Value.psarSignal} TSI: {pair.Value.tsi} (trigger): {pair.Value.tsi_trigger} ALMA-X: {pair.Value.almas_crossing} IsTriggered: {pair.Value.isTriggered}.");
					}
				}
			}
			*/
			if (symbolDataBySymbol.Any(sdbs => sdbs.Value.isRollable && sdbs.Value.isTriggered && sdbs.Value.openInterestCheck && !Portfolio[sdbs.Key].Invested))
			{
				try{
					if (doTracing) Log($" --- SCANNING PROSPECTIVE PORTFOLIO CANDIDATES AT {data.Time.ToString()}.");
					foreach(var pair in symbolDataBySymbol.Where(sdbs => sdbs.Value.isRollable && sdbs.Value.IsTriggered(this) && sdbs.Value.openInterestCheck && !sdbs.Value.FadeShort(this, Securities[sdbs.Key].Price) && !Portfolio[sdbs.Key].Invested)) {
						k +=1;
						if (pair.Key == null) {
							Debug(" --- ---- ---- Found null key in foreach sdbs.");
							continue;
						}
						Symbol thisSymbol = pair.Key;

						if (doTracing) Log($" --- ---- Checking Correct KEY: the key is {pair.Key}.");

						if (!IsMarketOpen(thisSymbol))	continue;

						if (!data.ContainsKey(thisSymbol)) {
							if (doDeepTracing)	Log($" --- ---- --- there's no data in slice for {thisSymbol.Value}. ");
							//continue;
						}

						if (!Securities.ContainsKey(thisSymbol)) {
							if (doDeepTracing)	Log($" --- ---- --- there's no data in Securities for {thisSymbol.Value}. ");
							continue;
						}

						if (Portfolio[thisSymbol].Invested) continue;			/// Don't compound packages.    Do only 1 package per symbol at any time
						if (doTracing) Log($" --- --- CANDIDATE: {thisSymbol.Value}.");

						LUD.clearLD(this);
						LUD.uSymbol = thisSymbol;
						LUD.intType = 0;
						SymbolData symbolData = pair.Value;
						
						goodThresh = false;									// set the threshold switch to false;

						hasPrimaryRec = hasSecondaryRec = false;			// reset processing branch flags

						string tickerString = thisSymbol.Value;
						
						////////////////////////////////////////////////////////////////////////////
						//								IN VERSION 5 DO NOT CHECK FOR DIVIDENDS THIS MONTH
						//								JUST DO THE TRADE WHEN VALUENGINE HAS A 5 ENGINE RATING
						
						//LUD.GetNextExDate(tickerString, this);													//// returns DateTime.MinValue if no date discovered
																												//// this function also populates the VE parameter members in LUD
						LUD.getNxtIExDt(tickerString, this);
						pair.Value.divdndAmt = LUD.divdndAmt;

						LUD.loadVEData(this);
						pair.Value.decOneYearPriceTarget_Initial = LUD.decOneYearPriceTarget; 
						pair.Value.decOneYearPriceTarget_Current = LUD.decOneYearPriceTarget;
						pair.Value.initialTargetEndDate = LUD.initialTargetEndDate;

						hasDividends = true;
						stockPrice = Securities[thisSymbol].Price;

						if (stockPrice == 0) {
							if (doDeepTracing)	Log($" --- ---- --- Securities price for {thisSymbol.Value} is zero. ");
							//LUD.clearLD(this);
							continue; 
						}
						//if ((LUD.divdndAmt * 4m) + LUD.decOneYearPriceTarget < 1.05m * stockPrice) {	// 
						LUD.GetTotalGain(0, stockPrice);
						if (LUD.decTotalGain > -.03M * stockPrice) {				// **** **** 2024-01 enforce 3% decline threshhold 2023-03-18 modified to total projected gain in 9 mos. 
							//symbolData.isRollable = false;
							//SymbolsToRemove.Add(thisSymbol);						//  don't trade and remove any symbol that isn't set to apprciate at least 5%
							if (doTracing) Debug($" *^*^*^*^*^**^*^**^  FADING ENTRY OF {thisSymbol.Value} @ {stockPrice.ToString("0.00")} due to less than 3% potential yield or {(LUD.decTotalGain/stockPrice*100).ToString("P2")}.");
							//LUD.clearLD(this);
							continue;	
						} 
						
						thisROC = 0;
						thisROR = 0;
						
						LUD.SSQRMatrix.Clear();	
						if(doTracing) Log($" -- PSAR: {symbolData.psarSignal.ToString()} | SBB: {symbolData.bbTrigger.ToString()}	| VDI: {symbolData.VDI_Signal.ToString()} | TSI {symbolData.tsi_weak_trigger.ToString()} | ALMA-X {symbolData.almas_crossing.ToString()}");
						if (hasDividends & symbolData.isRollable & symbolData.openInterestCheck & !symbolData.FadeShort(this, Securities[symbolData.symbol].Price) & symbolData.IsTriggered(this))
						{
							bestSSQRColumn = GetBestCollar(this,  ref LUD);							// send an LUD with the required information for making trading decisions
											
							if (bestSSQRColumn == null || bestSSQRColumn.IsEmpty())				// just in case somehow we got here with a null bestSSQRColumn 
							{
								if (doTracing) Log($"*** *** ***  null OR EMPTY bestSSQR in Trade Initializing  {thisSymbol} *************");
								symbolData.SSQRFailCnt += 1;
								if (symbolData.SSQRFailCnt >= 4) {
										if(doTracing) Log($"*** *** *** *** Removing {thisSymbol} after 4 null bestSSQLRs.");
										SymbolsToRemove.Add(pair.Key);
								}
								//LUD.clearLD(this);
								continue;
							} else {
								
								if (!bestSSQRColumn.IsEmpty()) {
									if (doTracing) Log($"*** *** *** ADDING NEW BESTSSQRCOLUMN AND SYMBOLDATA TO BE PROCESSED --  {thisSymbol} --- on " + data.Time.ToShortDateString() + " at " + data.Time.ToShortTimeString()) ;
									//LUD.GetNextExDate(bestSSQRColumn.uSymbol, this);
									//LUD.loadVEData(this);
									//LUD.uSymbol=bestSSQRColumn.uSymbol;
									ColumnSD thisCSD = new ColumnSD(bestSSQRColumn, ref symbolData, LUD);
									CSDsToDo.Add(thisCSD);
									
								}		/// !bestSSQRColumn.IsEmpty()
								//LUD.clearLD(this);
							}
						} else {									// buyMoreShares & hasDividends & symbolData.isRollable & symbolData.openInterestCheck
							if (doDeepTracing)	Log($" --- ---- --- --- {thisSymbol.Value} FAILED ROLLABLE or OI TEST. ");
							//LUD.clearLD(this);
						}

					}  // end ForEach(var pair in symbolDataBySymbol)
				} catch (Exception forExcp) {
					Log($" Error Prospect Evaluation in foreach sdbs in SDBS " + forExcp.Message);
					//LUD.clearLD(this);
				}
			} else if (symbolDataBySymbol.Any(sdbs => !Portfolio[sdbs.Key].Invested)) {
					if (doTracing) Log($" --- RE-SCANNING PROSPECTIVE PORTFOLIO CANDIDATES AT {data.Time.ToString()}.");
					foreach(var pair in symbolDataBySymbol.Where(sdbs => !Portfolio[sdbs.Key].Invested)) {
						if(doTracing) Log($" --- --- Symbol: {pair.Key.Value} Rollable: {pair.Value.isRollable} OICheck: {pair.Value.openInterestCheck} Triggered: {pair.Value.isTriggered} Faded: {pair.Value.fadeShort}.");
						if(doTracing) Log($" --- --- PSAR: {pair.Value.psarSignal} SBB: {pair.Value.bbTrigger} VDI: {pair.Value.VDI_Signal} TSI: {pair.Value.tsi_trigger} ALMA-X: {pair.Value.almas_crossing}.");
					}
			}
			
			if(CSDsToDo.Count != 0){
				foreach (ColumnSD csd in CSDsToDo){
					if(doDeepTracing) Log($" *** *** *** *** *** PROCESSING CSDsToDo: {csd.sd.symbol.Value}. ");
					if (csd.ld!=null){
						csd.ld.clearLD(this);
						csd.ld.uSymbol=csd.sd.symbol;
						//csd.ld.GetNextExDate(this);
						csd.ld.getNxtIExDt(csd.sd.symbol.Value, this);
						csd.ld.loadVEData(this);
					}
					if(csd.ld==null && doDeepTracing) { 
						if(doTracing) Log($" *** *** *** *** *** {csd.sd.symbol.Value} is null for some reason");
					} else if(doDeepTracing) Log($" *** *** *** *** *** LUD Check {csd.ld.uSymbol.Value} has VE statistic OneYearPriceTarget={csd.ld.decOneYearPriceTarget.ToString("0.00")}" ); 

					ExecuteSSQRs(data, csd);							// TradeDetermination
					if (csd.sd.bbTrigger == 1) {
						csd.sd.bbTrigger = 0;	// Clear an opposing/closing bbTrigger
						csd.sd.killShort = false;
					}
					if (csd.sd.VDI_Signal == 1) csd.sd.VDI_Signal = 0;	// Clear an opposing/closing bbTrigger
					Log($" tprtprtprtprtprtprtpr --- ContTrad: {csd.sd.continueTrade} KillShort: {csd.sd.killShort} SBB: {csd.sd.bbTrigger} VDI: {csd.sd.VDI_Signal}  TSI: {csd.sd.tsi_trigger} ALMA-X: {csd.sd.almas_crossing}  ");
				}
				CSDsToDo.Clear();
			}
        }	// OnData()

		public void ProcessRemoveSecurities()
		{
			SymbolData sd_r;
			if (SymbolsToRemove.Count != 0) {
				foreach(var symbol_r in SymbolsToRemove) {
					if (ourUniverse !=null && ourUniverse.ContainsMember(symbol_r)) {
						if (symbolDataBySymbol.ContainsKey(symbol_r)){
							symbolDataBySymbol[symbol_r].isRollable = false;
							symbolDataBySymbol[symbol_r].isTriggered = false;
						}
						continue;
					}
					if (doDeepTracing) Log($" -- Removing SDBS Record for {symbol_r.Value}");
					if(symbolDataBySymbol.ContainsKey(symbol_r)) {
						sd_r = symbolDataBySymbol[symbol_r];							/// SDBS for this symbol_r
						if(doTracing) Log($" - - --- Processing {sd_r.symbol.Value} out of SymbolDataBySymbol");
						if(strFilterTkr != ""){
							foreach (var indyWinKVP in sd_r.IndicatorsWindows) {			/// Dictionary<string, RollingWindow<IndicatorDataPoint>>
								var indyDataPointsWin = indyWinKVP.Value.Reverse();			/// RollingWindow<IndicatorDataPoint>
								
								String strObj = "";
								String strIndName = indyWinKVP.Key;
									
								// Log($" -- -- Processing SDBS Indicator: {indyWinKVP.Key}"); //// DIAGNOSTIC LOGGING
								foreach(IndicatorDataPoint indyDataPoint in indyDataPointsWin){
									strObj += $"{indyDataPoint.Time}, {indyDataPoint.Value.ToString("0.00")} \n";
									// Log($" -- -- -- {indyDataPoint.Time}, {indyDataPoint.Value.ToString("0.00")}");  /// Object Store DIAGNOSTICS
								}
								ObjectStore.Save(symbol_r.Value + "_" + strIndName, strObj);
								// Debug($" ObjStore Value saved: {strObj}");
							}
						}
						RemoveLowOI_OptSymbs(symbol_r);
						if (!Portfolio[symbol_r].Invested) 
						{
							SubscriptionManager.RemoveConsolidator(symbol_r, sd_r.consolidator);
							RemoveSecurity(symbol_r);
							if (doTracing) Debug("--- --- *** " + SecRemvdCnt.ToString() + ":  Removing " + symbol_r.Value + " Collar killed or no OI.");
							symbolDataBySymbol.Remove(symbol_r);
						}

					}
				}        	
			}
			SymbolsToRemove.Clear();
			/*
			Log($" |||| |||| LOGGING THE HOLDINGS ON REMOVE SYMBOLS");
			foreach(var kvp in Securities)
			{
				var security = kvp.Value;
				if (security.Invested)
				{
					Log($" |||| ||||  HOLDINGS: {security.Symbol} : {security.Holdings.Quantity} @ {security.BidPrice} by {security.AskPrice}");
				}
			}
			*/
		}	

		public void RemoveLowOI_OptSymbs(Symbol symbol) {
			if (symbolDataBySymbol.ContainsKey(symbol)) {
				SymbolData sd = symbolDataBySymbol[symbol];
        		if (Portfolio[sd.optSymbol].Invested) Debug($" *^*^*^*^**^*^*^  *^*^*^*^*^*^*^^*   OPTION BEING LIQUIDATED: {symbolDataBySymbol[symbol].optSymbol.Value} ||||||");
				RemoveSecurity(sd.optSymbol);
				//symbolDataBySymbol.Remove(symbol);
			} 
		}


        public override void OnSecuritiesChanged(SecurityChanges changes)
        {
			foreach(var security in changes.AddedSecurities) {
				// Debug("Securities updated at " + Time.ToString() + ": " + security.Symbol.Value);
				SecAddedCnt += 1;
				Symbol thisSymbol = security.Symbol;
        		if (security.Type == SecurityType.Equity)
        		{
        			// Debug("Securities updated at " + Time.ToString() + ": " + security.Symbol.Value);
					if (symbolDataBySymbol.ContainsKey(thisSymbol)) {
						
						if (doDeepTracing) Debug("--- --- " + SecAddedCnt.ToString() + " SDBS already has " + thisSymbol.ToString());
						continue;
					}
					if (thisSymbol.Value == "HLT" | thisSymbol.Value == "META" | thisSymbol.Value == "FOX"){
						RemoveSecurity(thisSymbol);
						continue;
					}
					addedStocks = true;

					var opt = AddOption(thisSymbol, Resolution.Minute, Market.USA, true, 0m);

					opt.PriceModel = OptionPriceModels.BjerksundStensland();		/// necessary for Greeks

					opt.SetFilter(universe => from symbol in universe												//.IncludeWeeklys()
						.Expiration(TimeSpan.Zero, TimeSpan.FromDays(190))
						where symbol.ID.StrikePrice >= 0.8m * universe.Underlying.Price & symbol.ID.StrikePrice <= 1.2m * universe.Underlying.Price select symbol);
					
					symbolDataBySymbol.Add(thisSymbol, new SymbolData(thisSymbol, true, false, opt.Symbol, symbSPY));

					SymbolData sd = symbolDataBySymbol[thisSymbol];
					if(doTracing) Log($" --- ---- ***** ADDING {thisSymbol.Value} to SDBS with option {opt.Symbol.Value}");

	            	
	    		} //else if (doDeepTracing) Debug("--- --- " + SecAddedCnt.ToString() + " Option Chains " + thisSymbol.Value + " added after Universe changes.");
        		
        	}
			
			foreach(var security in changes.RemovedSecurities) {
			//	if(doTracing) Debug("--- --- --- Removing Symbol:  " + security.ToString());
        		if (security.Type == SecurityType.Equity & !Portfolio[security.Symbol].Invested) {
					if (symbolDataBySymbol.ContainsKey(security.Symbol)) {
						RemoveSecurity(symbolDataBySymbol[security.Symbol].optSymbol);
						//symbolDataBySymbol.Remove(security.Symbol);
					}
				}
			}

		}

	    // **********************   OnOrderEvent		***********************************************
	    // ****************************************************************************************************
    
        public override void OnOrderEvent(OrderEvent orderEvent) {
        	var order = Transactions.GetOrderById(orderEvent.OrderId);
			var oeSymb = orderEvent.Symbol;

			try {
   			if (orderEvent.Status == OrderStatus.Filled) 
    		{
	    		//var order = Transactions.GetOrderById(orderEvent.OrderId);
				//var oeSymb = orderEvent.Symbol;

				if (order.Type == OrderType.OptionExercise)
				{
					if (doDeepTracing) Log(" OO OPTION EXERCISE ORDER EVENT AT:" + orderEvent.UtcTime + " OOOO");
   					if (orderEvent.IsAssignment) {
    					//   .IsAssignment seems only to occur when LEAN creates the ASSIGNMENT.  -- use this to troubleshoot
    					//   Check for this now because DIVIDEND APPROACHMENT may 
    					if (doDeepTracing) Log(" OO " + orderEvent.UtcTime + " LEAN LEAN LEAN    ASSIGNMENT ORDER EVENT    LEAN LEAN LEAN  OOOOOO");
    					if (doDeepTracing) Log(" OO LEAN ASSIGNMENT SYMBOL: " + oeSymb );
    				
   						if (oeSymb.HasUnderlying && oeSymb.ID.OptionRight == OptionRight.Call) {
   							sellThePut = true;
   						}
   					}

	        		if (doDeepTracing) Log(" OO Quantity: " + orderEvent.FillQuantity + ", price: " + orderEvent.FillPrice);
	
					if (oeSymb.HasUnderlying) {															/// /// /// THIS IS AN OPTION EXERCISE ORDER
						didTheTrade = true;
						var thisOption = (Option)Securities[oeSymb];
						var stkSymbol = thisOption.Underlying.Symbol;
    					if (doDeepTracing) Log(" OO OPTIONS EXERCISE ORDER FOR : " + oeSymb + " IS A " + (oeSymb.ID.OptionRight == OptionRight.Put ? "PUT. " : "CALL.") + "for underlying: " + stkSymbol.Value);

    					if(oeSymb.ID.OptionRight == OptionRight.Put)
    					{
    						if (doDeepTracing) Log(" oo PUT OPTION EXERCISE ORDER FOR : " + oeSymb);

							foreach (TradePerfRec pTPR in tradeRecs){
								//Debug($" OO TPR OO TPR OO {tpr.uSymbol.Value} is {(tpr.isOpen ? "open" : "closed")} and has {tpr.cQty.ToString()} calls.");
								if (pTPR.pSymbol.Equals(oeSymb) & pTPR.pQty == -order.Quantity) {
									if (doDeepTracing) Log(" oo oo FOUND SHORT PUT 1ST TPR oo ");
									pTPR.uEndPrice = oeSymb.ID.StrikePrice;																/// set the TPR underlying end price
									pTPR.endDate = orderEvent.UtcTime;

									if (pTPR.reasonForClose !=null || pTPR.reasonForClose != "") {
										pTPR.reasonForClose = pTPR.reasonForClose +  " PUT OPTIONS ASSIGNMENT -- UNDERLYING CLOSED";
									} else pTPR.reasonForClose = pTPR.reasonForClose + " PUT OPTIONS ASSIGNMENT -- UNDERLYING CLOSED";

									if (doDeepTracing) Log($" OO OO UPDATED {oeSymb.Value} END PRICE TO : {orderEvent.FillPrice}.");
									
									if (pTPR.cSymbol != null && pTPR.cEndPrice != 0) {
										var shrtCall = (Option)Securities[pTPR.cSymbol];
										TimeSpan daysToCallExpiry = shrtCall.Expiry.Subtract(orderEvent.UtcTime);
										if (doDeepTracing) Log(" OO OO OO SELLING THE CALL IF IT EXISTS");
										
										var closeCTkt = MarketOrder(pTPR.cSymbol, -pTPR.cQty);
										if (closeCTkt.Status == OrderStatus.Filled) {
											pTPR.cEndPrice = closeCTkt.AverageFillPrice;
										}
										//}
									}
									if (doDeepTracing) Log(" OO SELLING THE WING CALL IF IT EXISTS");
									if (pTPR.wcSymbol != null && pTPR.wcEndPrice != 0) {
										//var wingCall = (Option)Securities[pTPR.wcSymbol];
										var closeWingTkt = MarketOrder(pTPR.wcSymbol, -pTPR.wcQty);
										if (closeWingTkt.Status == OrderStatus.Filled) {
											pTPR.wcEndPrice = closeWingTkt.AverageFillPrice;
										}
									}	

								} 
							}
								
    					} else if (oeSymb.ID.OptionRight == OptionRight.Call){
    						if (doDeepTracing) Log(" oo CALL OPTION EXERCISE ORDER FOR : " + oeSymb);

							foreach (TradePerfRec cTPR in tradeRecs){
								if (cTPR.cSymbol.Equals(oeSymb) & cTPR.cQty == -order.Quantity) {
   
    								if (doDeepTracing) Log(" oo oo FOUND LONG CALL 1ST TPR oo ");
								
									cTPR.cEndPrice = orderEvent.FillPrice;
									if (doDeepTracing) Log($" oo oo oo UPDATED 1ST TPR SHORT CALL {oeSymb.Value} END PRICE TO : {orderEvent.FillPrice}.");
									
									cTPR.uEndPrice = oeSymb.ID.StrikePrice;																/// set the TPR underlying end price
									cTPR.endDate = orderEvent.UtcTime;
									if (cTPR.reasonForClose !=null || cTPR.reasonForClose != "") {
										cTPR.reasonForClose = cTPR.reasonForClose +  " CALL OPTIONS ASSIGNMENT -- UNDERLYING CLOSED";
									} else cTPR.reasonForClose = cTPR.reasonForClose + " CALL OPTIONS ASSIGNMENT -- UNDERLYING CLOSED";

									if(symbolDataBySymbol.ContainsKey(cTPR.uSymbol) ){
										symbolDataBySymbol[cTPR.uSymbol].isRollable = false;	
										SymbolsToRemove.Add(cTPR.uSymbol);
									}

									if (doDeepTracing) Log($" oo oo oo UPDATED 1ST SHORT CALL TPR uEndPrice {oeSymb.Underlying.Value} END PRICE TO : {cTPR.cStrike}.");
									
									if (cTPR.pSymbol != null) {
										var longPut = (Option)Securities[cTPR.pSymbol];
										if (doDeepTracing) Log(" oo oo SELLING THE PUT IF IT EXISTS");
										var closePTkt = MarketOrder(cTPR.pSymbol, -cTPR.pQty);
										if (closePTkt.Status == OrderStatus.Filled) {
											cTPR.pEndPrice = closePTkt.AverageFillPrice;
										}
									}
									if (cTPR.wcSymbol != null) {
										var wCallSymbol = (Option)Securities[cTPR.wcSymbol];
										if (doDeepTracing) Log(" oo oo oo SELLING THE WING CALL IF IT EXISTS OR HASN'T BEEN BOUGHT");
										if (cTPR.wcEndPrice != 0) {
											if (doDeepTracing) Log(" oo oo oo oo SELLING THE WING CALL");
											var closeWCTkt = MarketOrder(cTPR.wcSymbol, -cTPR.wcQty);
											if (closeWCTkt.Status == OrderStatus.Filled) {
												cTPR.wcEndPrice = closeWCTkt.AverageFillPrice;
											}
										} //else if (doDeepTracing) Log(" oo oo oo oo THE WING CALL WAS ALREADY SOLD");
									}	
									
									tprsToClose.Add(cTPR);
								}
							}
										////  THE FOLLOWING WOULD EXECUTE IF ALGO EXERCISED THE WING CALL -- NOT CONTEMPLATED
    					}
    					

					} else {				/// !.HasUnderlying -- this is stock being assigned
						if (doDeepTracing) Log(" oo ASSIGNMENT OF UNDERLYING ORDER FOR : " + oeSymb);
						if (doDeepTracing) Log(" oo STOCK EXERCISE ORDER EVENT FOR: " +  order.Quantity + " shares." );
						
						didTheTrade = true;
						if(tradeRecs.Any(t => t!=null && t.uSymbol.Equals(oeSymb) & Math.Abs(t.uQty) == Math.Abs(order.Quantity*100M))) {		/// this failed on 2/6/23 to find tpr  // 2023-04-09: USE MATH.ABS() TO FIND QTY
							if (doDeepTracing) Log($" oo UPDATING TPR -- UNDERLYING END PRICE AND DATE {orderEvent.FillPrice.ToString()}");
							var uTPR = tradeRecs.Where(t => t!=null && t.uSymbol.Equals(oeSymb) & Math.Abs(t.uQty) == Math.Abs(order.Quantity*100M)).FirstOrDefault();
							
							tradeRecCount = 0;									// reset tradeRec Counter ??? may be obviated
							uTPR.uEndPrice = orderEvent.FillPrice;
							uTPR.endDate = orderEvent.UtcTime;

							if (uTPR.reasonForClose !=null || uTPR.reasonForClose != "") {
								uTPR.reasonForClose = uTPR.reasonForClose +  " oo oo  OPTIONS ASSIGNMENT -- UNDERLYING CLOSED IN NOT_HAS_UNDERLYING";
							} else uTPR.reasonForClose = " oo oo  OPTIONS ASSIGNMENT -- UNDERLYING CLOSED IN notHASUNDERLYING";
							
							
							if(symbolDataBySymbol.ContainsKey(oeSymb)) {
								if (doDeepTracing) Log($"  oo oo oo FOUND {oeSymb.Value} in SDBS.  Setting isRollable to False and marking it for removal from prospects list.");
								symbolDataBySymbol[oeSymb].isRollable = false;
								SymbolsToRemove.Add(oeSymb);

							}
							tprsToClose.Add(uTPR);

							
						} else {        				
  							if (doDeepTracing) Log($" oo oo oo oo => FAILED TO LOCATE {oeSymb.Value} TPR THAT HAS {(100M* order.Quantity).ToString()} SHARES. ");
						}
					}
    				if (doDeepTracing) Log(" ---------------------------------------------------------------------------");
					
				} // Order.Type = OrderType.OptionExercise  
				else 
				{
					if (doDeepTracing) Log(" OO ** ** NON EXERCISE ORDER -- " + oeSymb);
					if (doDeepTracing) Log(" OO ** ** " + order.Type + ": " + orderEvent.UtcTime + ": "  + orderEvent.Direction + "  ** OO ");	
					if (doDeepTracing) Log(" OO ** ** " + orderEvent.Status + ": " + orderEvent.Direction + " " + order.Quantity + " @ " + orderEvent.FillPrice );	
					
					if (oeSymb.HasUnderlying && order.Type == OrderType.Limit ) {							/// Option
						if (oeSymb.ID.OptionRight == OptionRight.Put)
						{
    						if (doDeepTracing) Log(" OO PUT OPTION LIMIT ORDER FOR : " + oeSymb);
    						if (doDeepTracing) Log(" OO PROCESSING TPR IN NEXT ON DATA   oo oo oo oo ");

							foreach (TradePerfRec pTPR in tradeRecs){
								//Debug($" OO TPR OO TPR OO {tpr.uSymbol.Value} is {(tpr.isOpen ? "open" : "closed")} and has {tpr.cQty.ToString()} calls.");
								if (pTPR.pSymbol.Equals(oeSymb) & pTPR.pQty == -order.Quantity) {
									if (doDeepTracing) Log(" oo oo FOUND SHORT PUT 1ST TPR oo ");
									pTPR.pEndPrice = orderEvent.FillPrice;
									if (doDeepTracing) Log(" OO ** Setting pEndPrice.");
								}
							}
							
							
						} else if (oeSymb.ID.OptionRight == OptionRight.Call) {
    						if (doDeepTracing) Log(" OO CALL OPTION LIMIT ORDER FOR : " + oeSymb);
    						if (doDeepTracing) Log(" OO PROCESSING TPR IN NEXT ON DATA   oo oo oo oo ");
							foreach (TradePerfRec cTPR in tradeRecs){
								if (cTPR.cSymbol.Equals(oeSymb) & cTPR.cQty == -order.Quantity) {
									cTPR.cEndPrice = orderEvent.FillPrice;
									if (doDeepTracing) Log(" OO ** Setting cEndPrice.");
								}
							}
							
							//if (doDeepTracing) Log(" OO NOTE     CALL EXPIRATION execute a market order to sell underlying");
						}
					} else if (oeSymb.HasUnderlying && order.Type == OrderType.Market) {
						if (oeSymb.ID.OptionRight == OptionRight.Put)
						{
    						if (doDeepTracing) Log(" OO PUT OPTION MARKET ORDER FOR : " + oeSymb);
    						if (doDeepTracing) Log(" OO PROCESSING TPR SYNCHRONOUSLY IN LINE oo oo oo ");
							
							/*if (tradeRecs.Any(tpr => tpr!=null && tpr.pSymbol.Equals(oeSymb) && tpr.pQty == -order.Quantity )){
								TradePerfRec tpRec = tradeRecs.Where(tpr => tpr!=null && tpr.pSymbol.Equals(oeSymb) && tpr.pQty == -order.Quantity).FirstOrDefault();
								tpRec.pEndPrice = orderEvent.FillPrice;

							}
							*/
							if (doDeepTracing) Log(" OO NOTE     ALGO-DRIVEN PUT market order");
							
						} else if (oeSymb.ID.OptionRight == OptionRight.Call) {
							
    						if (doDeepTracing) Log(" OO CALL OPTION MARKET ORDER FOR : " + oeSymb);
    						if (doDeepTracing) Log(" OO PROCESSING TPR SYNCHRONOUSLY IN LINE oo oo oo ");

							/*if (tradeRecs.Any(tpr => tpr!=null && tpr.cSymbol.Equals(oeSymb) && tpr.cQty == -order.Quantity )){
								TradePerfRec tpRec = tradeRecs.Where(tpr => tpr!=null && tpr.cSymbol.Equals(oeSymb) && tpr.cQty == -order.Quantity).FirstOrDefault();
								tpRec.cEndPrice = orderEvent.FillPrice;

							}
							*/
							if (doDeepTracing) Log(" OO NOTE     ALGO-DRIVEN CALL MARKET ORDER");
						}
						
					} else if (!oeSymb.HasUnderlying)	 {													// limit order
					
    					if (doDeepTracing) Log($" OO UNDERLYING ORDER FOR : {oeSymb.Value} IN NON-EXERCISE.");
						
					} else {					// NON EXERCISE ORDER DOES NOT HAVE UNDERLYING == STOCK

    					if (doDeepTracing) Log(" OO UNKNOWN ALGO ORDER ORDER FOR : " + oeSymb);
    						
						foreach (TradePerfRec uTPR in tradeRecs){
							if ( uTPR.uSymbol.Equals(oeSymb) & uTPR.uQty == -100M*order.Quantity) {
								Debug (" OO ** THERE IS A TPR THAT IS " + (uTPR.isOpen ? " OPEN" : " CLOSED"));
								uTPR.isOpen = false;
								uTPR.uEndPrice = orderEvent.FillPrice;
								uTPR.endDate = orderEvent.UtcTime;
								uTPR.reasonForClose = uTPR.reasonForClose + ":  Options Expiration next day onOpen";
							}
							//Plot("Stock Chart", "Sells", orderEvent.FillPrice);
						}
					}
					if (doDeepTracing) Log(" ---------------------------------------------------------------------------");
				}	// non exercise option order
     		}		// orderStatus = Filled
   		} catch (Exception errMsg)
       	{
 	       	if (doTracing) Log(" ERROR  in OnOrder() Event " + errMsg );
    		return;
       	}

		}
		
		class StockDataSource : BaseData
        {
			// *** ::: short VE 1&2 MSSQL generated TotalLoss >= $8 or >=10% LOSS 
			//private const string BacktestUrl = @"https://hyperionhedgefundcom-my.sharepoint.com/:x:/g/personal/crmcwilliams_hldfund_com/EbJeQsE8A85PmKRwhcKvKgIBc6VfGe_vqmBQU259KVzLKQ?e=nJbEdP&download=1";
			// *** ::: short VE 1&2 Corrected Date and Number @Parameters in SQL  TotalLoss >= $10
			//private const string BacktestUrl = @"https://hyperionhedgefundcom-my.sharepoint.com/:t:/g/personal/crmcwilliams_hldfund_com/EfWMhBW7TChIt9R6J1EaN-ABGnisGKSF2nFUVZqLaNBAwg?e=ZCy6SK&download=1";
			// *** ::: short VE 1&2 Corrected Date and Number @Parameters in SQL  TotalLoss >= $8
			//private const string BacktestUrl = @"https://hyperionhedgefundcom-my.sharepoint.com/:x:/g/personal/crmcwilliams_hldfund_com/EfSdJTFsPFNFoBaEdAWCBVEB9yzr1b96i95gtk1lus8ueA?e=pdovae&download=1";
			// *** ::: Charley's manual selection
			//private const string BacktestUrl = @"https://hyperionhedgefundcom-my.sharepoint.com/:x:/g/personal/crmcwilliams_hldfund_com/Ef7B98ZT-WBFq2FqDdKYsUIB22hMN1GAbg2Sqv33J5t4Zw?e=MtKP92&download=1";
			// *** ::: -- Standardized monthly FDOM
			//private const string BacktestUrl = @"https://hyperionhedgefundcom-my.sharepoint.com/:x:/g/personal/crmcwilliams_hldfund_com/ESFpfDn8miBDnAuwOiS2R0MB4sQTc-d5x2PF813KYeaniw?e=kQ7pOG&download=1";
			// *** ::: Price>$90 loss>10pct
			private const string BacktestUrl = @"https://hyperionhedgefundcom-my.sharepoint.com/:x:/g/personal/crmcwilliams_hldfund_com/EeMhiQJs8_JFh0-BLeHvwokBgDm4fw2ouAEM8f9fcw1Vyg?e=UCtbPE&download=1";
			// *** ::: Price$90 loss>15pct
			//private const string BacktestUrl = @"https://hyperionhedgefundcom-my.sharepoint.com/:x:/g/personal/crmcwilliams_hldfund_com/EYBLXTBWzKtCqe8P8h1CnWcBpMpTS5Sic4NZgEF3UuzfLg?e=W8Wato&download=1";
			/// <summary>
            /// The symbols to be selected
            /// </summary>
            public List<string> Symbols { get; set; }

            public StockDataSource()
            {
                // initialize our list to empty
                Symbols = new List<string>();
            }

            public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
            {
                //var url = isLiveMode ? LiveUrl : BacktestUrl;
				var url = BacktestUrl;
                return new SubscriptionDataSource(url, SubscriptionTransportMedium.RemoteFile);
            }

            public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
            {
                try
                {
                    // create a new StockDataSource and set the symbol using config.Symbol
                    var stocks = new StockDataSource {Symbol = config.Symbol};
                    // break our line into csv pieces
					char[] charsToTrim = {',', ' '};
					line = line.TrimEnd(charsToTrim);
                    var csv = line.ToCsv();
                    if (isLiveMode)
                    {
                        // our live mode format does not have a date in the first column
                        stocks.Time = date;
                        stocks.Symbols.AddRange(csv);
                    }
                    else
                    {
						if(strFilterTkr != ""){
							if(!ourUniverse.Securities.ContainsKey(strFilterTkr) ){
								stocks.Time = date;
								stocks.Symbols.Add(strFilterTkr);
							}
						} else {
							// our backtest mode format has the first column as date, parse it
							stocks.Time = DateTime.Parse(csv[0]);
							stocks.Symbols.AddRange(csv.Skip(1));
							//foreach (var smbl in stocks.Symbols) {
							//}
						}
					}
                    return stocks;
                }
                // return null if we encounter any errors
                //catch { return null; }
				catch (Exception eMsg) {
					var msg = eMsg;
					return null;
				}
            }
        }
	

		public override void OnEndOfAlgorithm()
        {
        	Debug($" -- ON END OF ALGORITHM !!");
			var dailyBars = "";
			string strObj = "";
			string strIndName = "";
			string strIndValue = "";

			String ProjectID = this.ProjectId.ToString();
			String AlgoID = this.AlgorithmId.ToString();
			ObjectStore.Save("AlgoID", AlgoID);
			ObjectStore.Save("ProjectID", ProjectID);
			Log($" |||| |||| LOGGING THE HOLDINGS ON END OF ALGORITHM");
			foreach(var kvp in Securities)
			{
				var security = kvp.Value;
				if (security.Invested)
				{
					Log($" |||| |||| ||||  HOLDINGS: {security.Symbol} : {security.Holdings.Quantity} @ {security.BidPrice} by {security.AskPrice}");
				}
			}
			foreach(var pair in symbolDataBySymbol){
				//Debug($" Processing SDBS for {pair.Key}");
				Symbol symb = pair.Key;
				SymbolData sd = pair.Value;
				
				if(strFilterTkr != ""){
					foreach (var indyWinKVP in sd.IndicatorsWindows) {			/// Dictionary<string, RollingWindow<IndicatorDataPoint>>
						var indyDataPointsWin = indyWinKVP.Value.Reverse();			/// RollingWindow<IndicatorDataPoint>
						
						strObj = "";
						strIndName = indyWinKVP.Key;
							
						// Log($" -- -- Processing SDBS Indicator: {indyWinKVP.Key}");
						foreach(IndicatorDataPoint indyDataPoint in indyDataPointsWin){
							strObj += $"{indyDataPoint.Time.ToString("yyyy-M-d")}, {indyDataPoint.Value.ToString("0.00")} \n";
							// Log($" -- -- -- {indyDataPoint.Time}, {indyDataPoint.Value.ToString("0.00")}");

						}
						ObjectStore.Save(pair.Key + "_" + strIndName, strObj);
						// Debug($" ObjStore Value saved: {strObj}");

						//var ObjStore = ObjectStore.ToReadOnlyDictionary();

						//foreach(var kvp in ObjStore){
						//	Debug($" ObjStore : {kvp.Key}");
						//}
					}
				}				
				try{
					if (sd.VDIData != null) {
						string columns = string.Join(", ", sd.VDIData.Columns.Cast<System.Data.DataColumn>().Select(column => column.ColumnName));
						//Log($"Columns: {columns}");

						// Log each row of the dataTable
						foreach (System.Data.DataRow row in sd.VDIData.Rows)
						{
							string rowData = string.Join(", ", row.ItemArray);
							//Log($"Row: {rowData}");
						}
					}
				} catch (Exception e) {
					if (doTracing) Log($" -- --- Exception: {e}");
				}
			}

			var ObjStore = ObjectStore.ToReadOnlyDictionary();
			/*
			Debug($" -- -- Writing out Object Store Values -- --");
			foreach(var kvp in ObjStore){
				//Debug($" ObjStore : {kvp.Key}");
			}
			*/
			foreach(var kvp in Securities)
			{
				var security = kvp.Value;
				if (security.Invested)
				{
					Debug($" |||| |||| ||||  HOLDINGS: {security.Symbol} : {security.Holdings.Quantity} @ {security.BidPrice} by {security.AskPrice}");
				}
			}


			string saveString = "";
			bool hasStock = false;
			bool hasPuts = false;
			bool hasCalls = false;
			
			var tprEnum = tradeRecs.GetEnumerator();

			while (tprEnum.MoveNext()) {
				TradePerfRec tpr = tprEnum.Current;
				
				if (tpr.isOpen) {
					if (tpr.uEndPrice == 0 && tpr.uSymbol != null) {
						if (doDeepTracing) Log($" --- --- --- Setting End Prices for {tpr.uSymbol.Value} to {Securities[tpr.uSymbol].Price}. ");
						if (Securities[tpr.uSymbol].HasData & Securities[tpr.uSymbol].Price != 0) tpr.uEndPrice = Securities[tpr.uSymbol].Price;
					}
					
					if (tpr.pEndPrice == 0 && tpr.pSymbol != null) {
						if (Securities[tpr.pSymbol].HasData & Securities[tpr.pSymbol].Price != 0) tpr.pEndPrice = Securities[tpr.pSymbol].Price;
					}

					if (tpr.cEndPrice == 0 && tpr.cSymbol != null) {
						if (Securities[tpr.cSymbol].HasData & Securities[tpr.cSymbol].Price != 0) tpr.cEndPrice = Securities[tpr.cSymbol].Price;
						
					}
					
					tpr.endDate = Time;
				}
			}
			string jsonString = ConvertTradePerfRec(tradeRecs);
			
			tprEnum = ETFRecs.GetEnumerator();

			while (tprEnum.MoveNext()) {
				TradePerfRec tpr = tprEnum.Current;
				
				if (tpr.isOpen) {
					if (tpr.uEndPrice == 0 && tpr.uSymbol != null) {
						if (doDeepTracing) Log($" --- --- --- Setting End Prices for {tpr.uSymbol.Value} to {Securities[tpr.uSymbol].Price}. ");
						if(Securities[tpr.uSymbol].HasData & Securities[tpr.uSymbol].Price != 0) tpr.uEndPrice = Securities[tpr.uSymbol].Price;
					}
					
					if (tpr.pEndPrice == 0 && tpr.pSymbol != null) {
						if(Securities[tpr.pSymbol].HasData & Securities[tpr.pSymbol].Price != 0) tpr.pEndPrice = Securities[tpr.pSymbol].Price;
					}

					if (tpr.cEndPrice == 0 && tpr.cSymbol != null) {
						if(Securities[tpr.cSymbol].HasData & Securities[tpr.cSymbol].Price != 0) tpr.cEndPrice = Securities[tpr.cSymbol].Price;
						
					}
					
					tpr.endDate = Time;
				}
			}
			//jsonString = ConvertTradePerfRec(ETFRecs);
		
		}
	    
		private bool CheckBadDate(DateTime checkDate)
        {
			if (!badDtParameter) return badDtParameter;
        	DateTime badDate1 = Convert.ToDateTime(GetParameter("BadDate"));
        	DateTime badDate2 = Convert.ToDateTime(GetParameter("BadDate2"));
        	DateTime badDate3 = Convert.ToDateTime(GetParameter("BadDate3"));
        	//DateTime badDate1 = new DateTime(2020, 1, 6, 9, 45, 0);
        	//DateTime badDate2 = new DateTime(2020, 11, 1, 13, 45, 0);
		  	if(checkDate.Equals(badDate1) | checkDate.Equals(badDate2) | checkDate.Equals(badDate3))
		  	{
		  		return true;
		  	} else {
		  		return false;
		  		
		  	}
        }	
        
    }  // class
    
    
} // namespace
#region imports
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using QuantConnect.Util;
    using QuantConnect.Data;
    using QuantConnect.Securities;
    using QuantConnect.Securities.Option;
 #endregion
using QuantConnect.Securities.Option;
namespace QuantConnect.Algorithm.CSharp {
	public partial class CollarAlgorithm : QCAlgorithm
	{
	
		public class PutSpread
		{
			public decimal stockPrice;				// 2
			public DateTime exDate;					// 3  may not be necessary
			public DateTime tradeDate;				// 4
			public DateTime putExpiry;				// 5
			public Symbol oldPutSymb;				// 6
			public Symbol newPutSymb;				// 7
			public decimal oldPutBid;				// 8
			public decimal newPutAsk;				// 9
			public decimal oldPutStrike;			// 10
			public decimal newPutStrike;			// 11
			public decimal newPutOpenInterest;		// 12
			//public decimal newPutDelta;
			//public decimal newPutGamma;
			//public decimal newPutVega;
			//public decimal newPutRho;
			//public decimal newPutTheta;
			//public decimal newPutImpliedVol;
			public decimal divAmt;					// 13
			public decimal divCount;				// 14
			public decimal divDollars;				// 15
			public decimal stkIncr;					// 16 appreciation in stock value
			public decimal intCost;					// 17
			public decimal downsideRisk;			// 18
			public decimal upsidePotential;			// 19
			public decimal netIncome;				// 20
			public decimal netOptions;				// 21
			public decimal haircut;					// 22 committed capital in a portfolio margin account
			public string description1;				// 23
			//public string description2;
			//public string description3;

			public override string ToString()
			{
				return this.description1;
			}

			public bool IsEmpty()
			{
				return this.description1.IsNullOrEmpty();
			}
		}
	    	public List<PutSpread> AssemblePutSpreads(Slice slc, Dictionary<int, DateTime> expiries, TradePerfRec tPRec,  IEnumerable<Symbol> allUndrOptSymbs, decimal sPrice, decimal incrAmt){
    		
  	    	// only roll puts up if the appreciation in stock price + the expected dividends is greater than the cost of the put spread + interest cost
  	    	// appreciation = incrAmt
  	    	// get the expected dividends


			// 1. Get 	a) tPRec.pSymbol
			//  		b) Strike and 
			//			c) old bidPrice
			// 2. Get Stock Price and tPRec.uStartPrice -- calculate appreciation
			// 3. Get sdbs.decOneYearPriceTarget_initial
			// 4. is 	a) current price > 1yrTarget	-- What is VERank now?  Is initial 1 year more than 2 months
			//			b) current price < 1yrTarget	-- What is 1yrTarget now?  
  	    	
  	    	
  	    	int yearsInTrade = 0;		// to calculate dividends
  	    	decimal monthsInTrade = 0;		// to calculate dividends
	    	int daysInTrade = 0;		// to calculate interest
	    	int intCost = 0;			// interest cost
	    	decimal dividends = 0.0M;
	    	
			int k = 1;					// initialize iterator for AddOptionContracts below
       		Symbol optSymbol;			// initialize option symbol for building the list of contracts
        	Option tempOption;			// initialize option contract for building list of contracts and obtaining pricing data
        	Option thisPutOpt;			// initialize option contract for building list of contracts and obtaining pricing data
    		

	    	var justDate = slc.Time.Date;							// separate out the DATEVALUE from the DateTime variable bc fedFundsRates are so indexed
			LUD.thisFFRate = LUD.fedFundsRates[justDate];			// fedFundsRates is a Dictionary of all dates where DateTime index are all 12:00:00am
	    	
	    	decimal oldPutPrem = Securities[tPRec.pSymbol].BidPrice;			// need the price at which we might sell the puts;
	    	
    		List<Option> putOptionsList = new List<Option>();
	
	    	DateTime oldPutExpiry = tPRec.expDate;								// use old put expiry for selecting put options to examine
	    	
    		var atmPut =  allUndrOptSymbs.Where(s => s.ID.OptionRight == OptionRight.Put)			// get the ATM put strike for selecting put options to examine
    									.OrderBy(s => Math.Abs(s.ID.StrikePrice - sPrice))
    									.FirstOrDefault();

    		if (haltProcessing && doTracing) {
    			Debug(" *********   *******   WE GOT AN ATM PUT " );
    		}
    		var atmStrike = atmPut.ID.StrikePrice;								// get the ATM strike 

			var lowStrike = tPRec.pStrike;
			var highStrike = atmStrike;
    		//var lowStrike = (1 - (maxPutOTM / (decimal)100)) * atmStrike;   	// ~~ for selecting put options to examine
    		//var highStrike = (decimal)1.1 * atmStrike;  						// ~~ for selecting put options to examine

			
			List<PutSpread> pSpreads = new List<PutSpread>();					// ~~ List for assembling filterd put options
    		
	   
    		var putSymbs = allUndrOptSymbs;				// declare the variable before the conditional branching
    		
			// can we get current Put Expiration date?
			
			if (doTracing) Debug("----------------------   PUTS ROLLUP EXPIRIES PASS 1  ----------------------------");
			if (doTracing) Debug("--" + stockPrice.ToString() +", " + expiries[2].ToString("MM/dd/yy") + ", " + expiries[3].ToString("MM/dd/yy") + ", " + expiries[4].ToString("MM/dd/yy") + ", " + expiries[5].ToString("MM/dd/yy"));
			
																	/*putSymbs =  allUndrOptSymbs.Where( o=> (DateTime.Compare(o.ID.Date, expiries[1])==0 | 
																		DateTime.Compare(o.ID.Date, expiries[2])==0 | 
																		DateTime.Compare(o.ID.Date, expiries[3])==0 |
																		DateTime.Compare(o.ID.Date, expiries[4])==0 ) &&
																	o.ID.OptionRight == OptionRight.Put &&
     																o.ID.StrikePrice >= lowStrike &&
     																o.ID.StrikePrice < atmStrike)
     																.OrderByDescending(o => o.ID.StrikePrice);
																	*/
			
			putSymbs =  allUndrOptSymbs.Where( o=> o.ID.Date.Subtract(slc.Time).Days >= 10 &
													o.ID.OptionRight == OptionRight.Put &
     												o.ID.StrikePrice >= lowStrike &
     												o.ID.StrikePrice <= atmStrike)
     									.OrderByDescending(o => o.ID.StrikePrice);
			
			
			if (haltProcessing) {
				if (doTracing) IterateChain(putSymbs, "putSymbols");
			}

			if (putSymbs == null | putSymbs.Count()== 0)
			{ 
				if (doTracing) Debug(" AP AP AP AP  putSymbs is null or empty "); 
				return pSpreads;
				
			}	// putSymbs !=null && putSymbs.Count() != 0 -- in other words continue		
    		
			var pEnumerator = putSymbs.GetEnumerator();							// convert the options contracts list to an enumerator

			while (pEnumerator.MoveNext())										// process the contracts enumerator to add the options
    		{
				optSymbol = pEnumerator.Current;
				tempOption = AddOptionContract(optSymbol, Resolution.Minute, true);
				tempOption.PriceModel = OptionPriceModels.BinomialTian();		/// necessary for Greeks
				putOptionsList.Add(tempOption);
    		}    		
			
			var putEnum = putOptionsList.GetEnumerator();						// get the enumerator to build the List<PutSpread>

			while (putEnum.MoveNext())
    		{
				thisPutOpt = putEnum.Current;
    			
				//if ( thisPutOpt.Expiry.Subtract(slc.Time).Days >= 10 ) {
			 	PutSpread pSpread = new PutSpread();	

    			pSpread.stockPrice = sPrice; 
    			pSpread.tradeDate = justDate;
    			pSpread.stkIncr = incrAmt;
    			pSpread.oldPutSymb = tPRec.pSymbol; 
    			pSpread.newPutSymb = thisPutOpt.Symbol;
    			pSpread.oldPutBid = oldPutPrem;
    			pSpread.newPutAsk = thisPutOpt.AskPrice;
				pSpread.oldPutStrike = tPRec.pSymbol.ID.StrikePrice;
				pSpread.newPutStrike = thisPutOpt.StrikePrice;
    			pSpread.putExpiry = thisPutOpt.Expiry;	
    			
    			daysInTrade = (thisPutOpt.Expiry - justDate).Days;									// use the new put option expiration to calculate potential days in trade
			 	pSpread.intCost = (LUD.thisFFRate + LUD.ibkrRateAdj)/LUD.workingDays * (decimal) daysInTrade * stockPrice;  

				monthsInTrade = ((thisPutOpt.Expiry.Year - justDate.Year) * 12) + (thisPutOpt.Expiry.Month - justDate.Month);
				
				pSpread.divCount = Math.Truncate(monthsInTrade/3.00M) + 1.00M;				// add 1 for the next dividend and 1 for every 3 months thereafter
				pSpread.divAmt = stockDividendAmount;
				pSpread.divDollars = stockDividendAmount * pSpread.divCount;
				// pSpread.divDollars = stockDividendAmount * pSpread.divCount;
				pSpread.divDollars = stockDividendAmount * 1M;								// for profit calc and filtering, omit more than one dividend.  Many PTS's end before 1st dividend is paid
			
				pSpread.netOptions = oldPutPrem - tPRec.pStartPrice - thisPutOpt.AskPrice;	// get the total net cost of the options trade (not the spread traded)
				pSpread.netIncome = incrAmt + pSpread.divDollars - pSpread.intCost;			// net potential profit including unrealized gain in underlying since initial trade
				//pSpread.newPutOpenInterest;
				//pSpread.newPutDelta;
				//pSpread.newPutGamma;
				//pSpread.newPutVega;
				//pSpread.newPutRho;
				//pSpread.newPutTheta;
				//pSpread.newPutImpliedVol;
				//pSpread.haircut;				// committed capital in a portfolio margin account
				//pSpread.description1;
				//pSpread.description2;

				pSpreads.Add(pSpread);
				//}		
    		}				
			return pSpreads;				// return filled pSpreads;
    		
    	}

    	// **********************   GetBestPutSpread	 **************************************
	    // ***  			This sub routine takes in the assembled List of PutSpreads
	    // ***				available in the Slice.Data and calculates the best spread to use
	    // ***				to the roll up the puts
	    // ***********************************************************************************
    	
    	public PutSpread GetBestPutSpread(List<PutSpread> pSpreads) {
    		PutSpread pSprd = new PutSpread();							// get a null empty PutSpread
    		
    		pSprd = pSpreads.Where(s => s.netIncome + s.netOptions > 0 ).OrderByDescending( s => (s.netIncome + s.netOptions)/Math.Abs(s.stockPrice - s.newPutStrike)).FirstOrDefault();
    		
    		if (haltProcessing) {
    			if (doTracing) Debug("          HALTED IN GETBESTPUTSPREAD -- CHECKING PSPREADS");
    			var orderedPSpreads = pSpreads.Where(s => s.netIncome + s.netOptions > 0 ).OrderByDescending( s => (s.netIncome + s.netOptions)/Math.Abs(s.stockPrice - s.newPutStrike));
				IterateOrderedPutSpreadList(orderedPSpreads);
    		}
    		// null pSpread can occur when sPrice>oldPStrike but (sPrice-oldPStrike)/oldPStrike < ~2%:   Also, rolling forward would cost money.
    		
    		
    		return pSprd;
    	}

			//decimal currPutBidPrice = algo.Securities[tradablePut].BidPrice;

									// determine if the loss on the put leg is greater than the intial "real potential loss".  If it is, exercise the position
									/*if ((this.pStartPrice - currPutBidPrice) > (this.uStartPrice + this.pStartPrice - this.cStartPrice)  )
									{
										if (LUD.doTracing) algo.Log(" TT ITM PUT EXPIRATION -- FORCE PUT ASSIGNMENT CHEAPER   OOOOOOOOOOO");					// EXERCISE THE PUT removing PUTs and STOCK. Buy back calls in OnOrder()
										
										var closeCallTicket = MarketOrder(shortedCallSymbol, -this.cQty);
										
										if (closeCallTicket.Status == OrderStatus.Filled)
										{
											this.cEndPrice = closeCallTicket.AverageFillPrice;
										}
										
										var putExerciseTicket = ExerciseOption(longPutSymbol,  this.pQty);
										potentialCollars.Clear();
										bestSSQRColumn = new SSQRColumn();
										if (LUD.doTracing) algo.Log(" **************  END ITM PUT CALC -- EXERCISED PUTS    ******");
										
										return isRolled;
										
									} */
			//bool goodThresh = (LUD.intVERating == 5 & LUD.decOneYearPriceTarget > 1.05m * stockPrice) | (LUD.intVERating > 3 & bestSSQRColumn.upsidePotential >=5);
	
 	}
}
#region imports

#endregion
namespace QuantConnect {

///				2020-12-03:			Arranged all trade pathways, usingDeltas and not, to utilze GetPotentialCollars() ///////
///				####-##-##:			in order to IterateOrderedMatrices solely when executing a trade.
///				2020-12-04:			Added [[bestSSQRColumn = new SSQRColumn();]] to prevent looping and Matrix Iteration after initial SSQRMatrix buiding
///				####-##-##			This was found to occur and created multiple copies of the same SSQR in subsequent OnData() events.
///				2020-12-07:			Corrected RollTheCollar to calculate callQty by putPrem/callPrem (as is done in ExecuteTheTrade()).  
///				####-##-##			Also added bool didTheTrade to IterateOrderedSSQRMatix solely when actually trading
///				2020-12-08			Found GetPotentialCollars for ABBV would only return 2 divs (not 3 or 4) in 2015-10.  April Options missing.  Has May '16 options
///				####-##-##			conferred with John, and decided to look further (LEAPS) for more possible trades.  Added fifthExpirationDate to GetOptionsExpiries()
///				2020-12-08			Prevented duplicate call/put contracts from being added to SSQRMatrix in AssembleSSQRMatrix (!SSQRMatrix.Any(o=>o.optSymbo == optSymbol)
///				2020-12-13			Re-configured assembleSSQRMatrix to put and call list enumarators with all the options for 2-5 dividends, and loop 1X
///				####-##-##			Build SSQR only occurs for calls >= put strike and expiration.
///				2020-12-13			Evaluation of SSQR Matrix reveals the potential of using call time spreads (selling longer dated calls to pay for puts)
///				2020-12-15			Saw several instances of divide-by-zero error when evaluating vcc/pot. loss (stockprice - putstrike)
///				####-##-##			decided to reformulate the algorithm to sort first by loss potential and then by VCC.
///				2020-12-16			SIGNIFICANT -- modified bestSSQRColumn to sort descending by Math.Abs(stockPrice-putStrike) then ascending by putPremium/callPremium to get lowest risk and least call coverage

///				2021-01-04			Captured DivideByZero errors when StockPrice = PutStrike in CCOR calculations
///				2021-01-06			Added LogTrace to turn Debug on/off
///				2021-01-06			Debug placing and filling of limit orders for Call and Put closure
///				2021-01-07			refined debug placing/filling of Call/Put closure -- include MKT orders to better trace
///				2021-01-19			debugged oldRollDate.  Never set initially and not always set in various branches of code.
///				2021-01-19			Found that in longer expirations, may try to set AddedMonths to 24.  Error where Months%12 =0
///				2021-01-21			Added code to exercise puts when rolling is more expensive than exercising.
///				2021-01-24			Added code to conditionally roll up puts when stock appreciates
///				2021-01-31			Added code in OnOrder() to detect call assignment so that the primary TradeRec collar PUTs are sold uEndPrice is recorded and record is closed
///				2021-01-31			Modified OTM code because in VCC put and call expirations may be different.   Old code didnt trap all OTM situations
///				2021-02-01			Implemented calling Divididend Check to move code bytes to a different .cs file
///				2021-02-05			Wrapped OnEndOfDay in try-catch as well as .GetOpenOrders() routines.
///				2021-02-05			Found that LimitOrderTicket.Update() was not executing -- replaced update with MarketOrder
///				2021-02-08			ERROR:	Found System.InvalidOperationException: Collection was modified; enumeration operation may not execute.	
///										Remedied this by creating a list<int> of oLOs.Indices to remove in a second step
///				2021-02-10			Version 13 Found that slightly OTM 2nd TPRs will not roll at expiration because they are OTM but spread is very small ($1.00).  Thus,
///										had to force exercise
///				2021-02-10			Version 13 Found that the orderTicket.Quantity follows the option, not the stock.  Have to multiply by 100M in order to find the TPR
///				2021-02-10			Version 14 wrote foreach(2ndTPR in SecondTPRs) to process additional 2nd TPRs
///				2021-02-12			Version 15 reduced minDivs on PutRoll to 1 and only look out to 4th Div, not 5th.   Found appreciating stocks move up faster and longer durations unnecessary
///				2021-02-15			changed formatting codes in IterateOrderedPutSpreads to make visible the ExpirationDate and to limit the decimals to 2 places
///				2021-02-17			fixed RollPut where expireDateDelta2P<1 and OTM--call Close2TPR.  If ITM, then Exercise PUT
///				2021-02-18			Verssion 16 Found the 2nd TPR loop was using "current2ndRec" (1st 2nd TPR) data, not the actual sTPR from the loop.   In situations with more than 1 2nd TPR, was totally wrong
///				2021-02-20			Version 17 Modified GetExpiries to ensure expires[1] is more than 10 days after the trade date
///				2021-02-21			modified to allow various paths, CheckDiv, CheckCall, CheckPut, & CheckOTM to execute serially until a good threshold and non-losing roll can be found 
///									until the last day, when a Kill or Close is called and forced.   Modified OnOrder to track LEAN-intitiated call assignment
///				2021-02-23			Add GrossPnL and SSQR.netIncome to TPRs for analysis of roll PnL
///				2021-02-28			Attempted evaluation of ITM based upon actual option premiums rather than an arbitrary 5% based solely upon strikes -- failed due to QC internal algo's
///				2021-03-03			Base 2ndTPR split based upon intitial short call premium.  Rationale is that stock appreciation above that number results in nullification of inititial short term capital collar credit.
///				2021-03-03			Modified 2ndTPR roll up based upon incrAmount > cost-to-sell-original-puts
///				2021-03-03			fixed a nit in creating thetaTPR.isSecondary -- make it false to prevent null pointers in processing puts in 2nd TPR Rec

///				2021-03-05			
///				2021-03-10			Converted to Wing Trade -- added PerfRec columns for wing call performance tracking and removed 2ndTPR Put Rolling and thetaCall processing
///				2021-03-12			Amended oLO (open limit order) processing to accomondate shoring calls to open collars and wing calls.    
///				2021-03-12			WING VERSION 3 ELIMINATED CONVERSION TRADES -- SET CALLSTRIKE >> PUTSTRIKE
///				2021-03-12			WING VERSION 4 FIXED WINGFACTOR ERROR IN ROLLS
///				2021-03-12			WING VERSION 4C implemented hasDividends check
///				2021-03-12			WING VERSION 4D replaced TPR iteration loop AtEndOfAlogrithm() with expanded line-by-line string concatenation.... could not get actual options symbols otherwise
///				2021-03-21			WING VERSION 5  adjusted DownsideRisk to use Collar.netBid.    Check for ITM WingCall to sell ahead of ITM ShortCall (new code in OnData() after Dividend Approachment

///				2021-10-17			Moved all CheckRoll.cs code for evaluating and processing rolls based upon expirations and options-monieness into TradePerfRec class.  
///									Then, in Main.cs OnData() the list of 1st TPR's are iterated and processed by calling tpr.CheckRoll() method.


				/*var OpenOrders = Transactions.GetOpenOrders();					// Get the open orders to search for open limit orders
				if (OpenOrders.Count() > 0) {									// process them only if there's any open
					foreach (var OrderTkt in OpenOrders){						// loop through and process open options limit orders (HasUnderlying)
						if (OrderTkt.Status == OrderStatus.Submitted && OrderTkt.Type == OrderType.Limit) {
							if (OrderTkt.Symbol.HasUnderlying) {
								if (OrderTkt.Symbol.ID.OptionRight == OptionRight.Call) {
									var orderUnderlyingPrice = Securities[OrderTkt.Symbol.ID.Underlying.Symbol].Price;
									var Ticket = Extensions.ToOrderTicket(OrderTkt,Securities.SecurityTransactionManager);
									var orderLimitPrice = Ticket.Get(OrderField.LimitPrice);
									var orderStrikePrice = Ticket.Symbol.ID.StrikePrice;
									if (orderLimitPrice < orderUnderlyingPrice - orderStrikePrice + 0.10M) {				/// this is the criteria for placing a call buyback limit order.   This contition will exist if the underlying price has moved up
										Ticket.Update(new UpdateOrderFields{LimitPrice = orderUnderlyingPrice - orderStrikePrice + 0.10M});
									}			
								} else if (OrderTkt.Symbol.ID.OptionRight == OptionRight.Put) {
									var orderUnderlyingPrice = Securities[OrderTkt.Symbol.ID.Underlying.Symbol].Price;
									var orderLimitPrice = OrderTkt.Get(OrderField.LimitPrice);
									var orderStrikePrice = OrderTkt.Symbol.ID.StrikePrice;
									if (orderLimitPrice > orderStrikePrice - orderUnderlyingPrice - 0.10M) {				/// this is the criteria for placing a put sell-to-close limit order.   This contition will exist if the underlying price has moved down.
										OrderTkt.Update(new UpdateOrderFields{LimitPrice = orderStrikePrice - orderUnderlyingPrice - 0.10M});
									}			
																}
								
							}
						}
						
					}
				}*/

					/*
					var OpenTickets = Transactions.GetOrderTickets();					// Get all the orders to search for open limit orders
					if (OpenTickets.Count() > 0) {										// process them only if there's any open
					Debug(" ||||||||        We have " + OpenTickets.Count() + " tickets");
					foreach (var Ticket in OpenTickets){							// loop through and process open options limit orders (HasUnderlying)
						if (Ticket.Status == OrderStatus.Submitted && Ticket.OrderType == OrderType.Limit) {
								if (Ticket.Symbol.HasUnderlying) {
									Debug(" ||||||||        Ticket for " + Ticket.Symbol + " is " + Ticket.Status + " submitted at " + Ticket.Time + " for " + Ticket.Quantity + ".");
									if ((int)data.Time.Subtract(Ticket.Time).TotalMinutes > 15) {
										if (Ticket.Symbol.ID.OptionRight == OptionRight.Call) {
											var orderUnderlyingPrice = Securities[Ticket.Symbol.ID.Underlying.Symbol].Price;
											var orderLimitPrice = Ticket.Get(OrderField.LimitPrice);
											var orderStrikePrice = Ticket.Symbol.ID.StrikePrice;
											var lPrice = orderUnderlyingPrice - orderStrikePrice + 0.10M;
											if (orderLimitPrice < orderUnderlyingPrice - orderStrikePrice + 0.10M) {				/// this is the criteria for placing a call buyback limit order.   This contition will exist if the underlying price has moved up
												//Debug(" ||||||||        with " + Ticket.Symbol.ID.Underlying.Symbol + " trading at " + orderUnderlyingPrice + ", updating " + Ticket.Symbol + "limit order to new limit price: " + lPrice );
												//Ticket.Update(new UpdateOrderFields{LimitPrice = orderUnderlyingPrice - orderStrikePrice + 0.10M});
												Ticket.Cancel();
												Debug(" ||||||||        With " + Ticket.Symbol.ID.Underlying.Symbol + " trading at " + orderUnderlyingPrice + ", updating " + Ticket.Quantity + " of " + Ticket.Symbol + "limit order to market order");
												var buyCallTkt = MarketOrder(Ticket.Symbol, Ticket.Quantity);
												if (buyCallTkt.Status == OrderStatus.Filled ){
													bool anyTPRs = tradeRecs.Any(tr => tr.cSymbol.Equals(Ticket.Symbol) && -tr.cQty == Ticket.Quantity);
													if (anyTPRs) {
														var callTradeRec = tradeRecs.Where(tr => tr.cSymbol.Equals(Ticket.Symbol) && -tr.cQty == Ticket.Quantity).FirstOrDefault();
														callTradeRec.cEndPrice = buyCallTkt.AverageFillPrice;
														//foreach (TradePerfRec tpr in callTradeRecs) {
															//tpr.cEndPrice = buyCallTkt.AverageFillPrice;
														//}
													}
												}
											}			
										} else if (Ticket.Symbol.ID.OptionRight == OptionRight.Put) {
											var orderUnderlyingPrice = Securities[Ticket.Symbol.ID.Underlying.Symbol].Price;
											var orderLimitPrice = Ticket.Get(OrderField.LimitPrice);
											var orderStrikePrice = Ticket.Symbol.ID.StrikePrice;
											var lPrice = orderStrikePrice - orderUnderlyingPrice - 0.10M;
											if (orderLimitPrice > orderStrikePrice - orderUnderlyingPrice - 0.10M) {				/// this is the criteria for placing a put sell-to-close limit order.   This contition will exist if the
												//Debug(" ||||||||        with " + Ticket.Symbol.ID.Underlying.Symbol + " trading at " + orderUnderlyingPrice + ", updating " + Ticket.Symbol + "limit order to new limit price: " + lPrice ); //underlying price has moved down.
												//Ticket.Update(new UpdateOrderFields{LimitPrice = orderStrikePrice - orderUnderlyingPrice - 0.10M});
												Ticket.Cancel();
												Debug(" ||||||||        With " + Ticket.Symbol.ID.Underlying.Symbol + " trading at " + orderUnderlyingPrice + ", updating " + Ticket.Quantity + " of " + Ticket.Symbol + "limit order to market order");
												
												var sellPutTkt = MarketOrder(Ticket.Symbol, Ticket.Quantity);
												if (sellPutTkt.Status == OrderStatus.Filled ){
													bool anyTPRs = tradeRecs.Any(tr => tr.pSymbol.Equals(Ticket.Symbol) && -tr.pQty == Ticket.Quantity);
													if (anyTPRs) {
														var putTradeRec = tradeRecs.Where(tr => tr.pSymbol.Equals(Ticket.Symbol) && -tr.pQty == Ticket.Quantity).FirstOrDefault();
														putTradeRec.pEndPrice = sellPutTkt.AverageFillPrice;
														//foreach (TradePerfRec tpr in putTradeRecs) {
															//tpr.pEndPrice = sellPutTkt.AverageFillPrice;
														//}
													}	// is there  TPR
												}		// if order filled
											}			// limit price needs to be changed	
										}					/// < 15" after order submission
									}					// PUT	
								}						// OPTION ORDER
							}							// FOR LOOP	
						}					
					}
					
				} catch (Exception errMsg)
	        	{
	 		       	Debug(" ERROR  " + errMsg );
	        		if (errMsg.Data.Count > 0) {
	            		Debug("  Extra details:");
	            		foreach (DictionaryEntry de in errMsg.Data)
	            			Debug("    Key: {0,-20}      Value: {1}'" + de.Key.ToString() + "'" + de.Value);
	        		}
	        	}	*/
	
	
		// **********************   IsFirstTradingDay   	******************************************
	    // ***  	Generalized function to find and return a DateTime for a given year, month, DayOfWeek 
	    // ***		and occurrence in the month.   In this case, it's the 3rd Friday
	    // ***		
	    // ********************************************************************************************
	
/*	    public bool IsFirstTradingDay(DateTime testDate)
	    {
	
			if (haltProcessing) {
				Debug("--- --- Logging IsFirstTradingDay() " + testDate.ToString());
			}

			if (testDate.DayOfWeek == DayOfWeek.Sunday | testDate.DayOfWeek == DayOfWeek.Saturday) return false;

			DateTime firstDayOfMonth = new DateTime(testDate.Year, testDate.Month, 1);

			while (USHoliday.Dates.Contains(firstDayOfMonth)) firstDayOfMonth = firstDayOfMonth.AddDays(1);
			while (firstDayOfMonth.DayOfWeek == DayOfWeek.Sunday || firstDayOfMonth.DayOfWeek == DayOfWeek.Saturday) firstDayOfMonth = firstDayOfMonth.AddDays(1);
			while (USHoliday.Dates.Contains(firstDayOfMonth)) firstDayOfMonth = firstDayOfMonth.AddDays(1);

			///Debug("First Day of Month is " + firstDayOfMonth.ToString());
			if (testDate.Month.Equals(firstDayOfMonth.Month) && testDate.Day.Equals(firstDayOfMonth.Day)) {return true; } else {return false;}
	    }

*/
}
 #region imports
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Globalization;
    using System.Drawing;
    using QuantConnect;
    using QuantConnect.Algorithm.Framework;
    using QuantConnect.Algorithm.Framework.Selection;
    using QuantConnect.Algorithm.Framework.Alphas;
    using QuantConnect.Algorithm.Framework.Portfolio;
    using QuantConnect.Algorithm.Framework.Execution;
    using QuantConnect.Algorithm.Framework.Risk;
    using QuantConnect.Parameters;
    using QuantConnect.Benchmarks;
    using QuantConnect.Brokerages;
    using QuantConnect.Util;
    using QuantConnect.Interfaces;
    using QuantConnect.Algorithm;
    using QuantConnect.Indicators;
    using QuantConnect.Data;
    using QuantConnect.Data.Consolidators;
    using QuantConnect.Data.Custom;
    using QuantConnect.DataSource;
    using QuantConnect.Data.Fundamental;
    using QuantConnect.Data.Market;
    using QuantConnect.Data.UniverseSelection;
    using QuantConnect.Notifications;
    using QuantConnect.Orders;
    using QuantConnect.Orders.Fees;
    using QuantConnect.Orders.Fills;
    using QuantConnect.Orders.Slippage;
    using QuantConnect.Scheduling;
    using QuantConnect.Securities;
    using QuantConnect.Securities.Equity;
    using QuantConnect.Securities.Future;
    using QuantConnect.Securities.Option;
    using QuantConnect.Securities.Forex;
    using QuantConnect.Securities.Crypto;
    using QuantConnect.Securities.Interfaces;
    using QuantConnect.Storage;
    using QuantConnect.Data.Custom.AlphaStreams;
    using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
    using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
#endregion
using QuantConnect.Securities.Option;
namespace QuantConnect.Algorithm.CSharp {
	public partial class CollarAlgorithm : QCAlgorithm
	{
		public class SSQRColumn
		{
			public decimal stockPrice = 0;
			public DateTime exDate = DateTime.Now;
			public DateTime putExpiry = DateTime.Now;
			public DateTime callExpiry = DateTime.Now;
			public int daysInPosition = 0;
			public decimal interestCost = 0;
			public Symbol uSymbol;
			public Symbol putSymbol;
			public Symbol callSymbol;
			public Symbol wCallSymbol;
			public decimal putPremium = 0;			// paid for buying the body
			public decimal callPremium = 0;			// received for selling back call
			public decimal wCallPremium = 0;		// paid for buying the wings
			public decimal putStrike = 0;
			public decimal callStrike = 0;
			public decimal wCallStrike = 0;
			public decimal putOpenInterest = 0;
			public decimal callOpenInterest = 0;
			public decimal putDelta = 0;
			public decimal callDelta = 0;
			public decimal wcDelta = 0;
			public decimal wingFactor = 0;
			public decimal putGamma = 0;
			public decimal callGamma = 0;
			public decimal wcGamma = 0;
			public decimal putVega = 0;
			public decimal callVega = 0;
			public decimal putRho = 0;
			public decimal callRho = 0;
			public decimal putTheta = 0;
			public decimal callTheta = 0;
			public decimal putImpliedVol = 0;
			public decimal callImpliedVol = 0;
			public decimal divAmt = 0;
			public int divCount = 0;
			public decimal downsideRisk = 0;
			public decimal upsidePotential = 0;
			public decimal netIncome = 0;
			public decimal netOptions = 0;
			public decimal divDollars = 0;
			public decimal haircut = 0;				// committed capital in a portfolio margin account
			public decimal ROC = 0;					// Return on Capital
			public decimal ROR = 0;					// Return on Risk
			public decimal CCOR = 0;				// Call Coverage over downside Risk
			public int intVERating;					// This month's VE Rating
			public decimal decMomentum;				// This month's VE momentum
			public decimal decOneMonthForecat;		// VE One Month Forecast
			public decimal decOneYearPriceTarget;	// VE One Year Target
			public int intMomentumRank;	  			// VE Momentum Rank

			public string description1 = "";
			public string description2 = "";
			//public string description3;

			public override string ToString()
			{
				return this.description1;
			}

			public bool IsEmpty()
			{
				return this.description1.IsNullOrEmpty();
			}



		}
		
	}
}
#region imports
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using QuantConnect.Util;
    using QuantConnect.Data;
    using QuantConnect.Data.Market;
    using QuantConnect.Data.UniverseSelection;
    using QuantConnect.Securities;
    using QuantConnect.Securities.Option;
#endregion
/////////////////////////////			2020-12-01:   Added CCOR member to SSQR Column and to description2 for SSQR Matrices spreadsheet
using System.Linq;
using QuantConnect.Securities.Option;
namespace QuantConnect.Algorithm.CSharp {

    //
    //	Make sure to change "BasicTemplateAlgorithm" to your algorithm class name, and that all
    //	files use "public partial class" if you want to split up your algorithm namespace into multiple files.
    //

    public partial class CollarAlgorithm : QCAlgorithm
    { 
   	   	// **********************   AssembleSSQRMatrix		 **************************************
	    // ***  	This Method will assemble the calls and puts into separate List<OptionContract>
	    // ***		Here the VE Ranking will determine the composition and ultimate selection of the SSQR		
	    // ***		5 - Highest probability and appreciation -->  Use lower put (-3 strikes) because less probability of individual downside so reduce cost
		// ***			Also use shorter duration call OTM to offset Put cost
		// ***		4 - Lower but postive probability of appreciation -->  Use -2 stike put to protect individual downside and write OTM calls with same expiration
		// ***		3 - Neutral probability of appreciation -->  tighten collar --> experiment with ITM call
		// ***		2 - Probably will decline in price in 12 monmths --> Don't do these stocks
		// ***		1 - Highest probability to decline in value -- >> don't do these stocks
	    // ***********************************************************************************

    	public void AssembleSSQRMatrix(QCAlgorithm algo, ref LookupData LD, Dictionary<int, DateTime> putExpiries, Dictionary<int, DateTime> callExpiries)
    	{
			int i = 1;
			if (LD.doTracing) algo.Log($" -- AA AA ASSEMBLE SSQR MATRIX FOR {thisSymbol}");
			Symbol symbU = LD.uSymbol;

			int strikesCnt = 0;
			decimal strikeStep = 0;
			decimal estTrgtPutStrk = 0;
			decimal estTrgtCallStrk = 0;
			decimal stockPrice = 0;
			
			//List<OptionChain> allUnderlyingOptions = new List<OptionChain>();			// chain object to get all options
        	//allUnderlyingOptions = thisSlice.OptionChains.Values.Where(u => u.Underlying.Symbol.Equals(symbU)).ToList();
			OptionChain allUnderlyingOptions = null;	// chain opbjec to get all contracts
			OptionChain putChain;						// chain object to get put contracts
			OptionChain callChain;						// chain object to get call contracts
			OptionChain wcChain;						// chain object to get wc contracts
			OptionChain atmChain;						// chain object to ATM call
			
			List<OptionContract> putContracts = new List<OptionContract>();
			List<OptionContract> callContracts = new List<OptionContract>();
			List<OptionContract> wCallContracts = new List<OptionContract>();
			
			OptionContract putContract;					// contract object to collect put greeks
			OptionContract callContract;				// contract object to collect call greeks
			//OptionContract wcContract;				// contract object to collect wing call greeks

			Greeks putGreeks;
			Greeks callGreeks;
			Greeks wcGreeks;

        	Slice thisSlice = algo.CurrentSlice;
        	DateTime tradeDate = thisSlice.Time;		// current date, presumed date of trade
			
			SSQRColumn thisSSQRColumn = new SSQRColumn();
								
			stockPrice = algo.Securities[symbU].Price;
        	
        	if(LD.doTracing) algo.Log("@@@@@  logging assembleSSQR processing for: " + symbU.ToString() + " Price in Securities object is " + stockPrice.ToString());
        	
        	//   if(!thisSlice.OptionChains.TryGetValue(SD.optSymbol, out allUnderlyingOptions)) return;  /// NOTE:  DOES NOT RETURN wcChain
        	
         	
			// var gotChain = thisSlice.OptionChains.TryGetValue(symbU.opt, out var thisChain);

			// if (!gotChain) {return;}
			// allUnderlyingOptions = thisChain;

			foreach(var chain in thisSlice.OptionChains.Values){
        		if (chain.Underlying.Symbol != symbU) { continue; }
        		allUnderlyingOptions = chain;
        		break;
        	}
        	
        	if (allUnderlyingOptions == null) {
				if (LD.doTracing) algo.Debug("@@@@@ @@  No options returned at " + thisSlice.Time + " for " + symbU.Value);
				return;        	// return null SSQRMatrix and pass control back to OnData()
        	} 


 	        // Get the ATM call contract 
    		var atmCall = allUnderlyingOptions.Where(s => s.Right == OptionRight.Call)
    											.OrderBy(s => Math.Abs(stockPrice - s.Strike))/// - stockPrice))
    											.FirstOrDefault();
    											
			var atmPut =  allUnderlyingOptions.Where(s => s.Right == OptionRight.Put)
    											.OrderBy(s => Math.Abs(stockPrice - s.Strike)) /// - stockPrice))
    											.FirstOrDefault();
    		

			var atmStrike = atmCall.Strike;
			if (atmStrike == 0 ) { return;}

			var firstITMCallStrike = allUnderlyingOptions.Where(s => s.Right == OptionRight.Call & s.Strike < stockPrice)
    											.OrderByDescending(s => s.Strike - stockPrice)/// - stockPrice))
    											.FirstOrDefault().Strike;
    					

			var lowestOTMPutStrike = allUnderlyingOptions.Where(s => s.Right == OptionRight.Put & s.Strike < stockPrice)
    											.OrderByDescending(s => s.Strike - stockPrice)/// - stockPrice))
    											.FirstOrDefault().Strike;

    		// var lowStrike = (1 - ((decimal)LD.maxPutOTM / (decimal)100)) * atmStrike;   	// ~~ eventually need a mechanism to determine strike steps

			var lowStrike = allUnderlyingOptions.Where(s => s.Right == OptionRight.Put)
    											.OrderByDescending(s => (stockPrice - s.Strike)) /// - stockPrice))
    											.FirstOrDefault().Strike;
    		
    		//var highStrike = (decimal)1.1 * atmStrike;  						// ~~ and use strike steps to set upper and lower bounds
			
			var highStrike = allUnderlyingOptions.Where(s => s.Right == OptionRight.Call)
    											.OrderByDescending(s => (s.Strike - stockPrice))/// - stockPrice))
    											.FirstOrDefault().Strike;

			// get the distinct strikes in a list to get a count.  With the count, and the range, get the strike steps.

			var strikesList = allUnderlyingOptions.Where( o=> (DateTime.Compare(o.Expiry, callExpiries[1])==0)).DistinctBy(o => o.Strike);
			strikesCnt = strikesList.Count();

			if (strikesCnt == 1){
				strikeStep = (decimal)highStrike - (decimal)lowStrike;
			} else { 
				strikeStep = ((decimal)highStrike - (decimal)lowStrike)/((decimal)strikesCnt - 1M);
			}
			if (strikeStep % 0.5m != 0) strikeStep = Math.Round(strikeStep/0.5m) * 0.5m;


	        int k = 1;					// initialize iterator for AddOptionContracts below
       		Symbol optSymbol;			// initialize option symbol for building the list of contracts
        	//Option tempOption;			// initialize option contract for building list of contracts and obtaining pricing data
    		List<Option> callOptionsList = new List<Option>();
    		List<Option> putOptionsList = new List<Option>();
    		List<Option> wcCallsList = new List<Option>();
    		
    		//DateTime whichExpiry = new DateTime();
    		
 			//daysInTrade = ((TimeSpan) (whichExpiry - tradeDate)).Days;	// get the # of days from trade date to expiry for carry cost 
			
			///////// NOTE :  CATCH THE EXCEPTION WHERE LOOKUP FAILS
			var justDate = tradeDate.Date;									// separate out the DATEVALUE from the DateTime variable
			LD.thisFFRate = LD.fedFundsRates[justDate];						//	fedFundsRates is a Dictionary where DateTime index are all 12:00:00am

			if (stockPrice <= 100m & strikeStep == 5) strikeStep = 2.5m; 			/// make adjustment to standardize options placement
			if (strikeStep <= 1.5m) strikeStep = 2m;									/// make adjustment to widen unusual case of $1 strike steps

			decimal VEDepPct = 0;
			/// IF THIS IS POST INITIALIZATION, IS THE ONE YEAR TARGET THE SAME, ABOVE OR BELOW THE INITIAL TARGET ??? ??? ??? ??? 
			if (symbolDataBySymbol.ContainsKey(symbU)){
				if (symbolDataBySymbol[symbU].decOneYearPriceTarget_Initial > LD.decOneYearPriceTarget){
					symbolDataBySymbol[symbU].decOneYearPriceTarget_Current = LD.decOneYearPriceTarget;
					VEDepPct = (stockPrice - LD.decOneYearPriceTarget) / stockPrice;
				} else {
					VEDepPct = (stockPrice - symbolDataBySymbol[symbU].decOneYearPriceTarget_Initial) / stockPrice;
					LD.initialTargetEndDate = symbolDataBySymbol[symbU].initialTargetEndDate;
				}
			}

			if (VEDepPct < 0 && symbolDataBySymbol[symbU].continueTrade) VEDepPct = .10m;					/// Depreciation is positive when the decOneYearPrice target is lower

			/*
			var distinctExpirations = allUnderlyingOptions.Select(contract => contract.Expiry).Distinct();
			foreach(var thingy in distinctExpirations){
				algo.Log($" --- --- --- Option Expiry: {thingy.ToShortDateString()}");
			}
			*/
			if (LD.doDeepTracing) algo.Debug($" @@@@@ @@@@@ --  Assembling {symbU.Value} SSQRs using VEDepPct : {stockPrice.ToString("0.00")} - {LD.decOneYearPriceTarget.ToString()} = {VEDepPct.ToString("P2")} | StrikeStep: {strikeStep.ToString()}."); 
			if (LD.doDeepTracing) algo.Debug($" @@@@@ @@@@@ --   High  --  Low  -- ATM-C  -- ITM-C --");
			if (LD.doDeepTracing) algo.Debug($" @@@@@ @@@@@ -- {highStrike.ToString("0.00").PadLeft(4,' ')}  --  {lowStrike.ToString("0.00").PadLeft(4,' ')}  --  {atmStrike.ToString("0.00").PadLeft(4,' ')}  --  {firstITMCallStrike.ToString("0.00").PadLeft(4,' ')} --");
			//switch (LD.intVERating) {
			switch (VEDepPct){
				case var _ when VEDepPct <= .03M:						// Stock is predicted to gain value, do a bull collar, write ITM Put, but its Total YLD including Dividends is negative and ranked in monthly file, or this is a roll
					LD.VECase = "Case -1";									// **** **** **** /// **** **** **** *** /// 2023-03-15 prohibited ITM call writing
					//estTrgtCallStrk = atmStrike - strikeStep;
					//estTrgtPutStrk = atmStrike - 1m * strikeStep;
					estTrgtPutStrk = atmStrike - strikeStep;
					estTrgtCallStrk = atmStrike + strikeStep;
					
					if (LD.doDeepTracing) algo.Debug($" @@@@@ @@@@@ @@@@ -- Case 1: {VEDepPct.ToString("P2")} less than 3%.  Call target: {estTrgtCallStrk.ToString("0.00")} / Put Target: {estTrgtPutStrk.ToString("0.00")} "); 

					callContracts = allUnderlyingOptions.Where( o=> o.Right == OptionRight.Call &&
														    DateTime.Compare(o.Expiry, tradeDate.AddMonths(3))<=0 &							// Shorts take only a few weeks to work.  
															//DateTime.Compare(o.Expiry, tradeDate.AddMonths(2))>=0 &
															o.Strike >= estTrgtCallStrk)
															//o.Strike > stockPrice)
														.OrderByDescending(o=>o.Expiry)
														.ThenBy(o => o.Strike)
														.ToList();

					putContracts =  allUnderlyingOptions.Where( o=> o.Right == OptionRight.Put && 
 													       	DateTime.Compare(o.Expiry, tradeDate.AddMonths(3))<=0 &							// get close, but don't exceed 1 year target.  Get as much put premium (theta) as possible by staying close to stock price
															//DateTime.Compare(o.Expiry, tradeDate.AddMonths(2))>=0 &
															o.Strike < stockPrice)
     													.OrderByDescending(o=>o.Expiry)
														.ThenByDescending(o => o.Strike)
														.ToList();

					break;
				case var _ when VEDepPct > .03M & VEDepPct <=.08M:	// Stock is predicted to lose some value.   Do a standard collar, place put as close to VEDepPct as possible.
					LD.VECase = "Case -2";

					estTrgtPutStrk = LUD.decOneYearPriceTarget + strikeStep;	// OneYearPriceTarget is below stock price.  Short a put just above that target to capture decline
					estTrgtCallStrk = atmStrike + strikeStep;				// Buy a call 1 strike step above stock price.

					if (LD.doDeepTracing) algo.Debug($" @@@@@ @@@@@ @@@@ -- Case 2: {VEDepPct.ToString("P2")} grtr betrween 3-8%.  Call target: {estTrgtCallStrk.ToString("0.00")} / Put Target: {estTrgtPutStrk.ToString("0.00")} "); 

					callContracts = allUnderlyingOptions.Where( o=> o.Right == OptionRight.Call &&
 													       	DateTime.Compare(o.Expiry, tradeDate.AddMonths(3))<=0 &							// Try getting options out to 3 months or more.  Get as much call premium (theta) as possible
															//DateTime.Compare(o.Expiry, tradeDate.AddMonths(2))>=0 &
															o.Strike >= estTrgtCallStrk)
														.OrderByDescending(o=>o.Expiry)
														.ThenBy(o => o.Strike)
														.ToList();

					putContracts =  allUnderlyingOptions.Where( o=> o.Right == OptionRight.Put && 
 													       	DateTime.Compare(o.Expiry, tradeDate.AddMonths(3))<=0 &							// Try getting options out to 3 months or more.   Get as much call premium (theta) as possible
															//DateTime.Compare(o.Expiry, tradeDate.AddMonths(2))>=0 &
															//o.Strike < stockPrice)
															o.Strike <= estTrgtPutStrk)
														.OrderByDescending(o=>o.Expiry)
     													.ThenByDescending(o => o.Strike)
														.ToList();
					
					
					
					break;
				case var _ when VEDepPct > .08M:
					LD.VECase = "Case -3";
					estTrgtCallStrk = atmStrike + 2m * strikeStep;											// /// //// DO NOT WRITE A CALL WHEN INITIALIZING
					estTrgtPutStrk = LUD.decOneYearPriceTarget + 2M * strikeStep;										// /// //// 2023-05-02 :: changed from - to +.  
					
					if (LD.doDeepTracing) algo.Debug($" @@@@@ @@@@@ @@@@ -- Case 3: {VEDepPct.ToString("P2")} grtr than 8%.  PUT target: --no put --/ CALL Target: {estTrgtCallStrk.ToString("0.00")} "); 

					callContracts =  allUnderlyingOptions.Where( o=> DateTime.Compare(o.Expiry, justDate.AddMonths(3))<=0  &
															//DateTime.Compare(o.Expiry, tradeDate.AddMonths(2))>=0 &
													       	o.Strike >= estTrgtCallStrk &
															o.Right == OptionRight.Call)
														.OrderByDescending(o => o.Expiry)
														.ThenBy(o => o.Strike)
														.ToList();
														//.FirstOrDefault();

					putContracts =  allUnderlyingOptions.Where( o=> o.Right == OptionRight.Put && 
															DateTime.Compare(o.Expiry, tradeDate.AddMonths(3))<=0 &							// Try getting options out to 3 months or more.   Get as much call premium (theta) as possible
															//DateTime.Compare(o.Expiry, tradeDate.AddMonths(2))>=0 &
															//o.Strike < stockPrice)
															o.Strike <= estTrgtPutStrk)
														.OrderByDescending(o=>o.Expiry)
														.ThenByDescending(o => o.Strike)
														.ToList();
					break;

			
			}				/// end switch   
			
			// /// /// COLLAR PROCESSING BEGINS HERE // /// /// ///
			
			if (putContracts == null | putContracts.Count() == 0) 			// ****************		Check if any puts are returned.   
			{
				if (LD.doTracing) algo.Debug($"@@@@@ -- @@@@ -- @@@@  get putContracts failed new paradigm in VE Rank {LUD.VECase}. Exit AssembleSSQRMatrices Method.");					///  use the expiries[1] date as the seed and find the subsequent 4 3-month expirations
				return;
			}

			if( callContracts == null | callContracts.Count() == 0)				/// ****************	Check if no calls are returned.  If not, try incrementing 1 month for VERating 4 & 5 or decrementing for 3's
			{	
				
				// if (LD.haltProcessing) {
					if (LD.doTracing) algo.Debug($"@@@@@ -- @@@@ -- @@@@  get CALL contracts failed 1st Pass try using +3 mos expiries in VE Case {LUD.VECase} ---------------");
					if (LD.doTracing) algo.Debug("--" + stockPrice.ToString() +", " + callExpiries[1].ToString("MM/dd/yy") + ", " + callExpiries[2].ToString("MM/dd/yy") + ", " + callExpiries[3].ToString("MM/dd/yy") + ", " + callExpiries[4].ToString("MM/dd/yy") + ", " + callExpiries[5].ToString("MM/dd/yy"));
					//if (doDeepTracing);(callSymbolsForThisExpiry, "callSymbols");
				// }
		
				putContracts = allUnderlyingOptions.Where( o=> o.Right==OptionRight.Put &&
													( DateTime.Compare(o.Expiry, putExpiries[2])==0 | 
													DateTime.Compare(o.Expiry, putExpiries[3]) == 0) &
													o.Strike <= estTrgtPutStrk)
													.OrderByDescending(o=>o.Expiry)
    												.ThenByDescending(o => o.Strike)
													.ToList();

				callContracts = allUnderlyingOptions.Where( o=> o.Right == OptionRight.Call &&
													( DateTime.Compare(o.Expiry, putExpiries[2])==0 | 
													DateTime.Compare(o.Expiry, putExpiries[3])==0) &
													o.Strike > estTrgtCallStrk)
													//o.Strike > stockPrice)
													.OrderByDescending(o=>o.Expiry)
													.ThenBy(o => o.Strike)
													.ToList();

				if (putContracts == null || putContracts.Count() == 0)
				{
					if (LD.doTracing) algo.Debug("@@@@@ -- @@@@ -- @@@@  PUT Expiries Failed 2nd Pass try every month -----------------");
					if (LD.doTracing) algo.Debug("--" + stockPrice.ToString() +", " + putExpiries[1].ToString("MM/dd/yy") + ", " + putExpiries[2].ToString("MM/dd/yy") + ", " + putExpiries[3].ToString("MM/dd/yy") + ", " + putExpiries[4].ToString("MM/dd/yy") + ", " + putExpiries[5].ToString("MM/dd/yy"));
					putExpiries[1] = FindNextOptionsExpiry(putExpiries[1], symbU.Value, -1);		///  //// //// *** *** *** *** used this for first time in 6F.... changed results
					putExpiries[2] = FindNextOptionsExpiry(putExpiries[1], symbU.Value, 1);
					putExpiries[3] = FindNextOptionsExpiry(putExpiries[1], symbU.Value, 2);
					//putExpiries[4] = FindNextOptionsExpiry(putExpiries[1], 3);
					//putExpiries[5] = FindNextOptionsExpiry(putExpiries[1], 5);
					//putExpiries[6] = FindNextOptionsExpiry(putExpiries[1], 6);
					// putExpiries[7] = FindNextOptionsExpiry(putExpiries[1], 7);
					// putExpiries[8] = FindNextOptionsExpiry(putExpiries[1], 8);
				
					putContracts = allUnderlyingOptions.Where( o=> o.Right==OptionRight.Put &&
														( DateTime.Compare(o.Expiry, putExpiries[2])==0 | 
														DateTime.Compare(o.Expiry, putExpiries[3])==0 ) &
														o.Strike <= estTrgtPutStrk)
														.OrderByDescending(o=>o.Expiry)
														.ThenByDescending(o => o.Strike)
														.ToList();

					callContracts = allUnderlyingOptions.Where( o=> o.Right == OptionRight.Call &&
														( DateTime.Compare(o.Expiry, putExpiries[2])==0 | 
														DateTime.Compare(o.Expiry, putExpiries[3])==0) &
														//o.Strike <= estTrgtCallStrk
														o.Strike > stockPrice)
														.OrderByDescending(o=>o.Expiry)
														.ThenBy(o => Math.Abs(estTrgtCallStrk - o.Strike))
														.ToList();

					if (putContracts == null || putContracts.Count() == 0)
					{
						if (LD.doTracing) algo.Debug("@@@@@ -- @@@@ -- @@@@  PUT Expiries Failed 3rd Pass return out -----------------");
						if (LD.doTracing) algo.Debug("--" + stockPrice.ToString() +", " + putExpiries[1].ToString("MM/dd/yy") + ", " + putExpiries[2].ToString("MM/dd/yy") + ", " + putExpiries[3].ToString("MM/dd/yy") + ", " + putExpiries[4].ToString("MM/dd/yy") + ", " + putExpiries[5].ToString("MM/dd/yy"));
						foreach(var opt in allUnderlyingOptions) {
							Debug($"@@@@@ @@@@@ @@@@@ {opt.ToString()}");
						}
						return;
					} else {
						if (LD.doTracing) algo.Debug("----------------------    PUT Expiries Succeeded on 3rd Pass  -----------------");
					}
				} else {
					if (LD.doTracing) algo.Debug("----------------------    PUT Expiries Succeeded on 2nd Pass  -----------------");
				}
			} 

			if (LD.doTracing) Debug("@@@@@ -- @@@@@ -- @@@@@ -- get putSymbolsForTheseExpiries succeeded. -- @@@@@ -- @@@@@ -- @@@@@ -- ");	

			var pEnumerator = putContracts.GetEnumerator();	

			while (pEnumerator.MoveNext())
			{
				var cEnumerator = callContracts.GetEnumerator();	

				putContract = pEnumerator.Current;
			
				//if (LD.doDeepTracing) algo.Debug($" ---- ---- ---- : {putContract.ToString()} target strike: {estTrgtPutStrk.ToString()}.");
				
				OptionContract wcContract = atmCall;

				while (cEnumerator.MoveNext())
				{
					callContract = cEnumerator.Current;
					//if (LD.doDeepTracing) algo.Debug($" ---- ---- ---- ---- {callContract.ToString()}");
					// if ((callContract.Strike > putContract.Strike & DateTime.Compare(callContract.Expiry, putContract.Expiry)>=0) | (callContract.Strike >= putContract.Strike & DateTime.Compare(callContract.Expiry,putContract.Expiry)>0 ))		// only add put/call combinations where call strike is equal to or above put strike and call expiry is later than put OR (c.strike>=put.strike AND c.Expiry>=p.Expiry)
					if (callContract.Strike > putContract.Strike & DateTime.Compare(callContract.Expiry, putContract.Expiry)==0) {
						//foreach (var wcContract in wCallContracts) {
							//if (wcContract.Strike > callContract.Strike ) {
							//thisSSQRColumn = buildSSQRColumn(putContract, callContract, wcContract, algo, LD);
							thisSSQRColumn = buildSSQRColumn(putContract, callContract, algo, LD);
							//}
						//}
						if (thisSSQRColumn != null) LD.SSQRMatrix.Add(thisSSQRColumn);
					}				//  if thisCallStrike == thisPutStrike
				}					//  while callEnum
			}						//  while putEnum	
			
			if (LD.doTracing) Debug($"  @@@@@ -- AA AA RETURNED {LD.SSQRMatrix.Count()} SSQR MATRICES FOR {LD.uSymbol}" );
			// if (LD.doDeepTracing) {
				// var orderedSSQRMatrix = LUD.SSQRMatrix.OrderByDescending(p => p.upsidePotential);
				// IterateOrderedSSQRMatrix(orderedSSQRMatrix);
			// }

    		return;
    		
    	} // AssembleSSQRMatrix
 
   		// **********************   buildSSQRColumn 4-parms  VE 4 and 5	**************************************
	    // ***  	This sub routine takes in the variables for the iterated put Options Lists
	    // ***		as well as the dividends count, dividend amount, and stock price
	    // ***		and returns an SSQRColumn to be added to the SSQRMatrix list
	    // ***************************************************************************************************
	    public SSQRColumn buildSSQRColumn(OptionContract perkPutOpt, OptionContract thisCallOpt,  QCAlgorithm algo, LookupData LD)
        //public SSQRColumn buildSSQRColumn(Option perkPutOpt, Option thisCallOpt, OptionContract pGrks, OptionContract cGrks, DateTime whichExpiry, DateTime tradeDate, DateTime exDate, int dividends, decimal amtDividend, decimal stockPrice, int daysInTrade, decimal intCost)
	    {
			
	    	
	    	decimal thisSpread = 1M;
	    	decimal wingFactor = .2M;											// factor to determine wings contract load
	    	
	    	int monthsInTrade = 0;
	    	int daysInTrade = 0;
	    	int dividends = 0;
	    	
	    	Slice thisSlice = algo.CurrentSlice;
			// LD.loadVEData(thisSlice.Time);									// load this instance of LUD with VE data from file.
	    	decimal stockPrice = algo.Securities[LD.uSymbol].Price;

			SSQRColumn thisColumn = new SSQRColumn();							// get a new SSQRColumn
			if (thisCallOpt == null) return thisColumn;							// 2023-02-10 -- trap null thisPutOpt -- crashed in new differentiated VE model
	    	if (thisCallOpt.AskPrice == 0) return thisColumn;					// don't build SSQRColumns with missing premium values

	    	DateTime tradeDate = algo.CurrentSlice.Time;

	    	daysInTrade = (thisCallOpt.Expiry - tradeDate).Days;
	    	decimal intCost = (LD.thisFFRate + LD.ibkrRateAdj)/LD.workingDays * (decimal) daysInTrade * stockPrice;  

			if (perkPutOpt!=null){
				//if (LUD.doDeepTracing) algo.Log($"	BSSQR BSSQR	- Logging 4-parameter buildSSQRColumn processing for {thisCallOpt.Symbol.Value}/{perkPutOpt.Symbol.Value}.");
			} else {
				//if (LUD.doDeepTracing) algo.Log($"	BSSQR BSSQR	- Logging 4-parameter buildSSQRColumn processing for {thisCallOpt.Symbol.Value}/--no put --.");
			}

	    	monthsInTrade = thisCallOpt.Expiry.Month - LD.exDivdnDate.Month;
	    	
	    	if( thisCallOpt.Expiry.Year != LD.exDivdnDate.Year) {
	    		monthsInTrade = monthsInTrade + 12;
	    	}

			if (divFrequency.Equals("monthly", StringComparison.OrdinalIgnoreCase)) {	    	
	    		dividends =  monthsInTrade + 1;
	    	} else {
	    		dividends = monthsInTrade/3 + 1;			// add 1 for the next dividend and 1 for every 3 months thereafter
	    	}
	    	
	    	thisColumn.uSymbol = LD.uSymbol;
	    	thisColumn.putSymbol = perkPutOpt != null ? perkPutOpt.Symbol : null;
	    	// thisColumn.wCallSymbol = wcOpt.Symbol;		// atm call for this column (based upon put)
	    	thisColumn.putPremium = perkPutOpt != null ? perkPutOpt.BidPrice : 0;
	    	thisColumn.exDate = LD.exDivdnDate;
	    	thisColumn.putExpiry = perkPutOpt != null ? perkPutOpt.Expiry : default(DateTime);
	    	thisColumn.putStrike = perkPutOpt != null ? perkPutOpt.Strike : 0;

    		thisColumn.putDelta = perkPutOpt != null ? perkPutOpt.Greeks.Delta : 0;
    		// thisColumn.wcDelta = wcOpt.Greeks.Delta;
    		thisColumn.putGamma = perkPutOpt != null ? perkPutOpt.Greeks.Gamma : 0;
    		thisColumn.putImpliedVol = perkPutOpt != null ? perkPutOpt.ImpliedVolatility : 0;
			
			thisColumn.callSymbol = thisCallOpt.Symbol;
			thisColumn.callPremium = thisCallOpt.AskPrice;
			thisColumn.callExpiry = thisCallOpt.Expiry;
			thisColumn.callStrike = thisCallOpt.Strike;
			thisColumn.callDelta = thisCallOpt.Greeks.Delta;
			thisColumn.callGamma = thisCallOpt.Greeks.Gamma;
			thisColumn.callImpliedVol = thisCallOpt.ImpliedVolatility;
			//thisColumn.wcGamma = wcOpt.Greeks.Gamma;
    		//thisColumn.putVega = thisPutOpt.Greeks.Vega;
    		//thisColumn.callVega = thisCallOpt.Greeks.Vega;
    		//thisColumn.putRho = thisPutOpt.Greeks.Rho;
    		//thisColumn.callRho = thisCallOpt.Greeks.Rho;
    		//thisColumn.putTheta = thisPutOpt.Greeks.Theta;
    		//thisColumn.callTheta = thisCallOpt.Greeks.Theta;
	    	
	    	thisColumn.divAmt = LD.divdndAmt;
	    	thisColumn.divCount = dividends;
	    	thisColumn.stockPrice = stockPrice;
	    	thisColumn.daysInPosition = daysInTrade;
	    	thisColumn.interestCost = intCost;
			thisColumn.intVERating = LD.intVERating;
			thisColumn.decMomentum = LD.decMomentum;
			thisColumn.decOneMonthForecat = LD.decOneMonthForecast;
			thisColumn.decOneYearPriceTarget = LD.decOneYearPriceTarget;
			thisColumn.intMomentumRank = LD.intMomentumRank;	  
	    	
			thisSpread = Math.Truncate(thisCallOpt.Strike - stockPrice);

	    	if (!LD.ibkrHairCuts.ContainsKey( (thisSpread)) )
	    	{
	    		//Debug("*^*^*^*^*^*^*^*^*^*^**^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*");
	    		//Debug("Make a haircut entry for " + (thisCallStrike - thisPutStrike).ToString());
	    		//Debug("*^*^*^*^*^*^*^*^*^*^**^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*");
	    		if (thisSpread < 5M)
				{
					thisColumn.haircut = .5M;
				} else {
					if (thisSpread % 0.5m != 0) thisSpread = Math.Round(thisSpread/0.5m) * 0.5m;
					if(thisSpread > 35m) thisSpread = 35;
					thisColumn.haircut = LD.ibkrHairCuts[thisSpread];
				}
	    	}else
	    	{
	    		thisColumn.haircut = 35m;
	    	}
        	
	    	decimal divDollars = -LD.divdndAmt * dividends;																						// dividends are negative.  We pay dividends when we short.
	    	thisColumn.divDollars = divDollars;
	    	
	    	decimal stockLossIfCalled = 0;																										// loss=0 if stkPrice>putStrike, otherwise negative	***Loss (negative value) if ITM calls are assigned (0 if #calls<#puts)
	    	
			decimal perkPutPremium = 0;
			if (perkPutOpt != null) perkPutPremium = perkPutOpt.BidPrice;

	    	decimal netOptions = -thisColumn.callPremium + thisColumn.putPremium;																/// netOptions is a cost and equals negative callPrem (expense) plus positive put premium (income)
	    	thisColumn.netOptions = netOptions;

	    	thisColumn.netIncome = divDollars + netOptions - intCost; 																			// Net Income in SSQR.xls subtracts interest cost but does not allow for appreciation to OTM call strike /// obviated in Wing System which has an upside long call
	    	
			if (wingFactor < 0)  wingFactor = 0;
			if (wingFactor > 0.2M ) wingFactor = 0.2M;
	    	//thisColumn.wingFactor = wingFactor;
			thisColumn.wingFactor = 0;																											// for VE statistical analysis, set WingFactor to 0.  Don't do wings
	    	
	    	thisColumn.ROC = (divDollars + netOptions + stockLossIfCalled - intCost) / thisColumn.haircut;  									// store ROC for statistical analysis
	    	
	    	// 2021-03-21 -- (factored in netOptions into downsideRisk calculation)
	    	//decimal downsideRisk = thisPutStrike - stockPrice + divDollars + netOptions - intCost; 											// downside risk is defined as the potential loss due to stock price depreciation _
	    	decimal downsideRisk = ((stockPrice - thisColumn.netOptions) < thisColumn.callStrike) ? thisColumn.callStrike - stockPrice + netOptions - thisColumn.interestCost + divDollars: thisColumn.interestCost + divDollars + netOptions;	// downside risk is the net price of the collar - putStrike (deliberately discounts dividends as they are not guaranteed past the declared dividend)
	    	thisColumn.downsideRisk = downsideRisk;																								// subtracts dividends collected and net options premiums and intCost
	    	decimal upsidePotential = 0;
			
			if(perkPutOpt!=null) {
	    		upsidePotential = LD.decOneYearPriceTarget > perkPutOpt.Strike ? stockPrice - LD.decOneYearPriceTarget + divDollars + netOptions - intCost : stockPrice - perkPutOpt.Strike + divDollars + netOptions - intCost ;		// When writing OTM puts, there is a potential upside appreciation from net collar cost to the put strike.
			} else {
	    		upsidePotential = stockPrice - LD.decOneYearPriceTarget + divDollars + netOptions - intCost;									// When writing OTM puts, there is a potential upside appreciation from net collar cost to the put strike.
			}
			thisColumn.upsidePotential = upsidePotential;																						
	    	
	    	// 2021-03-24 -- -- changed sign on downsideRisk from negative to positive.  Earlier iterations represented downside risk as negative (putStrike - stock purchase price).   
	    	thisColumn.ROR = downsideRisk == 0 ? upsidePotential : upsidePotential/downsideRisk;															// store ROR for statistical analysis
	    		
	    	/*if (stockPrice == thisPutStrike) {
	    		thisColumn.CCOR = (1 - thisPutPrem/thisCallPrem)/0.01M;																			// get the maximum upside potential for a unit of actual risk
	    	} else {
	    		thisColumn.CCOR = (1 - thisPutPrem/thisCallPrem)/(stockPrice - thisPutStrike);	
	    	} */
	    	
	    	//   2021-03-21 -- -- changed to ordered by downsideRisk/upsidePotential
	    	//thisColumn.CCOR = netOptions/downsideRisk;		// get the maximum upside potential for a unit of actual risk
	    	thisColumn.CCOR = downsideRisk/upsidePotential;
	    	
	    	thisColumn.description1 = "Combination in " + LD.uSymbol  + " @ " + stockPrice + " is the " + thisColumn.putStrike + "/" + thisColumn.callStrike + " collar  ";
	    	
			/*
			thisColumn.description2 = thisColumn.uSymbol.Value;
			thisColumn.description2 = stockPrice.ToString();
			thisColumn.description2 = LD.exDivdnDate.ToString();
			thisColumn.description2 = thisColumn.putExpiry.ToString();
			thisColumn.description2 = thisColumn.callExpiry.ToString();
			thisColumn.description2 = thisColumn.putStrike.ToString();
			thisColumn.description2 = thisColumn.putPremium.ToString();
			thisColumn.description2 = thisColumn.callStrike.ToString();
			thisColumn.description2 = thisColumn.callPremium.ToString();
			thisColumn.description2 = thisColumn.putDelta.ToString();
			thisColumn.description2 = thisColumn.callDelta.ToString();
			thisColumn.description2 = thisColumn.wingFactor.ToString();
			*/

	    	try{
			thisColumn.description2 = "," + thisColumn.uSymbol.Value + "," + String.Format("{0:0.00}", stockPrice) + "," + LD.exDivdnDate.ToString("MM/dd/yy") + "," 
	    		+ dividends + "," + String.Format("{0:0.00}", LD.divdndAmt) + "," + String.Format("{0:0.00}",divDollars) + "," + daysInTrade + ", " 
	    		+ String.Format("{0:0.00}", intCost) + ", " + thisColumn.putExpiry.ToString("MM/dd/yy") + ", " + thisColumn.callExpiry.ToString("MM/dd/yy") + ", " 
	    		+ String.Format("{0:0.00}",thisColumn.putStrike) + ", " + String.Format("{0:0.00}",thisColumn.putPremium) + ", " 
	    		+ String.Format("{0:0.00}",thisColumn.callStrike) + ", " + String.Format("{0:0.00}",thisColumn.callPremium) + ", " 
	    		+ "-no wingcall-" + ", " + "-no wingcall-" + ", "
				+ String.Format("{0:0.00}",thisColumn.putDelta) + ", " + String.Format("{0:0.00}",thisColumn.callDelta) + ", "
	    		+ String.Format("{0:0.00}",thisColumn.netOptions) + ", " + String.Format("{0:0.00}", thisColumn.netIncome) + ", " 
				+ String.Format("{0:0.00}",thisColumn.intVERating) + ", " + String.Format("{0:0.00}",thisColumn.decMomentum) + ", " + String.Format("{0:0.00}",thisColumn.decOneYearPriceTarget) + ", " 
				+ String.Format("{0:0.00}", thisColumn.haircut) + ", " + String.Format("{0:0.00}",thisColumn.ROC) + "," + String.Format("{0:0.00}", thisColumn.upsidePotential) + "," 
	    		+ String.Format("{0:0.00}", thisColumn.downsideRisk) + "," + String.Format("{0:0.00}",thisColumn.ROR) + "," + String.Format("{0:0.00}", thisColumn.CCOR ) + ","
	    		+ "-no wingcall-," + (thisColumn.putSymbol!=null ? thisColumn.putSymbol.Value : "-no put-") + "," + (thisColumn.callSymbol!=null ? thisColumn.callSymbol.Value : "-no call-") + "," + String.Format("{0:0.00}", thisColumn.decOneYearPriceTarget);
			} catch (Exception msg) {
				Debug($" @@@@@ -- @@@@@ -- Description2 has some issue: {msg}");

			}
	    	return thisColumn;
	    }

	    // **********************   GetOptionsExpiries **************************************
	    // ***  			Use this to find and return the next 4 options expirations expirations dates
	    // ***				Function will determine if a date is a holiday and subtract 1 day
		// ***				Target next 3 Ex-Datas whether 1st is Slice.Time.Month or later.
	    // ***********************************************************************************
	    public Dictionary<int, DateTime> GetOptionExpiries(DateTime tradeD, LookupData lookupD, DateTime thisMonthExpiry, bool isPrimary, bool isCall){
				// Initialize expiration date variables //

				DateTime nextExDate = lookupD.exDivdnDate;
				String tkr = lookupD.uSymbol.Value;

        		DateTime firstExpiry = new DateTime();
        		DateTime secondExpiry = new DateTime();
        		DateTime thirdExpiry = new DateTime();
        		DateTime fourthExpiry = new DateTime();
        		DateTime fifthExpiry = new DateTime();
        		DateTime sixthExpiry = new DateTime();
        		// DateTime seventhExpiry = new DateTime();
        		// DateTime eigthExpiry = new DateTime();
        		// DateTime ninthExpiry = new DateTime();
        		// DateTime tenthExpiry = new DateTime();
        		// DateTime eleventhExpiry = new DateTime();
        		// DateTime twelvethExpiry = new DateTime();
        		// DateTime thirteenthExpiry = new DateTime();
        		

        		// Initialize the dictionary for return
        		// 1 : first expiry
        		// 2 : second expiry...
        		Dictionary<int, DateTime> expiries = new Dictionary<int, DateTime>();
        		
        		// is the nextExDate before or after the 3rd Friday?  Before ? use this month expiration
        		// After ? use next month's expiration.

				if (!isCall & isPrimary)													// isPrimary ? 1stTPR : 2ndTPR    1stTPR do monthly options every quarter : 2ndTPR do monthly options every month
        		{
	        		if (DateTime.Compare(nextExDate, thisMonthExpiry) <= 0)
	        		{
	        			firstExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 0);		// first figure out the options expiry for exDivDate month
	        
	        			if (firstExpiry.Subtract(tradeD).Days <= 10) {					// if firstExpiry is less than 10 days after tradeDate, assignment risk is too high.   Move expiries back a month 
		        			firstExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 1);
		        			secondExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 4);
		        			thirdExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 7);
		        			fourthExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 10);
		        			fifthExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 13);
	        			} else 
	        			{
		        			secondExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 3);	
		        			thirdExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 6);
		        			fourthExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 9);
		        			fifthExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 12);
	        			}
	        		} else
	        		{
	        			firstExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 1);
	        			secondExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 4);
	        			thirdExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 7);
	        			fourthExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 10);
	        			fifthExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 13);
	        		}
        		} else {																// this is for 2ndTPRs -- monthly options every month to catch some
	        		if (DateTime.Compare(nextExDate, thisMonthExpiry) <= 0)
	        		{
	        			firstExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 0);
	        			secondExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 1);	
	        			thirdExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 2);
	        			fourthExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 3);
	        			fifthExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 4);
						sixthExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 5);
						// seventhExpiry = FindNextOptionsExpiry(thisMonthExpiry, 6);
						// eigthExpiry = FindNextOptionsExpiry(thisMonthExpiry, 7);
						// ninthExpiry = FindNextOptionsExpiry(thisMonthExpiry, 8);
						// tenthExpiry	= FindNextOptionsExpiry(thisMonthExpiry, 9);
						// eleventhExpiry = FindNextOptionsExpiry(thisMonthExpiry, 10);
						// twelvethExpiry= FindNextOptionsExpiry(thisMonthExpiry, 11);
						// thirteenthExpiry = FindNextOptionsExpiry(thisMonthExpiry, 12);

	        		}else
	        		{
	        			firstExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 1);
	        			secondExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 2);
	        			thirdExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 3);
	        			fourthExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 4);
	        			fifthExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 5);
						sixthExpiry = FindNextOptionsExpiry(thisMonthExpiry, tkr, 6);
						// seventhExpiry = FindNextOptionsExpiry(thisMonthExpiry, 7);
						// eigthExpiry = FindNextOptionsExpiry(thisMonthExpiry, 8);
						// ninthExpiry = FindNextOptionsExpiry(thisMonthExpiry, 9);
						// tenthExpiry	= FindNextOptionsExpiry(thisMonthExpiry, 10);
						// eleventhExpiry = FindNextOptionsExpiry(thisMonthExpiry, 11);
						// twelvethExpiry= FindNextOptionsExpiry(thisMonthExpiry, 12);
						// thirteenthExpiry = FindNextOptionsExpiry(thisMonthExpiry, 13);
	        		}
        			
        		}
           		expiries.Add(1, firstExpiry);
        		expiries.Add(2, secondExpiry);
        		expiries.Add(3, thirdExpiry);
        		expiries.Add(4, fourthExpiry);
        		expiries.Add(5, fifthExpiry);
	       		expiries.Add(6, sixthExpiry);
       			// expiries.Add(7, seventhExpiry);
       			// expiries.Add(8, eigthExpiry);
				if (isCall) {
        			// expiries.Add(9, ninthExpiry);
        			// expiries.Add(10, tenthExpiry);
        			// expiries.Add(11, eleventhExpiry);
        			// expiries.Add(12, twelvethExpiry);
        			// expiries.Add(13, thirteenthExpiry);
				}
        		return expiries;
	    }
	    
	    // **********************   FindNextOptionsExpiry    **************************************
	    // ***  	Use this to find and return the next options expirations date x months ahead
	    // ***		Check the new date to make sure it isn't a holiday and if it is, subtract 1 day
	    // ********************************************************************************************
	
	    public DateTime FindNextOptionsExpiry(DateTime thisExpiry, string tkr, int addedMonths){
	    	// Given a 3rd friday expiration, it will find the next 3rd friday expiration, addedMonths ahead
	    	// figure out how to handle holidays such as Good Friday, April 19, 2019.
	    	// ****************  should this be amended for non-quarterly dividend frequencies?  ****************
	    	int year = thisExpiry.Year;
	    	int month = thisExpiry.Month;
	    	
	    	while (addedMonths >= 12) {
	    		year = year + 1;
	    		addedMonths = addedMonths - 12;
	    	}
	    	
			// adjust if month = 0
			if(month + addedMonths == 0) {
				month = 12;
			} else {
				month = month + addedMonths;
			}

	    	// Adjust if bigger than 12
	    	if(month > 12){
	    		month = month % 12; 
	    		year = year + 1;
	    	}


			if (haltProcessing) {
				Debug("--- --- Logging FindNextOptionsExpiry() " + year.ToString() + "-" + month.ToString() );
			}

			DateTime findDate = FindDay(year, month, DayOfWeek.Friday, 3);
			
     		// Evaluate if found expirations fall upon holidays and if they do, decrement them 1 day
			var securityExchangeHours = Securities[tkr].Exchange.Hours;
			while(!securityExchangeHours.IsDateOpen(findDate)) findDate = securityExchangeHours.GetPreviousTradingDay(findDate);
   	    	
	    	return findDate;
	    }


		// **********************   FindDay	(options expiry)	***************************************
	    // ***  	Generalized function to find and return a DateTime for a given year, month, DayOfWeek 
	    // ***		and occurrence in the month.   In this case, it's the 3rd Friday
	    // ***		
	    // ********************************************************************************************
	
	    public DateTime FindDay(int year, int month, DayOfWeek Day, int occurrence)
	    {
	
			if (haltProcessing) {
				//Debug("--- --- Logging FindDay() " + year.ToString() + "-" + month.ToString() + "-" + Day.ToString() + ", at " + occurrence.ToString() + " day");
			}
	
	    	// Given a valid month, it will find the datetime for the 3rd friday of the month
	
	        if (occurrence <= 0 || occurrence > 5)
	            throw new Exception("occurrence is invalid");
	
	        DateTime firstDayOfMonth = new DateTime(year, month, 1);
	        //Substract first day of the month with the required day of the week 
	        var daysneeded = (int)Day - (int)firstDayOfMonth.DayOfWeek;
	        //if it is less than zero we need to get the next week day (add 7 days)
	        if (daysneeded < 0) daysneeded = daysneeded + 7;
	        //DayOfWeek is zero index based; multiply by the occurrence to get the day
	        var resultedDay = (daysneeded + 1) + (7 * (occurrence - 1));
	
	        if (resultedDay > (firstDayOfMonth.AddMonths(1) - firstDayOfMonth).Days)
	            throw new Exception(String.Format("No {0} occurrence(s) of {1} in the required month", occurrence, Day.ToString()));
            if (month == 2) {
            	if (year == 2016 | year == 2020) {
            		if (resultedDay > 29) {
            			resultedDay = resultedDay - 29;
            			month = 3;
            		}
            	} else {
            		if (resultedDay > 28) {
            			resultedDay = resultedDay - 28;
            			month = 3;
            		}
            	}
            }
			
			try
			{
	        	return new DateTime(year, month, resultedDay);
			}
			catch
			{
				throw new Exception($"Invalid date: {year}/{month}/{resultedDay}");
			}
	    }

		// **********************   IsLastTradingDay   	******************************************
	    // ***  	Generalized function to find and return a DateTime for a given year, month, DayOfWeek 
	    // ***		and occurrence in the month.  
	    // ********************************************************************************************
	    public bool IsLastTradingDay(DateTime testDate)
	    {
			DateTime nextTestDate = testDate.AddMonths((1));
			
			DateTime lastDayOfMonth = new DateTime(nextTestDate.Year, nextTestDate.Month, 1).AddDays(-1);

			var securityExchangeHours = Securities["SPY"].Exchange.Hours;
			while(!securityExchangeHours.IsDateOpen(lastDayOfMonth)) lastDayOfMonth = securityExchangeHours.GetPreviousTradingDay(lastDayOfMonth);

			///Debug("First Day of Month is " + lastDayOfMonth.ToString());
			if (testDate.Month.Equals(lastDayOfMonth.Month) && testDate.Day.Equals(lastDayOfMonth.Day)) {return true; } else {return false;}
	    }

	    public bool IsFirstTradingDay(DateTime testDate, string testTicker)
	    {
			DateTime firstDayOfMonth = new DateTime(testDate.Year, testDate.Month, 1);

			var securityExchangeHours = Securities[testTicker].Exchange.Hours;
			while(!securityExchangeHours.IsDateOpen(firstDayOfMonth)) firstDayOfMonth = securityExchangeHours.GetNextTradingDay(firstDayOfMonth);

			return testDate.Day == firstDayOfMonth.Day;

	    }

		// **********************   Get Last Options Expiry Day   	************************************
	    // ***  	Generalized function to find and return a DateTime for a given year, month, DayOfWeek 
	    // ***		and occurrence in the month.  
	    // ********************************************************************************************
	    public DateTime GetLastOptionsExpiry(DateTime testDate)
	    {

			DateTime nextTestDate = testDate.AddMonths((1));

			DateTime lastDayOfMonth = new DateTime(nextTestDate.Year, nextTestDate.Month, 1).AddDays(-1);

			var securityExchangeHours = Securities["SPY"].Exchange.Hours;
			while(!securityExchangeHours.IsDateOpen(lastDayOfMonth)) lastDayOfMonth = securityExchangeHours.GetPreviousTradingDay(lastDayOfMonth);

			///Debug("First Day of Month is " + lastDayOfMonth.ToString());
			return lastDayOfMonth;
	    }
		// **********************   IterateChain		*******************************************************
	    // ***  	Generalized function to iterate through and print members of an IEnumerable
	    // ***		This is used for debugging only
	    // ********************************************************************************************
		public void IterateChain(IEnumerable<Symbol> thisChain, string chainName)
        {	
        	int k = 1;
       		Symbol optSymbol;
        	var enumerator = thisChain.GetEnumerator();	
        	
        	//Debug("  ||||||||||||||||||||||||||||||||   NEW OPTION SYMBOL CHAIN  |||||||||||||||||||||||||||||||");
        	//Debug("There are " + thisChain.Count() + " options symbols in this list of chains, " + chainName);
        	
        	
        	while (enumerator.MoveNext()) 
        	{
				optSymbol = enumerator.Current;
				
				//Debug("Iterated " + k + " times");
				//Debug(optSymbol.Value);
				
				//Debug(optSymbol.Value + " " + optSymbol.ID.StrikePrice + " " + optSymbol.ID.Date + " " + optSymbol.ID.OptionRight);
				k++;
        	}
        	//Debug(" ---------------------------------------------------------------------------------------------");
    
        }
        
     
       	// **********************   Iterate Ordered Matrix		***********************************************
	    // ***  	Generalized function to iterate through and print members of an IEnumerable of Contracts
	    // ***		This is used for debugging only  tricky part is passing an IOrderedEnumerable into this 
	    // ****************************************************************************************************
	    public void IterateOrderedSSQRMatrix(IOrderedEnumerable<SSQRColumn> thisOrdMatrix)
	    {	
        	
        	int k = 1;
    		
        	Debug("  ||||||||||||||||||||||||||||||||   NEW TRADABLE SSQRMatrix  |||||||||||||||||||||||||||||||");
			Debug("There are " + thisOrdMatrix.Count() + " columns in this SSQRMatrix.");
			//       1      2            3        4         5        6       7         8      9        10       11         12      13        14      15           16        17         18         19        20        21         22           23       24      25   26    27       28  29    30          31       32			33
           	Debug(",Ticker,Stock Price,Ex-Date,# Dividends,Dividend,Dollars,Days In,Interest,PExpiry, CExpiry, PutStrike, PutBid, CallStrike, CallAsk, wCStrike, wCallAsk, PutDelta, CallDelta, NetOptions,Net Income,VE Rating, VE Momentum, VE 1 Yr, Haircut,ROC,Upside,Downside,ROR,CCOR, wingFactor, PutSymb, CallSymb, OneYearTarget");

        	foreach (SSQRColumn thisColumn in thisOrdMatrix) 
        	{
				//Debug("Iterated  " + k + " times");
				Debug(thisColumn.description2);
				//Debug("  ");
				k++;
				if (k == 31) break;
        	}

        }
    
       	// **********************   Iterate Ordered PutSpread   **********************************************
	    // ***  	Generalized function to iterate through and print members of an IEnumerable of PutSpreads
	    // ***		This is used for debugging only  tricky part is passing an IOrderedEnumerable into this 
	    // ****************************************************************************************************
	    public void IterateOrderedPutSpreadList(IOrderedEnumerable<PutSpread> thisOrdSpreads)
	    {	
        	string logLine = "";						// for writing the logs
        	int k = 1;
    		
        	Debug("  ||||||||||||||||||||||||||||||||   NEW TRADABLE PutSpreads List  |||||||||||||||||||||||||||||||");
			Debug(",¶¶,There are " + thisOrdSpreads.Count() + " PutSpreads in this List.");
			//       1       2        3             4        5       6           7           8        9          10      11     	 12         13         14             15       16        17         18       19        20		   21        22        23
        	//Debug("¶¶,Stock Price, Ex-Date, Trade Date, pExpiry, oldPutSymb, newPutSymb, oldBid, newAsk, oldStrike, newStrike, Open Interst, Div Amt, # Dividends, Div Dollars, stock Incr,Interest,DownSide, Upside, Net Income, NetOptions, Haircut, Descr

        	logLine = ",¶¶";
        	
        	foreach (PutSpread thisSpread in thisOrdSpreads) 
        	{
				if (k==1){						// iterate field names
					
					foreach (var fieldN in typeof(PutSpread).GetFields())	
					{
						logLine = logLine + "," + fieldN.Name;
					}
					Debug(logLine);
					logLine = ",¶¶";
					//k = k + 1;
				}
				
				foreach (var fieldV in typeof(PutSpread).GetFields())	
				{
					if (fieldV.GetType() == typeof(decimal)) {
						logLine = logLine + "," + String.Format("{0:0.00}", fieldV.GetValue(thisSpread));
					}
					else if (fieldV.GetType() == typeof(DateTime)) {
						logLine = logLine + "," + String.Format("{0:MM/dd/yy H:mm:ss}", fieldV.GetValue(thisSpread));
						
					}
					else logLine = logLine + "," + fieldV.GetValue(thisSpread);

				}
				
				Debug(logLine);
				logLine = ",¶¶";
				//Debug("Iterated  " + k + " times");
				//Debug(thisSpread.description1);
				//Debug("  ");
				k++;
				//if (k == 11) break;
        	}

        }
 
     }
    	
}
#region imports
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using QuantConnect.Util;
    using QuantConnect.Data;
    using QuantConnect.Data.Market;
    using QuantConnect.Orders;
    using QuantConnect.Securities;
    using QuantConnect.Securities.Option;
#endregion
using QuantConnect.Securities.Option;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace QuantConnect.Algorithm.CSharp
{
    public partial class CollarAlgorithm : QCAlgorithm
    {
		private bool goodThresh2 = false;

		////////////////////////////////////////////////////////////////////////////////////
		//								RollCriterium
		////////////////////////////////////////////////////////////////////////////////////
		/// Securities[tpr.uSymbol].Price < 100 & tpr.uStartPrice-Securities[tpr.uSymbol].Price>=5m) | (Securities[tpr.uSymbol].Price>=100 & Securities[tpr.uSymbol].Price<200 & tpr.uStartPrice-Securities[tpr.uSymbol].Price>=10m) | (Securities[tpr.uSymbol].Price>=200 & tpr.uStartPrice-Securities[tpr.uSymbol].Price>=15m)) ))
		public bool RollCriterium(decimal startPrice, decimal currPrice)
		{
			decimal diffy = startPrice - currPrice;
			if (diffy >= 0) return false;
			switch (currPrice)
			{
				case <= 100:
					if (diffy >= 5)	return true; else return false;
					break;
				case > 100 and <=200:
					if (diffy >= 10) return true; else return false;
					break;
				case > 200 and <=300:
					if (diffy >= 20) return true; else return false;
					break;
				case > 300 :
					if (diffy >= 25) return true; else return false;
					break;
				default:
					return false;
					break;
			}

		}

		////////////////////////////////////////////////////////////////////////////////////
		//								ExecuteTrade
		////////////////////////////////////////////////////////////////////////////////////

		public bool ExecuteTrade(Slice data, SSQRColumn bestSSQRColumn, ref SymbolData symbData, LookupData LD)
		{
	        thisCCOR = bestSSQRColumn.CCOR;
	        decimal maxWingFactor = 0;
	        decimal thisWingFactor = 0;
			decimal wingPremium = 0;
			decimal thisNetOptions = bestSSQRColumn.netOptions;

			Log($" TD TD TD TD **** LUD check symbol is {LD.uSymbol.Value} and OneYearPriceTarget={LD.decOneYearPriceTarget.ToString("0.00")}");
			
			if (haltProcessing) 
			{
				//if (doDeepTracing) Log("     Logging ExecuteTheTrade() ");
			}

			//goodThresh = (thisCCOR >= CCORThresh);
			goodThresh = true;

			if (goodThresh)
			{
				sharesToBuy = Math.Truncate(LUD.unitRiskAmt/(bestSSQRColumn.callStrike - bestSSQRColumn.stockPrice - bestSSQRColumn.netOptions));				// use +NetOptions.   netOptions is calculated as an expense in the SSQR potential SSQRColumn.P&L
				sharesToBuy = sharesToBuy - sharesToBuy % 100;
				sharesToBuy = -sharesToBuy;
				optionsToTrade = sharesToBuy/100;
				//callsToTrade = Decimal.Round(optionsToTrade * bestSSQRColumn.putPremium / bestSSQRColumn.callPremium);					/// legacy VCCPTS code

				//Log(tradableColumn.ToString());
				Symbol tradablePut = bestSSQRColumn.putSymbol;
				Symbol tradableCall = bestSSQRColumn.callSymbol;
				Symbol tradableWCall = bestSSQRColumn.wCallSymbol;
				
				if (bestSSQRColumn.putSymbol!=null && bestSSQRColumn.putStrike - Securities[tradablePut].AskPrice > stockPrice) // make sure that no one can buy the option for less than the stock
        		{
        			if (doDeepTracing) Log($"@E@E@E@E@E@E  EXERCISE PREVENTION FADE  FOR  {bestSSQRColumn.uSymbol} @E@E@E@E@E@E");
        			if (doDeepTracing) Log("@E@E@E@E@E@E   Put ASK: " + Securities[tradablePut].AskPrice  + " Strike: " + bestSSQRColumn.putStrike + " Stock Price: " + stockPrice +" @E@E@E@E@E@E"); 
	    			if (doDeepTracing) Log("@E@E@E@E@E@E   @E@E@E@E@E@E  @E@E@E@E@E@E @E@E@E@E@E@E");
    				return false;   
    			}
    		
       			if (doTracing) Log($"@E@E@E@E@E@E  EXECUTING COVERED CALL INITIALIZATION FOR {bestSSQRColumn.uSymbol} @E@E@E@E@E@E with {sharesToBuy.ToString()} shares and {optionsToTrade.ToString()}.");
    			//tradeRecCount = tradeRecCount + 1;				//  increment trade record count

				symbData.intTPRCntr += 1;
    			//collarIndex = collarIndex + 1;
    			doTheTrade = true;
				var stockTicket = MarketOrder(bestSSQRColumn.uSymbol, sharesToBuy);	
				if (stockTicket.Status == OrderStatus.Filled)
        		{
            		didTheTrade = true;
					
            		//if (!string.IsNullOrEmpty(strFilterTkr)) Plot("Stock Chart", "Buys", stockTicket.AverageFillPrice + 5);
            		// make a new TradePerfRec
            		TradePerfRec thisNewCollar = new TradePerfRec();

            		thisNewCollar.strtngCndtn = "INITIAL COLLAR: " + " SBB " + symbData.bbTrigger.ToString() + " VDI " + symbData.VDI_Signal.ToString() + " TSI " + symbData.tsi_trigger.ToString() + " ALMA " + symbData.almas_crossing.ToString();
            		thisNewCollar.isOpen = true;
            		thisNewCollar.isInitializer = true;
            		thisNewCollar.tradeRecCount = collarIndex;
            		thisNewCollar.index = symbData.intTPRCntr;;
					thisNewCollar.startDate = data.Time;
					thisNewCollar.expDate = bestSSQRColumn.callExpiry;
					thisNewCollar.thetaExpiration = bestSSQRColumn.callExpiry;
            		thisNewCollar.uSymbol = bestSSQRColumn.uSymbol;
            		thisNewCollar.cSymbol = tradableCall;
		    		thisNewCollar.pSymbol = tradablePut;		
            		//thisNewCollar.wcSymbol = tradableWCall;
            		thisNewCollar.uStartPrice = stockTicket.AverageFillPrice;
            		thisNewCollar.pStrike = bestSSQRColumn.putStrike;
            		thisNewCollar.cStrike = bestSSQRColumn.callStrike;
            		//thisNewCollar.wcStrike = bestSSQRColumn.wCallStrike;
            		thisNewCollar.uQty = (int)stockTicket.QuantityFilled;
            		thisNewCollar.ROR = bestSSQRColumn.ROR;
            		thisNewCollar.ROC = bestSSQRColumn.ROC;
            		thisNewCollar.CCOR = bestSSQRColumn.CCOR;
            		thisNewCollar.RORThresh = RORThresh;
            		thisNewCollar.ROCThresh = ROCThresh;
            		thisNewCollar.CCORThresh = CCORThresh;
            		//thisNewCollar.tradeCriteria = switchROC ? "ROC" : "ROR";
            		thisNewCollar.tradeCriteria = symbData.VECase;
            		//thisNewCollar.stockADX  = 0;						//lastAdx;
            		//thisNewCollar.stockADXR = 0;						//lastAdxr;
            		//thisNewCollar.stockOBV = 0;						//lastObv;
            		//thisNewCollar.stockAD = lastAd;
            		//thisNewCollar.stockADOSC = lastAdOsc;
            		//thisNewCollar.stockSTO = lastSto;
            		//thisNewCollar.stockVariance = lastVariance;
            		thisNewCollar.SSQRnetProfit = stockTicket.QuantityFilled * bestSSQRColumn.netIncome;
					thisNewCollar.VERating = LD.intVERating;
					thisNewCollar.momentum = LD.decMomentum;
					thisNewCollar.oneYearPriceTarget = LD.decOneYearPriceTarget; 
					thisNewCollar.momentumRank = LD.intMomentumRank;

	
					doTheTrade = true;
					if(bestSSQRColumn.callSymbol!=null){
						if (thisNewCollar.cStrike < thisNewCollar.uStartPrice) {
							
							var limitPrice = Securities[tradableCall].BidPrice + ((Securities[tradableCall].AskPrice - Securities[tradableCall].BidPrice) / 2M);	// get the mid point for the limit price
							var callTicket = LimitOrder(tradableCall, -optionsToTrade, limitPrice);							// sell limit order
							thisNewCollar.cQty = -(int)optionsToTrade;
							OpenLimitOrder oLO = new OpenLimitOrder();
							oLO.oTicket = callTicket;
							oLO.tpr = thisNewCollar;
							oLO.oRight = OptionRight.Call;
							oLOs.Add(oLO);
							//if (closePutTicket.Status == OrderStatus.Submitted) oldTradeRec.pEndPrice = limitPrice;
						} else {
							var callTicket = MarketOrder(tradableCall, -optionsToTrade);
							if (callTicket.Status == OrderStatus.Filled)
							{
								thisNewCollar.cStartPrice = callTicket.AverageFillPrice;
								thisNewCollar.cQty = (int)callTicket.QuantityFilled;
							}
						}
					}

					//if(bestSSQRColumn.wCallSymbol!=null) thisWingFactor = bestSSQRColumn.wingFactor;

	    			//var putTicket = MarketOrder(tradablePut, (1 + thisWingFactor) * optionsToTrade);
					if(bestSSQRColumn.putSymbol!=null) {
						if (thisNewCollar.pStrike > thisNewCollar.uStartPrice) {
							var limitPrice = Securities[tradablePut].BidPrice + ((Securities[tradablePut].AskPrice - Securities[tradablePut].BidPrice) / 2M);	// get the mid point for the limit price
							var putTicket = LimitOrder(tradablePut, -optionsToTrade, limitPrice);							// sell limit order
							thisNewCollar.pQty = -(int)optionsToTrade;
							OpenLimitOrder oLO = new OpenLimitOrder();
							oLO.oTicket = putTicket;
							oLO.tpr = thisNewCollar;
							oLO.oRight = OptionRight.Put;
							oLOs.Add(oLO);
						} else {
							var putTicket = MarketOrder(tradablePut, optionsToTrade);
							if (putTicket.Status == OrderStatus.Filled)
							{
								thisNewCollar.pStartPrice = putTicket.AverageFillPrice;
								thisNewCollar.pQty = (int)putTicket.QuantityFilled;
							}
						}
					}
						
					/*
					if (thisWingFactor > 0) {
						var wCallTicket = MarketOrder(tradableWCall, thisWingFactor * optionsToTrade);
						if (wCallTicket.Status == OrderStatus.Filled) {
							thisNewCollar.wcStartPrice = wCallTicket.AverageFillPrice;
							thisNewCollar.wcQty = (int)wCallTicket.QuantityFilled;
						}
					}
					*/
					doTheTrade = true;
					
	    			tradeRecs.Add(thisNewCollar);
	    			if (doTracing) Log("@E@E@E@E@E@E - ADDING A VE 3-4-5  TPR ");
					string sSBB = symbData.bbTrigger.ToString();
					string sVDI = symbData.VDI_Signal.ToString();
					string sTSI = symbData.tsi_trigger.ToString();
					string sALMA = symbData.almas_crossing.ToString();
					string reason = " SBB " + sSBB + " VDI " + sVDI + " TSI " + sTSI + " ALMA " + sALMA;
					Log($" -- --- --- INITIALIZATION TRIGGERS {reason}");


        		}		// marketOrder(bestSSQRColumn.uSymbol) == filled
			}			// goodThresh is TRUE
			return true;
		}

		///////////////////////////////////////////////////////////////////////////////////
		//								Close2ndTPR
		////////////////////////////////////////////////////////////////////////////////////
		public void Close2ndTPR (TradePerfRec closeRec, DateTime closeDate, string reason)
		{
			decimal limitPrice = 0;
		
			if (haltProcessing) 
			{
				//Log("           Logging Close2ndTPR ");
			}
			
			doTheTrade = true;
			var stockTicket = MarketOrder(closeRec.uSymbol, -closeRec.uQty);	// sell the stock
			//if (doDeepTracing) Debug(" C2 **  MARKET ORDER TO SELL " + closeRec.uQty.ToString() + " shares of " + closeRec.uSymbol + " at the market.");

			//if (doDeepTracing) Log(" C2 ** C2 ** STARTING CLOSE2ndTPR PROCESSING ** C2 ** C2 ");
			//if (doDeepTracing) Log(" -- ");
			if (doDeepTracing)	{
				foreach(var kvp in Securities)		/// make sure there's no leaking of abandoned stocks or options
				{
					var security = kvp.Value;
					if (security.Invested)
					{
						//saveString = "," + security.Symbol + ", " + security.Holdings.Quantity + Environment.NewLine;
						//Log($" ||||  HOLDINGS: {security.Symbol} : {security.Holdings.Quantity} @ {security.BidPrice} by {security.AskPrice}");
					}
				}
			}

			if (stockTicket.Status == OrderStatus.Filled)
			{
				//closeRec.isOpen = false;
				tprsToClose.Add(closeRec);
				closeRec.uEndPrice = stockTicket.AverageFillPrice;
				//if (symbFilter != null) Plot("Stock Chart", "Sells", stockTicket.AverageFillPrice + 1);
				//if (symbFilter != null) Plot("Stock Chart", "PTSs", divPlotValue);
				tradeRecCount = 0;				//  reset trade record count

			}
			
			doTheTrade = true;
			closePutTicket = MarketOrder(closeRec.pSymbol, -closeRec.pQty); // sell the puts	
			
			if (closePutTicket.Status == OrderStatus.Filled)
			{
				closeRec.pEndPrice = closePutTicket.AverageFillPrice;
			}
			closeRec.reasonForClose = reason;
			closeRec.endDate = closeDate;			// set the end date of this collar
			//if (doDeepTracing) Log(" C2 ** C2 ** C2 ** C2 ** CLOSED 2nd TPR  ** C2 ** C2 ** C2 ** C2 ** C2 ** ");
			//if (doDeepTracing) Log("-");
		}
	
		///////////////////////////////////////////////////////////////////////////////////
		//								KillTheCollar
		////////////////////////////////////////////////////////////////////////////////////

		public bool KillTheCollar(TradePerfRec killRec, ref LookupData LUD, string reason, bool force, bool isStock)
		{
			bool bKTC = false;									// controls Main.cs foreach TPR routine -- exit the for loop if .isOpen is changed.
			decimal limitPrice = 0;
			//decimal currUPrice = Securities[killRec.uSymbol].Price;
			
			if (LUD.haltProcessing) {
				// Log(" logging kill rec on bad date");
				
			}
			try {
				if (Securities[killRec.uSymbol].HasData) 
				{ 
					var tryPrice = Securities[killRec.uSymbol].Price;   
					if (tryPrice == null) {
						Debug($" KK ** KK ::: UNABLE TO FIND A STOCK PRICE IN PACKAGE KILL PROCESS FOR {killRec.uSymbol.Value}. ");
						return false;
					}
					decimal currUPrice = Convert.ToDecimal(tryPrice);
					killRec.uEndPrice = currUPrice;								//2023-04-11 :: INSERTED THIS HERE TO FORCE uEndPrice
				} else { 
					Debug($" KK ** KK ::: UNABLE TO FIND A STOCK DATA IN PACKAGE KILL PROCESS FOR {killRec.uSymbol.Value}. ");
					return false;
				}
			} catch (Exception excpt) {
				if (LUD.doTracing) Debug($" KK ** KK **  {excpt} for  {killRec.uSymbol} at {CurrentSlice.Time.ToShortTimeString()} on {CurrentSlice.Time.ToShortDateString()}");
				return bKTC;
			}

			decimal currPPrice = killRec.pSymbol != null ? Securities[killRec.pSymbol].BidPrice : 0;
			decimal currCPrice = killRec.cSymbol != null ? Securities[killRec.cSymbol].AskPrice : 0;
			decimal currWCPrice = killRec.wcSymbol != null ? Securities[killRec.wcSymbol].BidPrice : 0;
			decimal stockPrice = Securities[killRec.uSymbol].Price;

			if (doDeepTracing) Log($" KK ** STARTING KILLTHECOLLAR PROCESSING  FOR  {killRec.uSymbol} ");
			if (doDeepTracing) Log(" -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
			if (doDeepTracing & force) Log($" KK ** KK ** FORCING PACKAGE KILL ");
			if (force) goto noExercise;
/*			if (doDeepTracing)	{
				foreach(var kvp in Securities)		/// make sure there's no leaking of abandoned stocks or options
				{
					var security = kvp.Value;
					if (security.Invested)
					{
						//saveString = "," + security.Symbol + ", " + security.Holdings.Quantity + Environment.NewLine;
						// Log($" ||||  HOLDINGS: {security.Symbol} : {security.Holdings.Quantity} @ {security.BidPrice} by {security.AskPrice}");
					}
				}
				// Log($" ||||  SELL OPTS  P&L: " + String.Format("{0:0.00}", currSellPnL));
				// Log($" ||||  Exrcs PUT  P&L: " + String.Format("{0:0.00}", currExrcsPutPnL));
				// Log($" ||||  Exrcs CALL P&L: " + String.Format("{0:0.00}", currExrcsCallPnL));
				
			}
*/

			doTheTrade = true;
			
			//   determine if this is an ITM call or ITM put and within 1 day of expiry
			
			if (killRec.cSymbol != null && stockPrice >= callStrike && LUD.daysRemainingP <= 1) {				///   ITM CALL -- Exercise or sell
				//  determine if it's more expensive to sell or exercise ***** remember, killRec.cQty is negative for collars (sold calls)
	
				if (killRec.currExrcsCallPnL > killRec.currSellPnL) {					// for an ITM PUT, both costs should be negative 
					if (doDeepTracing) Log($" KK ** KK ** KK ** EXERCISING PUTS IN KILLTHECOLLAR FOR {thisSymbol}");
					if (killRec.cSymbol != null) {			// Exercise the CALLs.   Let longer expiry calls ride to attempt theta decay -- create a "theta TPR" to track and manage call
						//var shrtCall = (Option)Securities[killRec.cSymbol];
						//TimeSpan daysToCallExpiry = shrtCall.Expiry.Subtract(killDate);
    					/*if (daysToCallExpiry.Days > 10 ) {
							Log(" OO CALL " + shrtCall + " EXPIRES IN " + daysToCallExpiry.Days + "DAYS. CREATING THETA TPR.");

							//  create a thetaTPR to move the call data and track it.   Buy it back when theta decays.	
							TradePerfRec newThTPR = new TradePerfRec();
							newThTPR.uSymbol = killRec.uSymbol;
							newThTPR.index = killRec.index;
							newThTPR.isOpen = true;
							newThTPR.isInitializer = true;
							newThTPR.isSecondary =false;
							newThTPR.isTheta = true;
							newThTPR.startDate = killRec.startDate;
							newThTPR.strtngCndtn = "SPINNING OFF THETA CALLS";
							newThTPR.expDate = shrtCall.Expiry;
							newThTPR.cSymbol = killRec.cSymbol;
							newThTPR.cStrike = killRec.cStrike;
							newThTPR.cQty = killRec.cQty;
							newThTPR.cStartPrice = killRec.cStartPrice;
							newThTPR.tradeCriteria = killRec.tradeCriteria;
							
							tradeRecs.Add(newThTPR);
										
							killRec.cSymbol = null;			// eliminate the call from the existint TPR
							killRec.cStartPrice = 0;
							killRec.cQty = 0;
    					} else { */
			
						if (doDeepTracing) Debug(" KK ** KK ** BUYING BACK SHORT PUTS IN KILLTHECOLLAR CALL EXERCISE ** KK ** KK ");
						
						if (killRec.pQty != 0){
							closePutTicket = MarketOrder(killRec.pSymbol, -killRec.pQty);  // buy the puts
							if (doDeepTracing) Log(" KK ** KK ** KK ** MARKET ORDER TO BUY " + killRec.pQty.ToString() + " contracts of " + killRec.pSymbol + " at the market.");
							if (doDeepTracing) Log("-");

							if (closePutTicket.Status == OrderStatus.Filled)
							{
								killRec.pEndPrice = closePutTicket.AverageFillPrice;
							}
						}		/////   killRec.cSymbol != null
					}
					//if (doDeepTracing) Log(" ------- ");
					//if (doDeepTracing) Log(" KK ** KK ** EXERCISING PUTS IN KILLTHECOLLAR ** KK ** KK ");
						
					closeCallTicket = ExerciseOption(killRec.cSymbol, killRec.cQty);			/// underlying will be closed in onOrder() event
					killRec.grossPnL = killRec.currExrcsCallPnL;								/// log the PnL used in runtime decision

					bestSSQRColumn = new SSQRColumn();
					bKTC = true;
					return bKTC;
				} else {						//// ITM PUT but more profitable to sell the collar
					if (doDeepTracing) Log("  KK ** KK ** ITM CALL MORE PROFITABLE TO SELL COLLAR THAN EXERCISE ** KK ** KK");
					goto noExercise;
				}


			}  else {					/// ITM CALL ON LAST DAY -->>  GET HERE IF CALL IS OTM OR THERE IS NO CALL
				if (doDeepTracing) Log(" KK ** KK ** OTM CALL -- CHECKING CALL MONEY  ** KK ** KK ");
			}
			
			if (killRec.pSymbol != null && stockPrice <= putStrike && LUD.daysRemainingC <= 1) {
				if (doDeepTracing) Log(" KK ** KK ** CHECKING PUT STRATEGY P&L ** KK ** TT ");
				killRec.grossPnL = killRec.currExrcsPutPnL;							// log the PnL used in runtime decision

				if (killRec.currExrcsPutPnL > killRec.currSellPnL) {					// for an ITM CALL, both costs should be positive
					//if (doDeepTracing) Log(" KK ** KK ** EXIT KILLTHECOLLAR AND AWAIT CALL EXERCISE** KK ** TT ");
					
					if (doDeepTracing) Log(" KK ** KK ** KK ** ITM PUT AWAITING LEAN EXERCISE -- CLOSING POSITIONS IN OnOrder() Processing ");
					bKTC = true;
					return bKTC;
				} else {
					if (doDeepTracing) Log(" KK ** KK ** KK ** ITM PUT MORE PROFITABLE TO SELL COLLAR THAN EXERCISE");
					goto noExercise;
				}
			}	else {	// ITM CAll and 3rd Friday
				if (doDeepTracing) Log(" KK ** KK ** KK ** OTM CALL -- POSITIONS IN KILLTHECOLLAR ");
			}

			//if OTM or it's less costly to execute orders, then do so here.
			
			if (doDeepTracing) Log(" KK ** KK ** KK ** KK ** OTM PUT AND CALL --  LIQUIDATE HERE IN KILLTHECOLLAR");

noExercise:

			var stockTicket = MarketOrder(killRec.uSymbol, -killRec.uQty);																					// sell the stock
			if (doDeepTracing) Log(" KK ** KK ** KK ** MARKET ORDER TO SELL " + killRec.uQty.ToString() + " shares of " + killRec.uSymbol + " at the market.");		// Log the sale
			bKTC = true;			
			if (stockTicket.Status == OrderStatus.Filled)
			{
				if (doDeepTracing) Log(" KK ** KK ** KK ** KK ** UPDATING TPR.U END PRICE AND SDBS.ISROLLABLE ** KK ** KK");																		// Log the UPDATING
				
				/// add the killTPR to TPRS to close;
				tprsToClose.Add(killRec);
	
				killRec.uEndPrice = stockTicket.AverageFillPrice;
				string sSBB = symbolDataBySymbol[killRec.uSymbol].bbTrigger.ToString();
				string sVDI = symbolDataBySymbol[killRec.uSymbol].VDI_Signal.ToString();
				string sTSI = symbolDataBySymbol[killRec.uSymbol].tsi_trigger.ToString();
				string sALMA = symbolDataBySymbol[killRec.uSymbol].almas_crossing.ToString();
				reason = reason + " SBB " + sSBB + " VDI " + sVDI + " TSI " + sTSI + " ALMA " + sALMA;

				Log($" -- -- REASON FOR KILL: {reason}");
				killRec.reasonForClose = reason;
				killRec.endDate = CurrentSlice.Time;										// set the end date of this collar
				killRec.grossPnL = currSellPnL;												// for logging and analysis of runtime conditions
				if (isStock) {
					symbolDataBySymbol[killRec.uSymbol].intTPRCntr = 0;						//// reset the SYMBOL DATA COUNTER  -- should be redundant
					symbolDataBySymbol[killRec.uSymbol].isRollable = false;					//// 2023-02-08   Found that some orders are so delayed that the Symbol is not removed from SDBS
					//SymbolsToRemove.Add(killRec.uSymbol);									//// 2024-01-01   Testing hypothesis to keep the SD in case it re-qualifies
				 } else
				{
					etfDataBySymbol[killRec.uSymbol].intTPRCntr = 0;						//// reset the SYMBOL DATA COUNTER  -- should be redundant
					//etfDataBySymbol[killRec.uSymbol].isRollable = false;					//// ETFs are always available to be traded based upon relative momentum
					//SymbolsToRemove.Add(killRec.uSymbol);									//// ETFs are fixed and always prospects
					
				}
				//if (symbFilter != null) Plot("Stock Chart", "Sells", stockTicket.AverageFillPrice + 1);
				tradeRecCount = 0;				//  reset trade record count

			}
			
			doTheTrade = true;
			
			if (doDeepTracing) Log(" KK ** KK ** KK ** KK ** SOLD UNDERLYING -- WORKING OPTION  ** KK ** KK ** KK ** KK ** KK ** ");


			if (killRec.pSymbol != null) {			// Buy back any PUTS if possible
				var shrtPut = (Option)Securities[killRec.pSymbol];
				if (doDeepTracing) Log(" KK ** Stock Price: " + stockPrice.ToString() + " Call Bid/Offer: " + Securities[killRec.pSymbol].BidPrice.ToString() + "/" + Securities[killRec.pSymbol].AskPrice.ToString());

				// -- if dealing with theta TPR use LUD.daysRemainingC  not ***  //TimeSpan daysToCallExpiry = shrtCall.Expiry.Subtract LUD.dtTst);
 
    			/*if (daysToCallExpiry.Days > 10 ) {
					Log(" OO CALL " + shrtCall + " EXPIRES IN " + daysToCallExpiry.Days + ". CREATING THETA TPR.");

					//  create a thetaTPR to move the call data and track it.   Buy it back when theta decays.	
					TradePerfRec newThTPR = new TradePerfRec();
					newThTPR.uSymbol = killRec.uSymbol;
					newThTPR.index = killRec.index;
					newThTPR.isOpen = true;
					newThTPR.isInitializer = true;
					newThTPR.isSecondary = true;
					newThTPR.isTheta = true;
					newThTPR.startDate = killRec.startDate;
					newThTPR.strtngCndtn = "SPINNING OFF THETA CALLS";
					newThTPR.expDate = shrtCall.Expiry;
					newThTPR.cSymbol = killRec.cSymbol;
					newThTPR.cQty = killRec.cQty;
					newThTPR.cStartPrice = killRec.cStartPrice;
					newThTPR.tradeCriteria = killRec.tradeCriteria;
					
					tradeRecs.Add(newThTPR);
								
					killRec.cSymbol = null;			// eliminate the call from the existint TPR
					killRec.cStartPrice = 0;
					killRec.cQty = 0;
				} else */
				
				if  (killRec.pStrike >= stockPrice) {											/// ITM Put -- use limit order
		
					limitPrice = stockPrice - killRec.pStrike + 0.10M;
					killRec.pEndPrice = killRec.pStrike - stockPrice;							/// /// 2023-04-23 :::  FORCED pEndPrice to Moneyness
					
					closePutTicket = LimitOrder(killRec.pSymbol, -killRec.pQty, limitPrice);
					
					OpenLimitOrder oLO = new OpenLimitOrder();
					oLO.oTicket = closePutTicket;
					oLO.tpr = killRec;
					oLO.oRight = OptionRight.Put;
					oLOs.Add(oLO);
					if (doDeepTracing) Log(" KK ** LIMIT ORDER TO BUY TO CLOSE SHORT PUT " + killRec.pQty.ToString() + " contracts of " + killRec.pSymbol + " at " + limitPrice.ToString());
				} else { 
					closePutTicket = MarketOrder(killRec.pSymbol, -killRec.pQty);  // buy the puts
					if (doDeepTracing) Log(" KK **  KK ** MARKET ORDER TO BUY TO CLOSE SHORT CALL" + killRec.pQty.ToString() + " contracts of " + killRec.pSymbol + " at the market.");
					if (closePutTicket.Status == OrderStatus.Filled)
					{
						killRec.pEndPrice = closePutTicket.AverageFillPrice;
					}

				}
			}
			
			//if (doDeepTracing) Log("---------------------------------------");

			if(killRec.cSymbol != null) {
			
				if  (killRec.cStrike <= stockPrice)											/// ITM CALL -- use limit order
				{
					limitPrice = stockPrice - killRec.cStrike + 0.10M;
					closeCallTicket = LimitOrder(killRec.cSymbol, -killRec.cQty, limitPrice); // sell the puts	
					
					OpenLimitOrder oLO = new OpenLimitOrder();
					oLO.oTicket = closeCallTicket;
					oLO.tpr = killRec;
					oLO.oRight = OptionRight.Call;
					oLOs.Add(oLO);
					
					if (doDeepTracing) Log($" KK ** LIMIT ORDER TO SELL TO CLOSE {killRec.cQty.ToString()} contracts of {killRec.cSymbol.Value} at {limitPrice.ToString()}");
					//if (doDeepTracing) Log("-");
				
				} else { 
					closeCallTicket = MarketOrder(killRec.cSymbol, -killRec.cQty); // sell the puts	
					if (doDeepTracing) Log($" KK ** KK ** MARKET ORDER TO SELL TO CLOSE {killRec.cQty.ToString()} contracts of {killRec.cSymbol.Value} at the market." );
					if (doDeepTracing) Log("-");
				
					if (closeCallTicket.Status == OrderStatus.Filled)
					{
						killRec.cEndPrice = closeCallTicket.AverageFillPrice;
						if (doDeepTracing) Log(" KK ** KK ** KK ** UPDATING CALL PRICE TO " + killRec.cEndPrice + " ** KK ** KK");
					}

					return bKTC;
				}
			}					// /// /// ///   killRec.pSymbol != null			
			/*
			if (killRec.wcSymbol != null && killRec.wcQty != 0 && killRec.wcEndPrice == 0)		 {
				if  (killRec.wcStrike < stockPrice)											/// ITM Put -- use limit order
				{
					limitPrice = stockPrice - killRec.wcStrike + 0.10M;
					//if (doDeepTracing) Log(" KK **  LIMIT ORDER TO SELL TO CLOSE WING " + killRec.wcQty.ToString() + " contracts of " + killRec.wcSymbol + " at " + limitPrice.ToString());
					closeWCallTicket = LimitOrder(killRec.wcSymbol, -killRec.wcQty, limitPrice); // sell the wing calls	
					
					OpenLimitOrder oLO = new OpenLimitOrder();
					oLO.oTicket = closeWCallTicket;
					oLO.tpr = killRec;
					oLO.oRight = OptionRight.Call;
					oLO.isWingCall = true;
					oLOs.Add(oLO);
					
					//if (doDeepTracing) Log("-");
				
				} else { 
					closeWCallTicket = MarketOrder(killRec.wcSymbol, -killRec.wcQty); // sell the puts	
					//if (doDeepTracing) Log(" KK **  LIMIT ORDER TO SELL TO CLOSE WING " + killRec.wcQty.ToString() + " contracts of " + killRec.wcSymbol + " at " + limitPrice.ToString());
					//if (doDeepTracing) Log("-");
				//}
				
				if (closeWCallTicket.Status == OrderStatus.Filled)
				{
					killRec.wcEndPrice = closePutTicket.AverageFillPrice;
					//if (doDeepTracing) Log(" KK ** UPDATING WING END PRICE TO " + killRec.wcEndPrice + " ** KK ** KK");
				}
				
			}
			*/
			return bKTC;	
			//if (doDeepTracing) Log("-");
		}
		

		///////////////////////////////////////////////////////////////////////////////////
		//								RollCallDown
		////////////////////////////////////////////////////////////////////////////////////
		
		public bool RollCallDown(SSQRColumn bestSSQRColumn, ref LookupData LUD, TradePerfRec oldTPR, decimal sPrice, bool isCollar){
			int rollQty = oldTPR.cQty;					// change in qty, difference between total stock and covered stock = uncovered stock == amount to roll up.
			int findYear = CurrentSlice.Time.Year;
			int findMonth = CurrentSlice.Time.Month;
			bool prosecuteCalls;

			OrderTicket closePutTicket;					// used to close the open puts
			OrderTicket rollPutTicket;					// used to open (roll up) new puts
			OrderTicket closeCallTicket;
			OrderTicket rollCallTicket;

			prosecuteCalls = !oldTPR.cSymbol.Equals(bestSSQRColumn.callSymbol);

			if (LUD.doTracing) Log(" RC ** RC ** RC **	Logging ROLLCALL RC ** RC ** RC **");
			
			// Compute the 3rd Friday of this month [options expiration] ---> do not adjust for potential holiday here
			DateTime thisMonthExpiry = FindDay(findYear, findMonth, DayOfWeek.Friday, 3);

			/*
				if (oldTPR.isSecondary) {									// close secondary tickets only   
					if (oldTPR.pStrike > sPrice & forceAction) {
						oldTPR.reasonForClose = "FAILED TO OBTAIN PUT ROLL SPREAD";
						var putExerciseTicket = ExerciseOption(oldTPR.pSymbol,  oldTPR.pQty);
					} else if (oldTPR.pStrike < sPrice & forceAction) {
						Close2ndTPR(oldTPR, slcData.Time, " CLOSING 2nd TPR at Expiration with stock @: " + String.Format("{0:C2}", sPrice));	
					}

					//if (doDeepTracing) Log(" **************  END 2nd TPR ITM PUT CALC ****************");
					//if (doDeepTracing) Log("-");
				}
				if (symbFilter != null) Plot("Stock Chart", "PTSs", divPlotValue);
				return bKTC;									// loop around and try again
			}
			*/
			symbolDataBySymbol[oldTPR.uSymbol].intTPRCntr =+ 1;
			//if (doDeepTracing) Log(" RP **  MARKET ORDER TO SELL " + rollQty + " contracts of " + oldTPR.pSymbol + " at market");
			if (prosecuteCalls) {
				closeCallTicket = MarketOrder(oldTPR.cSymbol, -rollQty); 			// sell the calls
				if (closeCallTicket.Status == OrderStatus.Filled)
				{
					oldTPR.cEndPrice = closeCallTicket.AverageFillPrice;
				}
			} else {
				oldTPR.cEndPrice = Securities[oldTPR.cSymbol].Price;
			}

			// first adjust the old tradePerfRec to decrement pQty and uQty.  It remains open to be processed for the remaining covered, collared stock.
				
			TradePerfRec newTPR1 = new TradePerfRec();							// create a tradePerfRec #1 for the puts sold, solely to log their P/L (including underlying unrealized P/L).
			// TradePerfRec newTPR2 = new TradePerfRec();							// create a TradePerfRec #2 for the new Synthetic Call (stock-covered puts)

			//if (doDeepTracing) Log(" RP **  MARKET ORDER TO BUY " + rollQty + " contracts of " + bestPutSpread.newPutSymb + " at market");
			if (prosecuteCalls) {
				rollCallTicket = MarketOrder(bestSSQRColumn.callSymbol, rollQty);	// buy the lower calls
				if (rollCallTicket.Status == OrderStatus.Filled)
				{
					newTPR1.cStartPrice = rollCallTicket.AverageFillPrice;
				} else {
					newTPR1.cStartPrice = Securities[oldTPR.cSymbol].Price;			/// place holder 
				}
			}
			// TradePerfRec newTPR2 = new TradePerfRec();							// create a TradePerfRec #2 for the new Synthetic Call (stock-covered puts)
			
			////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			// // // NOTE: THIS CODE MAY CLONE THE OLDTPR... DOES IT COPY SYMBOLS PROPERLY?
			////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/*	
				foreach (var field in typeof(TradePerfRec).GetFields())				// copy oldTPR to newTPR1
				{
					field.SetValue(newTPR1, field.GetValue(oldTPR));
				}
			*/
			//TradePerfRec rolledPutTPR = this.MemberwiseClone();

			//symbolDataBySymbol[oldTPR.uSymbol].intTPRCntr += 1;

			oldTPR.uEndPrice = sPrice;
			oldTPR.endDate = CurrentSlice.Time;


			newTPR1.uSymbol = oldTPR.uSymbol;										// newTPR1 for the uncovered synthetic call (put + stock) portion of the original collar
			newTPR1.index = symbolDataBySymbol[oldTPR.uSymbol].intTPRCntr;			// maintain collarIndex throughout the entire sequence of collars and synthCalls
			newTPR1.uQty = oldTPR.uQty;												// log the starting and ending values and close the TradePerfRec
			newTPR1.uStartPrice = oldTPR.uStartPrice;
			newTPR1.uEndPrice = 0;

			newTPR1.cSymbol = bestSSQRColumn.callSymbol;
			newTPR1.cStrike = bestSSQRColumn.callStrike;
			newTPR1.pSymbol = bestSSQRColumn.putSymbol;
			newTPR1.pStrike = bestSSQRColumn.putStrike;
			newTPR1.cDelta = bestSSQRColumn.callDelta;
			newTPR1.pDelta = bestSSQRColumn.putDelta;
			newTPR1.cGamma = bestSSQRColumn.callGamma;
			newTPR1.pGamma = bestSSQRColumn.putGamma;
			
			newTPR1.expDate = bestSSQRColumn.callExpiry;
			newTPR1.cQty = rollQty;
			newTPR1.startDate = CurrentSlice.Time;
			newTPR1.isInitializer = false;
			newTPR1.isSecondary = false;
			newTPR1.numDividends = 0;
			newTPR1.divIncome = 0;
			newTPR1.tradeRecCount = oldTPR.tradeRecCount + 1;
			newTPR1.ROR = oldTPR.ROR;
			newTPR1.ROC = oldTPR.ROC;
			newTPR1.CCOR = oldTPR.CCOR;
			newTPR1.tradeCriteria = oldTPR.tradeCriteria;
			newTPR1.strtngCndtn = "CALL ROLL DOWN" + (bestSSQRColumn.putSymbol!=null ? " WITH PERK PUT" : "");

			if (prosecuteCalls) {
				oldTPR.reasonForClose = $"C ROLL DOWN STOCK DEPRECIATION:  {oldTPR.uSymbol.Value}  : {String.Format("{0:0.00}",sPrice-oldTPR.uStartPrice)} COSTING {(newTPR1.cStartPrice - oldTPR.cEndPrice).ToString()}";
			} else{
				oldTPR.reasonForClose = $"C ROLL DOWN STOCK DEPRECIATION ADD PUTS ONLY:  {oldTPR.uSymbol.Value}  : {String.Format("{0:0.00}",sPrice-oldTPR.uStartPrice)} PUTS {(newTPR1.pStartPrice).ToString()}";
			}

			if (doDeepTracing) Log($" R ** R ** CREATING NEW TPR IN ROLL CALL DOWN PROCESSING ** R ** R ");
			if (doDeepTracing) Log($" RC ** RC **        	{oldTPR.uSymbol.Value}            ** RC ** R ");     
			if (doDeepTracing) Log($" RC ** Depreciation: 	{String.Format("{0:0.00}",sPrice-oldTPR.uStartPrice)} COSTING {(newTPR1.cStartPrice - oldTPR.cEndPrice).ToString()} ** RP ** RP ");


			newTPR1.VERating = LUD.intVERating;
			newTPR1.momentum = LUD.decMomentum;
			newTPR1.oneYearPriceTarget = LUD.decOneYearPriceTarget;
			newTPR1.momentumRank = LUD.intMomentumRank;
			newTPR1.uStartPrice = sPrice;									// set the newTPR.uPrice to 0-delta current sPrice

			if(isCollar & oldTPR.pSymbol!=null){							// buy back old calls   /// NOTE:  NEED TO VERIFY THIS SITUATION IS HANDLED PROPERLY
				
				if (oldTPR.pStrike > sPrice) {
					
					var limitPrice = Securities[oldTPR.pSymbol].BidPrice + ((Securities[oldTPR.pSymbol].AskPrice - Securities[oldTPR.pSymbol].BidPrice) / 2M);	// get the mid point for the limit price
					closePutTicket = LimitOrder(oldTPR.pSymbol, -oldTPR.pQty, limitPrice);							// sell limit order
					oldTPR.pEndPrice = oldTPR.pStrike - sPrice;
					OpenLimitOrder oLO = new OpenLimitOrder();
					oLO.oTicket = closePutTicket;
					oLO.tpr = oldTPR;
					oLO.oRight = OptionRight.Put;
					oLOs.Add(oLO);
					oldTPR.pEndPrice = limitPrice;					/// place holder for endprice
				} else {
					closePutTicket = MarketOrder(oldTPR.pSymbol, -oldTPR.pQty);
					if (closePutTicket.Status == OrderStatus.Filled)
					{
						oldTPR.pEndPrice = closePutTicket.AverageFillPrice;
					}
				}

				if (bestSSQRColumn.putSymbol!=null && doDeepTracing) Log($" RP ** RP **   SELL {oldTPR.pQty.ToString()} NEW CALLS  {bestSSQRColumn.putSymbol.Value}            ** RP ** RP ");     
			}

			if(bestSSQRColumn.putSymbol!=null){												// sell calls to generate income  /// NOTE:  NEED TO VERIFY THIS SITUATION IS HANDLED PROPERLY
				newTPR1.pStrike = bestSSQRColumn.putStrike;
				newTPR1.thetaExpiration = bestSSQRColumn.putExpiry;
				newTPR1.pSymbol = bestSSQRColumn.putSymbol;
				newTPR1.pQty = -rollQty;

				if (bestSSQRColumn.putStrike > newTPR1.uStartPrice) {
					
					var limitPrice = Securities[bestSSQRColumn.putSymbol].BidPrice + ((Securities[bestSSQRColumn.putSymbol].AskPrice - Securities[bestSSQRColumn.putSymbol].BidPrice) / 2M);	// get the mid point for the limit price
					rollPutTicket = LimitOrder(bestSSQRColumn.putSymbol, -rollQty, limitPrice);							// sell limit order
					OpenLimitOrder oLO = new OpenLimitOrder();
					oLO.oTicket = rollPutTicket;
					oLO.tpr = newTPR1;
					oLO.oRight = OptionRight.Put;
					oLOs.Add(oLO);
					newTPR1.pStartPrice = limitPrice;					/// place holder for pStartPrice.  Should be set in oloHandling
					newTPR1.pQty = -rollQty;
				} else {
					rollPutTicket = MarketOrder(bestSSQRColumn.putSymbol, -rollQty);
					if (rollPutTicket.Status == OrderStatus.Filled)
					{
						newTPR1.pStartPrice = rollPutTicket.AverageFillPrice;
						newTPR1.pQty = (int)rollPutTicket.QuantityFilled;
					}
				}

				if (doDeepTracing) Log($" RP ** RP **             ROLLING COLLAR CALLS                     ** RP ** RP ");
				if (doDeepTracing) Log($" RP ** RP ** BUYING: {(oldTPR.pSymbol!=null ? oldTPR.pSymbol.Value : "-- no put --")} | SELLING: {(bestSSQRColumn.putSymbol != null ? bestSSQRColumn.putSymbol.Value : "-- no put--") } ** RP ** RP ");     
				if (doDeepTracing) Log($" RP ** Call Appreciation: {(oldTPR.pSymbol != null ? String.Format("{0:0.00}",Securities[oldTPR.pSymbol].AskPrice - oldTPR.pStartPrice) : "-- NA --")} COSTING {(oldTPR.pSymbol != null ? (Securities[newTPR1.pSymbol].BidPrice - Securities[oldTPR.pSymbol].AskPrice).ToString() : newTPR1.pStartPrice)} ** RP ** RP ");
			}

			if (doDeepTracing) Log(" -- ");
			
			
			if (doTracing) Log(" RP ** RP ** END CALL DOWN  ** RP ** RP ** ");

			tprsToClose.Add(oldTPR);
			tprsToOpen.Add(newTPR1);
			return true;


		}			


		///////////////////////////////////////////////////////////////////////////////////
		//								RollTheCollar
		////////////////////////////////////////////////////////////////////////////////////
		public bool RollTheCollar(LookupData LUD, TradePerfRec oldTradeRec, ref SSQRColumn bestSSQRColumn, string reason)
		{

	        Slice data = CurrentSlice;
	        decimal stockPrice = Securities[LUD.uSymbol].Price;
	        
	        thisCCOR = bestSSQRColumn.CCOR;
			decimal thisNetOptions = bestSSQRColumn.netOptions;

			decimal limitPrice = 0;
			OrderTicket closeCallTicket;
			OrderTicket closePutTicket;
			OrderTicket callTicket;
			
			if (haltProcessing) 
			{
				//if (doDeepTracing) Log("                 Logging ROLL ");
			}
			
			//if (symbFilter != null) Plot("Stock Chart", "Rolls", stockPrice + 5);
			
        	Symbol oldShortCallSymb = oldTradeRec.cSymbol;
        	Symbol oldLongPutSymb = oldTradeRec.pSymbol;
        	Symbol oldWCCallSymb = oldTradeRec.wcSymbol;
			
			// Cannot execute options spread orders at this time in QuantConnect, so do the collar as 
			// individual legs
			// 1st sell the long put
			if (doDeepTracing) Debug(" ROLLING ** STARTING ** ROLLING ** STARTING ** ROLLING  ** STARTING ** ROLLING ** STARTING ** ROLLING ** STARTING ** ");
			doTheTrade = true;
			
			//if (doDeepTracing) Log(" -- ");
			if (doDeepTracing)	{
				foreach(var kvp in Securities)		/// make sure there's no leaking of abandoned stocks or options
				{
					var security = kvp.Value;
					if (security.Invested)
					{
						//saveString = "," + security.Symbol + ", " + security.Holdings.Quantity + Environment.NewLine;
						//Log($" ||||  HOLDINGS: {security.Symbol} : {security.Holdings.Quantity} @ {security.BidPrice} by {security.AskPrice}");
					}
				
				}
				// Log($" ||||  SELL OPTS  P&L: " + String.Format("{0:0.00}", currSellPnL));
				// Log($" ||||  Exrcs PUT  P&L: " + String.Format("{0:0.00}", currExrcsPutPnL));
				// Log($" ||||  Exrcs CALL P&L: " + String.Format("{0:0.00}", currExrcsCallPnL));
			}

			
			if  (oldTradeRec.cStrike <= stockPrice)											/// ITM Call -- use limit order to close
			{
				limitPrice = stockPrice - oldTradeRec.cStrike + 0.10M;
				if (doDeepTracing) Log(" @R @R @R LIMIT ORDER TO SELL " + oldTradeRec.cQty.ToString() + " contracts of " + oldTradeRec.cSymbol + " at " + limitPrice.ToString());
				 closeCallTicket = LimitOrder(oldTradeRec.cSymbol, -oldTradeRec.cQty, limitPrice); // sell the callse
				// closeCallTicket = MarketOrder(oldTradeRec.cSymbol, -oldTradeRec.cQty); // sell the calls
				oldTradeRec.cEndPrice = stockPrice - oldTradeRec.cStrike;
				OpenLimitOrder oLO = new OpenLimitOrder();
				oLO.oTicket = closeCallTicket;
				oLO.tpr = oldTradeRec;
				oLO.oRight = OptionRight.Call;
				oLOs.Add(oLO);
				//if (closePutTicket.Status == OrderStatus.Submitted) oldTradeRec.pEndPrice = limitPrice;
			
			} else {
				if (doDeepTracing) Log(" @R @R @R @R MARKET ORDER TO SELL TO CLOSE " + oldTradeRec.cQty.ToString() + " contracts of " + oldTradeRec.cSymbol + " at market");
				closeCallTicket = MarketOrder(oldTradeRec.cSymbol, -oldTradeRec.cQty); // sell the calls
			}

			if (closeCallTicket.Status == OrderStatus.Filled)
			{
				oldTradeRec.cEndPrice = closeCallTicket.AverageFillPrice;
				if (doDeepTracing) Log(" @R @R @R @R UPDATING PUT " + oldTradeRec.cSymbol + " END PRICE @ " + oldTradeRec.cEndPrice );
			}

			if (doDeepTracing) Log("-");

			// 2nd, buy back the long call
			doTheTrade = true;
			
			if(oldTradeRec.pSymbol != null){
				if  (oldTradeRec.pStrike >= stockPrice)											/// ITM PUT -- use limit order
				{																				/// PUT QTY should be negative from the opening short trade
					limitPrice = oldTradeRec.pStrike - stockPrice + 0.10M;
					if (doDeepTracing) Log(" @R @R @R @R LIMIT ORDER TO BUY TO CLOSE " + oldTradeRec.pQty.ToString() + " contracts of " + oldTradeRec.pSymbol + " at " + limitPrice.ToString());
					closePutTicket = LimitOrder(oldTradeRec.pSymbol, -oldTradeRec.pQty, limitPrice);
					//if (closePutTicket.Status == OrderStatus.Submitted) oldTradeRec.pEndPrice = limitPrice;
					oldTradeRec.pEndPrice = oldTradeRec.pStrike - stockPrice;
					OpenLimitOrder oLO = new OpenLimitOrder();
					oLO.oTicket = closePutTicket;
					oLO.tpr = oldTradeRec;
					oLO.oRight = OptionRight.Put;
					oLOs.Add(oLO);
		
				} else { 
					if (doDeepTracing) Log(" @R @R @R @R MARKET ORDER TO BUY TO CLOSE " + oldTradeRec.pQty.ToString() + " contracts of " + oldTradeRec.pSymbol + " at market");
					closePutTicket = MarketOrder(oldTradeRec.pSymbol, -oldTradeRec.pQty);  // buy the calls	
				}

				if (closePutTicket.Status == OrderStatus.Filled)
				{
					oldTradeRec.pEndPrice = closePutTicket.AverageFillPrice;
					if (doDeepTracing) Log(" @R @R @R @R UPDATING PUT END PRICE @ " + oldTradeRec.pEndPrice );
				}
			}
			// Log("-");

			
			// 3rd, buy back the long call
			doTheTrade = true;
			/*
			if (oldTradeRec.wcSymbol != null && oldTradeRec.wcQty != 0 && oldTradeRec.wcEndPrice == 0) {
				if  (oldTradeRec.wcStrike <= stockPrice)										/// ITM aCall -- use limit order
				{																				/// call QTY should be negative from the opening short trade
					limitPrice = stockPrice - oldTradeRec.wcStrike + 0.10M;
					if (doDeepTracing) Log(" @R @R @R LIMIT ORDER TO SELL TO CLOSE WING CALL " + oldTradeRec.wcQty.ToString() + " contracts of " + oldTradeRec.wcSymbol + " at " + limitPrice.ToString());
					closeWCallTicket = LimitOrder(oldTradeRec.wcSymbol, -oldTradeRec.wcQty, limitPrice);
					//if (closeCallTicket.Status == OrderStatus.Submitted) oldTradeRec.cEndPrice = limitPrice;
	
					oldTradeRec.wcEndPrice = limitPrice;												// set the wc Call End Price here bc finding this record in OnOrder() will be very difficult
					
					OpenLimitOrder oLO = new OpenLimitOrder();
					oLO.oTicket = closeWCallTicket;
					oLO.tpr = oldTradeRec;
					oLO.oRight = OptionRight.Call;
					oLO.isWingCall = true;
					oLOs.Add(oLO);
		
				} else {
					if (doDeepTracing) Log(" @R @R @R MARKET ORDER TO SELL TO CLOSE WING CALL " + oldTradeRec.wcQty.ToString() + " contracts of " + oldTradeRec.wcSymbol + " at market");
					closeWCallTicket = MarketOrder(oldTradeRec.wcSymbol, -oldTradeRec.wcQty);  // buy the calls	
					
				}
	
				if (doDeepTracing) Log("-");
	
				if (closeCallTicket.Status == OrderStatus.Filled)
				{
					oldTradeRec.wcEndPrice = closeWCallTicket.AverageFillPrice;
					if (doDeepTracing) Log(" @R @R @R UPDATING WING CALL " + oldTradeRec.wcSymbol + "END PRICE @ " + oldTradeRec.wcEndPrice );
				}
			}
			//  Keep the stock, but close this trade performance record.
			*/
			
			if (doDeepTracing) Log(" ROLLING ** ROLLING ** ROLLING ** ROLLING ** ROLLING ** SELL NEW COLLAR ** ROLLING ** ROLLING ** ROLLING ** ROLLING ** ROLLING ** ");
			
			symbolDataBySymbol[oldTradeRec.uSymbol].intTPRCntr += 1;
			//LUD.GetNextExDate(this);										// set last known dividend and next exdata
			LUD.getNxtIExDt(LUD.uSymbol.Value, this);
			LUD.loadVEData(this);

			symbolDataBySymbol[oldTradeRec.uSymbol].divdndAmt = LUD.divdndAmt;											// set new dividend amount
			symbolDataBySymbol[oldTradeRec.uSymbol].decOneYearPriceTarget_Initial = LUD.decOneYearPriceTarget; 		// set new initial 1-yr price target
			symbolDataBySymbol[oldTradeRec.uSymbol].initialTargetEndDate = LUD.initialTargetEndDate;					// set net initial 1-yr target date
			symbolDataBySymbol[oldTradeRec.uSymbol].VECase = LUD.VECase;

			oldTradeRec.uEndPrice = stockPrice;
			
			oldTradeRec.reasonForClose = reason;
			//oldTradeRec.isOpen = false;
			oldTradeRec.endDate = data.Time;
			oldTradeRec.grossPnL = currSellPnL;											// rolling essentially sells the existing options.  Log the currSellPnL for analysis purposes
			oldTradeRec.SSQRnetProfit = oldTradeRec.uQty*bestSSQRColumn.netIncome;		// log the best SSQRColumn.netIncome for tracking purposes
			
			//  Put on a new collar and start a new trade performance record
    		// make a new TradePerfRec
    		tradeRecCount = oldTradeRec.tradeRecCount + 1;				//  increment trade record count

    		TradePerfRec thisNewTPRec = new TradePerfRec();    
    		thisNewTPRec.uSymbol = LUD.uSymbol;					// keep the underlying symbol
    		thisNewTPRec.cSymbol = bestSSQRColumn.callSymbol;
    		thisNewTPRec.pSymbol = bestSSQRColumn.putSymbol;
    		//thisNewTPRec.wcSymbol = bestSSQRColumn.wCallSymbol;
    		thisNewTPRec.uStartPrice = stockPrice;				// log the current slice stock price
    		thisNewTPRec.uQty = oldTradeRec.uQty;				// maintain the same quantity
			//thisNewTPRec.isOpen = true;							// this new trade performance record is open
			thisNewTPRec.isInitializer = false;					// this is a continuation Collar
			thisNewTPRec.strtngCndtn = "ROLLED / " + reason;
			thisNewTPRec.index = symbolDataBySymbol[oldTradeRec.uSymbol].intTPRCntr;				// maintain the collarIndex through the entire sequence of collars
			thisNewTPRec.tradeRecCount = oldTradeRec.tradeRecCount + 1;			// count the trades
			thisNewTPRec.startDate = data.Time;					// set the start date
            thisNewTPRec.cStrike = bestSSQRColumn.callStrike;
			thisNewTPRec.cDelta = bestSSQRColumn.callDelta;
            //thisNewTPRec.wcStrike = bestSSQRColumn.wCallStrike;
			thisNewTPRec.expDate = bestSSQRColumn.callExpiry;			// set the options Expiry
			//thisNewTPRec.thetaExpiration = bestSSQRColumn.callExpiry;	// set the theta Expiry
			thisNewTPRec.ROC = bestSSQRColumn.ROC;		
			thisNewTPRec.ROR = bestSSQRColumn.ROR;
			thisNewTPRec.CCOR = bestSSQRColumn.CCOR;
            thisNewTPRec.RORThresh = RORThresh;
            thisNewTPRec.ROCThresh = ROCThresh;
            thisNewTPRec.CCORThresh = CCORThresh;
            //thisNewTPRec.tradeCriteria = switchROC ? "ROC" : "ROR";
            thisNewTPRec.tradeCriteria = LUD.VECase;
    		// thisNewTPRec.stockADX  = lastAdx;
    		// thisNewTPRec.stockADXR = lastAdxr;
    		// thisNewTPRec.stockOBV = lastObv;
    		// thisNewTPRec.stockAD = lastAd;
    		// thisNewTPRec.stockADOSC = lastAdOsc;
    		// thisNewTPRec.stockSTO = lastSto;
    		// thisNewTPRec.stockVariance = lastVariance;
			thisNewTPRec.VERating = LUD.intVERating;
			thisNewTPRec.momentum = LUD.decMomentum;
			thisNewTPRec.oneYearPriceTarget = LUD.decOneYearPriceTarget;
			thisNewTPRec.momentumRank = LUD.intMomentumRank;


			//Log(tradableColumn.ToString());
			var tradablePut = bestSSQRColumn.putSymbol;				// retrieve the put to buy
			var tradableCall = bestSSQRColumn.callSymbol;			// retrieve the call to sell
			var tradableWCall = bestSSQRColumn.wCallSymbol;		// retrievce wc call to sell
			
			// netOptions should be greater than the put premium + wc call premium.   Figure out how many wings can be bought.
			// wingPremium = bestSSQRColumn.wingFactor;
			// thisWingFactor = bestSSQRColumn.wingFactor;
			// thisWingFactor = 1;
			
			doTheTrade = true;
	
			//calculate the # of call Options to sell in $-Neutral Variable Call Coverage model:
			optionsToTrade = oldTradeRec.uQty/100;
    		//callsToTrade = Decimal.Round(optionsToTrade * bestSSQRColumn.putPremium / bestSSQRColumn.callPremium);					/// VCCPTS legacy code


			//  ***  ///    ****   tradablePut can be null
			doTheTrade = true;
			//if (doDeepTracing) Log(" @R @R @R EXECUTING PUT BUY MARKET ORDER TO OPEN " + ((1 + thisWingFactor) * optionsToTrade) + " contracts of " + tradablePut );
			if (tradablePut!=null){
				var putTicket = MarketOrder(tradablePut, optionsToTrade);						/// 2023-04-23  *** ::: **** reverted this to quantity==optionsToTrade from wingFactor*optionsToTrade
				if (putTicket.Status == OrderStatus.Filled)
				{
					thisNewTPRec.pSymbol = tradablePut;
					thisNewTPRec.pStartPrice = putTicket.AverageFillPrice;
					thisNewTPRec.pQty = (int)putTicket.QuantityFilled;
					thisNewTPRec.pStrike = bestSSQRColumn.putStrike;
					thisNewTPRec.pDelta = bestSSQRColumn.putDelta;

					//if (doDeepTracing) Log(" @R @R @R UPDATING PUT START PRICE TO " + thisNewTPRec.pStartPrice + " FOR " + thisNewTPRec.pQty + " CONTRACTS" );
				}
			}
					
			doTheTrade = true;

			if(bestSSQRColumn.callSymbol != null) {
				if (doDeepTracing) Log(" @R @R @R @R EXECUTING CALL BUY MARKET ORDER TO OPEN " + optionsToTrade + " contracts of " + tradableCall );
				//if (tradableCall.ID.StrikePrice > stockPrice) {
					callTicket = MarketOrder(tradableCall, -optionsToTrade);
				/*} else {
					limitPrice = stockPrice - tradableCall.ID.StrikePrice + 0.10M;
					if (doDeepTracing) Log(" @R @R @R @R LIMIT ORDER TO BUY TO CLOSE " + oldTradeRec.cQty.ToString() + " contracts of " + oldTradeRec.cSymbol + " at " + limitPrice.ToString());
					callTicket = LimitOrder(tradableCall, -optionsToTrade, limitPrice);
					//if (closeCallTicket.Status == OrderStatus.Submitted) oldTradeRec.cEndPrice = limitPrice;

					OpenLimitOrder oLO = new OpenLimitOrder();
					oLO.oTicket = callTicket;
					oLO.tpr = thisNewTPRec;
					oLO.oRight = OptionRight.Call;
					oLOs.Add(oLO);

				}
				*/
				//var callTicket = MarketOrder(tradableCall, -callsToTrade);
				if (callTicket.Status == OrderStatus.Filled)
				{
					thisNewTPRec.cSymbol = tradableCall;
					thisNewTPRec.cStartPrice = callTicket.AverageFillPrice;
					thisNewTPRec.cQty = (int)callTicket.QuantityFilled;
					//if (doDeepTracing) Log(" @R @R @R UPDATING SHORT CALL START PRICE TO " + thisNewTPRec.cStartPrice + " FOR " + thisNewTPRec.cQty + " CONTRACTS" );
				}
			}
			/*
			doTheTrade = true;
			if (thisWingFactor > 0) {
				//if (doDeepTracing) Log(" @R @R @R EXECUTING WING CALL BUY MARKET ORDER TO OPEN " + (thisWingFactor*optionsToTrade) + " contracts of " + tradableWCall );
				var wCallTicket = MarketOrder(tradableWCall, thisWingFactor * optionsToTrade);
				if (wCallTicket.Status == OrderStatus.Filled) {
					thisNewTPRec.wcSymbol = tradableWCall;
					thisNewTPRec.wcStartPrice = wCallTicket.AverageFillPrice;
					thisNewTPRec.wcQty = (int)wCallTicket.QuantityFilled;
					//if (doDeepTracing) Log(" @R @R @R UPDATING WING CALL START PRICE TO " + thisNewTPRec.wcStartPrice + " FOR " + thisNewTPRec.wcQty + " CONTRACTS" );
				} else {
					//if (doDeepTracing) Log(" ROLLING ** WING FACTOR IS 0 -- NO WINGS ADDED");
				}
			}	
			*/
			/// Roll is done.    save the new trade performance record
			
			var orderedSSQRMatrix = LUD.SSQRMatrix.OrderByDescending(p => p.ROR);
			IterateOrderedSSQRMatrix(orderedSSQRMatrix);
			
			//	IterateTradeRecord(thisNewTPRec);
			
			tprsToClose.Add(oldTradeRec);
			tprsToOpen.Add(thisNewTPRec);
			//tradeRecs.Add(thisNewTPRec);
			return true;
		}


		///////////////////////////////////////////////////////////////////////////////////
		//								GetBestCollar 2 parameters
		////////////////////////////////////////////////////////////////////////////////////
		public SSQRColumn GetBestCollar(CollarAlgorithm algo, ref LookupData LD)
        {
			if (haltProcessing)
			{
				// Log("  @@@@@@   Logging GetPotentialCollars 1 on Upside Potential");
			}
			
			Slice thisSlice = CurrentSlice;
			Symbol thisStock = LD.uSymbol;
			// First get the underlying stock price in this Slice
			decimal stockPrice = thisSlice[thisStock].Price;
			SSQRColumn bestTradableColumn = new SSQRColumn();

			OptionChain putChain;      // instantiate an OptionChain var for updating SSQRMatrix with slice data
			OptionChain callChain;      // 
			OptionChain wcallChain;		//
			
			OptionContract putContract;  //
			OptionContract callContract; //
			
			Symbol ssqrPutSymbol;           // instantiate a Symbol var for updating SSQRMatrix with slice Data
			Symbol ssqrCallSymbol;          //


			// Second get its options symbols
			var allUnderlyingOptionsSymbols = OptionChainProvider.GetOptionContractList(thisStock, thisSlice.Time);

			if (allUnderlyingOptionsSymbols.Count() == 0)               // missing data at this time
			{
				if (doDeepTracing) Debug(" DDDDDDDDDDDDDDDDDDDDD   Missing Data at " + thisSlice.Time + " no options for " + thisStock);
				return bestTradableColumn;
			}

			int findYear = thisSlice.Time.Year;
			int findMonth = thisSlice.Time.Month;

			// Compute the 3rd Friday of this month [options expiration] ---> do not adjust for potential holiday here
			DateTime thisMonthExpiry = FindDay(findYear, findMonth, DayOfWeek.Friday, 3);

			// Use the 3rd Friday of the current month to seed the function to return the next 4 ex-dividends expiries adjusted for holidays
			// in version 6, put package on whenever VE ranking is high.  (or Chaiken / Accumulation/Distribution indicates)
			Dictionary<int, DateTime> putExpiries = GetOptionExpiries(thisSlice.Time, LD, thisMonthExpiry, true, false);
			Dictionary<int, DateTime> callExpiries = GetOptionExpiries(thisSlice.Time, LD, thisMonthExpiry, true, true);

			// now assemble the SSQR matrix using the expiries dictionary and the contracts lists
			
			LD.SSQRMatrix.Clear();
			AssembleSSQRMatrix(this, ref LD, putExpiries, callExpiries);

			
    		// Get the SSQRColumn with the best reward to risk
	    	if (LD.SSQRMatrix == null | LD.SSQRMatrix.Count == 0){
				if(LD.doTracing) algo.Debug($" ** GET BEST COLLAR ** ::  0 or empty SSQR in TradeDetermination.GetBestCollar for {LD.uSymbol.Value}");
				return bestTradableColumn;		/// found it's possible to have no SSQRs, if so, pass the empty/null SSQRColumn to calling routine
			}
			
			if(algo.symbolDataBySymbol.ContainsKey(thisStock)) {
				algo.symbolDataBySymbol[thisStock].VECase = LD.VECase;

			}
			
			// var qualifyingCollars = LD.SSQRMatrix.Where(s=>s.putPremium!=0 & s.putPremium<=s.callPremium).Count();
			
			// if (qualifyingCollars == 0) return bestTradableColumn;
			
			// bestTradableColumn = passedMatrix.OrderByDescending(p => p.CCOR).FirstOrDefault();
			bestTradableColumn = LD.SSQRMatrix.OrderByDescending(bTC => bTC.ROR).FirstOrDefault();   						/// 2021-03-21 -- changed from OrderedByDescending ..... using downsideRisk/upsidePotential
			//bestTradableColumn = LD.SSQRMatrix.OrderByDescending(bTC => bTC.upsidePotential).FirstOrDefault();   /// 2022-12-12 -- changed from ROR ..... using upsidePotential
			//bestTradableColumn = LD.SSQRMatrix.OrderByDescending(bTC=>bTC.putExpiry).ThenByDescending(bTC=>bTC.upsidePotential).FirstOrDefault();
			if(LD.doDeepTracing) algo.Log($" ** GET BEST COLLAR ** :: The bestTradable ROR for {LD.uSymbol.Value} is {bestTradableColumn.ROR.ToString("0.00")}");
			/*if ((decimal)bestTradableColumn.ROR < 1m){
				if(LD.doTracing) algo.Debug($" TD ** TD ** TD ** TD *** BestCollar {bestTradableColumn.ROR} failed ROR Threshold for {LD.uSymbol.Value}");
				if (symbolDataBySymbol.ContainsKey(bestTradableColumn.uSymbol)) {
					symbolDataBySymbol[bestSSQRColumn.uSymbol].SSQRFailCnt += 1;
					if (symbolDataBySymbol[bestSSQRColumn.uSymbol].SSQRFailCnt >=4 ) {
						symbolDataBySymbol[bestSSQRColumn.uSymbol].isRollable = false;
						SymbolsToRemove.Add(bestSSQRColumn.uSymbol);
					}

				}
				return null;		/// found it's possible to have no SSQRs, if so, pass the empty/null SSQRColumn to calling routine
			}
			*/
			return bestTradableColumn;
		}

		/////////////////////////////////////////////////////////////////////////////////////
		// // // 						Excute SSQRS
		////////////////////////////////////////////////////////////////////////////////////
		public void ExecuteSSQRs(Slice slc, ColumnSD csd){
			didTheTrade = ExecuteTrade(slc, csd.col, ref csd.sd, csd.ld);

			if (didTheTrade) {
				logPortfolio = true;
				if (doTracing) Log($"*** *** ***  DID TRADE --  {thisSymbol} --- Based upon this SSQR matrix: ");
				if (csd.sd.bbSignal == 1) csd.sd.bbSignal = 0;
				// var orderedSSQRMatrix = LUD.SSQRMatrix.OrderByDescending(p => p.upsidePotential);
				var orderedSSQRMatrix = csd.ld.SSQRMatrix.OrderByDescending(p => p.ROR);
				IterateOrderedSSQRMatrix(orderedSSQRMatrix);
			
			} else {
				if (doTracing) Log($"*** *** ***  DIDN'T TRADE - {thisSymbol} --- ");
			}

			if (doTracing && logPortfolio)
			{
				Log($"|||| |||| TRADE RESULTS/HOLDINGS: on {slc.Time.ToShortDateString()} at {slc.Time.ToShortTimeString()}");
				foreach(var kvp in Securities)		/// make sure there's no leaking of abandoned stocks or options
				{
					try{
						var security = kvp.Value;
						if (security.Invested)
						{
							Log($"|||| |||| ||||  Package: {security.Symbol} : {security.Holdings.Quantity} @ {security.BidPrice} by {security.AskPrice}");
						}
					} catch (Exception errMsg)
					{
						Log(" ERROR at 383 in main.cs " + errMsg );
					}
					
				}
			
				didTheTrade = false;
			}
		}

        // |||||||||||||||||||||||||||||||||||||||||||||||
        // Prints greeks for the corresponding symbol
        public void PrintGreeks(ref Dictionary<Symbol, bool> foundOption, Slice thisSlice, Symbol pairKey, bool pairValue) {
        	decimal callDelta;
        	if (pairValue == true) { return; }
        	foreach(var chain in thisSlice.OptionChains) {
				foreach(var option in chain.Value) {
					if(pairKey.ToString() == option.ToString()) {
						callDelta = option.Greeks.Delta;
						foundOption[pairKey] = true;
						/////if (doDeepTracing) Log(" || Succesfully added Greeks || " + pairKey + " Delta = " + callDelta.ToString());
						//break;
					}
				}
			}
        }
        
        // |||||||||||||||||||||||||||||||||||||||||||||||
        // Loops through dictionary of active contracts
        
        public void CheckGreeks(ref Dictionary<Symbol, bool> foundOption, Slice thisSlice) {
			OptionContract callContract;
			OptionChain callChain;
			Symbol optSymbol;
			Dictionary<Symbol, bool> tempDict = foundOption;
			foreach(var pair in tempDict) {
				Symbol pairKey = pair.Key;
				bool pairValue = pair.Value;
				PrintGreeks(ref foundOption, thisSlice, pairKey, pairValue);
			}
		}

    }
}
#region imports
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using QuantConnect.Data;
    using QuantConnect.Orders;
#endregion
using QuantConnect.Securities.Option;
using Newtonsoft.Json;

namespace QuantConnect.Algorithm.CSharp {
	
	public partial class CollarAlgorithm : QCAlgorithm
	{
		public class optGrksRec {
			//algo.Log(thisContract.Symbol.Value  + ", " + thisContract.BidPrice + ", " + thisContract.AskPrice + ", "  + thisContract.LastPrice + ", " + 
							//thisContract.OpenInterest + ", "+ testVol + ", " + thisContract.TheoreticalPrice + ", " + thisContract.Greeks.Delta + ", " + thisContract.ImpliedVolatility);
							// "Gamma: " + thisContract.Greeks.Gamma + "Vega: " + thisContract.Greeks.Vega + "Rho: " + thisContract.Greeks.Rho + "Theta: " + thisContract.Greeks.Theta / 365 +4
			
			public string uSymbol;			// Underlying Symbol
			public string BidPrice;			// Bid Price
			public string AskPrice;			// Ask Price
			public string LastPrice;		// Last Price
			public string OpenInterest;		// Open Interest
			public string TheoreticalPrice;	// Theoretical Price
			public string Delta;			// Delta
			public string ImpliedVolatility; // Implied Vol
			public string Gamma;			// Gamma
			public string Vega;				// Vega
			public string Rho;				// Rho
			public string Theta;			// Theta
		
			public string ToJson()
			{
				string json = JsonConvert.SerializeObject(this, Formatting.Indented);
				return json;
			}
			
		}
		
		public partial class TradePerfRec
		{
			public Symbol uSymbol;					// 1 Underlying Symbol
			public int index;						// 2 Index to trace the trade and all offspring P&L
			public bool isOpen = false;				// 3 Is the trade ongoing (open)?
			public bool isInitializer = false;		// 4 Is this the collar-initializing trade
			public bool isSecondary = false;		// 5 Is this a put roll up 
			public bool isTheta = false;			// 6 Is this a solely-call TPR
			public int tradeRecCount;				// 7 counter for trade records -- use in the single-stock use case
			public DateTime startDate;				// 8 starting date for collar
			public DateTime endDate;				// 9 ending date for the collar
			public string strtngCndtn;				// 10 for 2nd TPRs, record the starting conditions
			public string reasonForClose;			// 11 reason why collar was killed (ITM options roll, etc.)
			public DateTime expDate;				// 12 expiration date for collar
			public DateTime thetaExpiration;		// 13 expiration date for the short call
			public Symbol pSymbol;					// 14 Put Symbol
			public Symbol cSymbol;					// 15 Call Symbol
			public Symbol wcSymbol;					// 16 Wing Call Symbol
			public decimal pStrike;					// 17 put strike
			public decimal cStrike;					// 18 call strike
			public decimal wcStrike;				// 19 ATM Call Strike
			public decimal pDelta;					// 20 put Delta
			public decimal cDelta;					// 21 call Delta
			public decimal wcDelta;					// 22 atm Call Delta
			public decimal pGamma;					// 23 put Gamma
			public decimal cGamma;					// 24 call Gamma
			public decimal wcGamma;					// 25 atm Call Gamma
			public int uQty;						// 26 number of underlying shares
			public int pQty;						// 27 number of put contracts
			public int cQty;						// 28 number of call contracts
			public int wcQty;						// 29 number of wing call contracts
			public decimal uStartPrice;				// 30 Underlying Price when trade put on
			public decimal pStartPrice;				// 31 Put Price when trade put on
			public decimal cStartPrice;				// 32 Call Price when trade put on
			public decimal wcStartPrice;			// 33 ATM Call Price when trade put on
			public decimal uEndPrice;				// 34 Underlying Price when trade taken off
			public decimal pEndPrice;				// 35 Put Price when trade taken off
			public decimal cEndPrice;				// 36 Call Price when trade taken off
			public decimal wcEndPrice;				// 37 ATM Call Price when trade taken off
			public int	numDividends;				// 38 # of dividends collected during the trade
			public decimal divIncome;				// 39 $'s collected in Dividend income during the trade
			public decimal betaValue;				// 40 beta value of underlying when trade put on
			public decimal RORThresh;				// 41 Threshold for ROR
			public decimal ROCThresh;				// 42 Threshold for ROC
			public decimal CCORThresh;				// 43 Threshold for CCOR
			public string tradeCriteria;			// 44 ROR or ROC or CCOR
			public decimal ROR;						// 45 ROR calculation from SSQR Matrix
			public decimal ROC;						// 46 ROC calculation from SSQR Matrix
			public decimal CCOR;					// 47 CCOR calculation from SSQR Matrix
			public decimal stockADX;				// 48 Average Directional Index Value
			public decimal stockADXR;				// 49 Average Directional Index Rating
			public decimal stockOBV;				// 50 On Balance Volume
			public decimal stockAD;					// 51 Accumulation/Distribution
			public decimal stockADOSC;				// 52 Accumulation/Distribution Oscillator
			public decimal stockSTO;				// 53 Stochastic value
			
			public decimal stockVariance;			// 54 Variance of underlying stock
			public decimal currSellPnL;				// 55.. 	Rolltime evaluation of PnL if selling
			public decimal currExrcsPutPnL;			// 56..		Rolltime evaluation of PnL if exercising put
			public decimal currExrcsCallPnL;		// 57..		Rolltime evaluation of PnL if calls are assigned
			public decimal grossPnL;				// 58 runtime calculation of PnL at close;
			public decimal SSQRnetProfit;			// 59 runtime calculation of replacement bestSSQR net Profit
			public int VERating;					// 60 VE Rating for Stat Analysis
			public decimal momentum;				// 61 VE momentum for Stat Analysis
			public decimal oneYearPriceTarget;		// 62 VE OYPT for Stat Analysis
			public int momentumRank;	  			// 63 VE Momentum Rank for Stat Analysis

		
			//   ****    put class methods here to use collection of TradePerfRecs as basis to examine positions for expirations and assignments
			
			public void CleanUp(CollarAlgorithm algo) {
				DateTime endTime = algo.CurrentSlice.Time;
				//if (this.pEndPrice != 0) this.pEndPrice = 0;
				//if (this.cEndPrice != 0) this.cEndPrice = 0;
				this.endDate = endTime;
				/*decimal securitiesPrice = algo.Securities[this.uSymbol].Price;
				if (securitiesPrice == 0){
					algo.Debug($" ***** ***** No Clean up Securities Price for {this.uSymbol.Value}. ");
					if (algo.lastClosePrice[this.uSymbol] !=0 ){
						securitiesPrice = algo.lastClosePrice[this.uSymbol];
					} else algo.Debug($" ***** ***** No Clean up lastClosePrice data for {this.uSymbol.Value} . ");
					
				}

				if (this.uEndPrice != 0) this.uEndPrice = securitiesPrice;
				*/
				this.reasonForClose = "Orphaned Package due to Options Expiration without assignment / exercise.";
				//algo.Log($"  **************  CLEANING UP ORPHAN STOCK {this.uSymbol.Value} and setting end price to {this.uEndPrice.ToString("0.00")}");
				algo.tprsToClose.Add(this);

			}
			

			public bool CheckRolling(CollarAlgorithm algo, LookupData LUD)
			{
			try {
				bool hasPut = false;
				bool hasCall = false;

				Slice slc = algo.CurrentSlice;
				Symbol symbUndr = this.uSymbol;
				LUD.clearLD(algo);
				LUD.uSymbol = this.uSymbol;										////  CRITICAL :: SET THE SYMBOL TO BE PROCESSED IN THE LUD

				
				//if (symbUndr.Value == "CNP") {
				//	algo.Debug(" --- --- This is CNP Processing");
				//}
				string strTckr = symbUndr.Value;

				decimal stkPrc = 0m;
				decimal putPrc = 0m;
				decimal callPrc = 0m;

				if(LUD.doTracing) algo.Log($"  ******* Check Rolling Symbol {this.uSymbol.Value} TPR .");
				
				if (LUD.intType==0 && algo.symbolDataBySymbol.ContainsKey(this.uSymbol)){
					if(!algo.symbolDataBySymbol[this.uSymbol].isRollable){
						if(LUD.doTracing) algo.Log($"  **************  Symbol {this.uSymbol.Value} is not rollable -- check if this TPR is going to be closed.");
						return true;
					}
				} else if (LUD.intType!=0 && algo.etfDataBySymbol.ContainsKey(this.uSymbol)){
					if(!algo.etfDataBySymbol[this.uSymbol].isRollable){
						if(LUD.doTracing) algo.Log($"  **************  Symbol {this.uSymbol.Value} is not rollable -- check if this EFTPR is going to be closed.");
						return true;
					}
				} else {
						if(LUD.doTracing) algo.Log($"  **************  Symbol {this.uSymbol.Value} is no longer in SDBS.");
						algo.tprsToClose.Add(this);
						return false;
				}

				if (LUD.doTracing) algo.Log(" **************  TPR CheckRolling for  " + symbUndr.Value + "  @" + slc.Time.ToString() );						


				//if (slc.ContainsKey(symbUndr) )							// 2023-03-12  ***** **** WHY CHECK IF THERE WAS A TICK FOR THIS UNDERLYING IN CURRENT SLICE.   SHOULD BE UNNECCESSARY
				//{ 
					// var tryPrice = slc[symbUndr].Price;   
					// 2023-03-13 *** *** *** NOTE NOTE NOTE *** SUBSTANTIAL CHANGE
					// var tryPrice == null;
					if(slc.ContainsKey(this.uSymbol)) {
						stkPrc = slc[this.uSymbol].Price;
					} else stkPrc = algo.Securities[this.uSymbol].Price;

					if (stkPrc == 0) 
					{
						if (LUD.doTracing) algo.Log(" **************  TPR CheckRolling found no price data for  " + symbUndr.Value + "  @" + slc.Time.ToString() );						
						return false;
					}
					//stkPrc = Convert.ToDecimal(tryPrice);

					LUD.stockPrice = stkPrc;
					if(LUD.intType==0){
						LUD.initialTargetEndDate = algo.symbolDataBySymbol[this.uSymbol].initialTargetEndDate;
						LUD.decOneYearPriceTarget = algo.symbolDataBySymbol[this.uSymbol].decOneYearPriceTarget_Initial;
					} else {
						LUD.initialTargetEndDate = algo.FindDay(slc.Time.AddMonths(1).Year, slc.Time.AddMonths(1).Month, DayOfWeek.Friday, 4);
						LUD.decOneYearPriceTarget = algo.etfDataBySymbol[this.uSymbol].decMOMP/100m * stkPrc;
					}

					if (this.pQty != 0){
						var tryPrice = algo.Securities[this.pSymbol].AskPrice;
						if (tryPrice==null)
						{
							if (LUD.doTracing) algo.Log(" **************  TPR CheckRolling found no Put Bid Price data for  " + this.pSymbol.Value + "  @" + slc.Time.ToString() );						
							return false;
						} else {
							putPrc = Convert.ToDecimal(tryPrice);
							hasPut = true;
						}
					}

					if (this.cQty != 0){
						var tryPrice = algo.Securities[this.cSymbol].BidPrice;
						if (tryPrice==null)
						{
							if (LUD.doTracing) algo.Log(" **************  TPR CheckRolling found no Call Price data for  " + this.pSymbol.Value + "  @" + slc.Time.ToString() );						
							return false;
						} else {
							callPrc = Convert.ToDecimal(tryPrice);
							hasCall = true;
						}
					}
				
				//} else { 
				//	if (LUD.doTracing) algo.Log(" **************  TPR CheckRolling found no data for  " + symbUndr.Value + "  @" + slc.Time.ToString() );
				//		return false;
				//}
				
				LUD.dtTst = slc.Time;
				
				if(LUD.intType==0) LUD.getNxtIExDt(this.uSymbol.Value, algo);					////  get NextExDate for this symbol
				
				this.GetPnLs(algo, LUD, ref stkPrc, ref callPrc, ref putPrc);
				
				if(LUD.intType==0) LUD.daysRemainingDiv = LUD.exDivdnDate.Subtract(slc.Time).Days;
				
				if (hasCall) {
					LUD.daysRemainingC = this.cSymbol.ID.Date.Subtract(slc.Time).Days;
				 } else LUD.daysRemainingC = 100;

				if (hasPut) { 
					LUD.daysRemainingP = this.pSymbol.ID.Date.Subtract(slc.Time).Days;	
				} else LUD.daysRemainingP = 100;
			
				if(LUD.doDeepTracing) algo.Debug($" ********* {this.uSymbol.Value} Package Days Remaining:  | Div: {LUD.daysRemainingDiv.ToString()} | Put: {LUD.daysRemainingP.ToString()}  |  Call: {LUD.daysRemainingC.ToString()} ---"  );


				/*
				if (LUD.intType==0 && hasCall && LUD.daysRemainingDiv < 4 && LUD.daysRemainingDiv > 0)				// restrict Dividend Approachment to stock.  Don't do ETF's
				{
					LUD.SSQRMatrix.Clear();
					if (LUD.doTracing) algo.Debug(" **************  Calling TPR CheckDivRoll  " + symbUndr.Value + "  @" + slc.Time.ToString() );
					if (this.CheckDivRoll(algo, ref stkPrc, LUD)) return true;
					
				}
				*/

				if (hasCall) {
					if (((stkPrc - this.cStrike)/stkPrc >= .05M && LUD.daysRemainingC <= 10 && LUD.daysRemainingC > 1) || ((stkPrc - this.cStrike) > 0 && LUD.daysRemainingC <= 1))		
					{
						LUD.SSQRMatrix.Clear();
						if (LUD.doTracing) algo.Debug(" **************  Calling TPR CheckCallRoll  " + symbUndr.Value + "  @" + slc.Time.ToString() );
						if (this.CheckCallRoll(algo, LUD, ref stkPrc, ref callPrc, ref putPrc)) return true;
					}
				}			
				
				if (hasPut) {
					if (( (this.pStrike - stkPrc )/stkPrc >= .05M && LUD.daysRemainingP <= 10 && LUD.daysRemainingP > 1) || ( (this.pStrike > stkPrc) && LUD.daysRemainingP <= 1) )
					{
						LUD.SSQRMatrix.Clear();
						if (LUD.doTracing) algo.Debug(" **************  Calling TPR CheckPutRoll  " + symbUndr.Value + "  @" + slc.Time.ToString() );
						if (this.CheckPutRoll(algo, LUD, ref stkPrc, ref callPrc, ref putPrc)) return true;
					}
				}

				if ((hasCall && (LUD.daysRemainingC <= 1 && stkPrc <= this.cStrike)) | (hasPut && (LUD.daysRemainingP <= 1 && stkPrc >= this.pStrike)))					// this is the put expiration by design.  the puts always control the collar and the risk
				{
					LUD.SSQRMatrix.Clear();
					if (LUD.doTracing) algo.Debug(" ************** Calling TPR CheckOTMRoll  " + symbUndr.Value + "  @" + slc.Time.ToString() );
					if (CheckOTMRoll(algo, LUD, ref stkPrc, ref callPrc, ref putPrc) ) return true;
				}
			
				return false;	
				
				} catch (Exception errMsg)
				{
					algo.Debug(" ERROR TradeLogging.CheckRolling.cs " + errMsg );

					return false;													//// 2022-02-15:  replaced return with continue -- could have caused premature exits
				}	
			}		////// end CheckRolling
		
		
		//*************************************************************************************************
		//**************	GetPnLs          *************************************************************
		//*** **** **** 	calculate P^L based upon current put bid and current call ask prices   -- 
		//*** **** **** 	this is conservative becuase limit orders at mid point would actually be used.		//// /// *** *** *** *** VERIFIED CALCULATIONS FOR SHORT SYSTEM
		//*************************************************************************************************
			public void GetPnLs(CollarAlgorithm algo, LookupData LUD, ref decimal stockPrice, ref decimal currCallBidPrice, ref decimal currPutAskPrice)
			{
				
				this.currSellPnL = (this.uQty*(stockPrice-this.uStartPrice)) + (100*this.pQty*(currPutAskPrice - this.pStartPrice)) + 
					(100*this.cQty*(currCallBidPrice - this.cStartPrice));																/// + (100*this.wcQty*(this.wcEndPrice - this.wcStartPrice)); 
				
				if (this.pStrike > stockPrice ) {
					this.currExrcsPutPnL = (this.uQty*(this.pStrike-this.uStartPrice)) + (100*this.pQty*(0 + this.pStartPrice)) + 
						(100*this.cQty*(currCallBidPrice - this.cStartPrice));																/// + (100*this.wcQty*(this.wcEndPrice - this.wcStartPrice)); 
				} else {this.currExrcsPutPnL = -10000000;}					

				if (this.cStrike < stockPrice ) {
					this.currExrcsCallPnL = (this.uQty*(this.cStrike-this.uStartPrice)) + (100*this.pQty*(currPutAskPrice - this.pStartPrice)) + 
						(100*this.cQty*(0 - this.cStartPrice));																			/// + (100*this.wcQty*(this.wcEndPrice - this.wcStartPrice));
				} else {this.currExrcsCallPnL = -10000000;}

				//algo.Log($" -- -- -- P&L's   Sell: {this.currExrcsPutPnL.ToString("0.00")} PutExercise: {this.currExrcsPutPnL.ToString("0.00")}  CallExercise: {this.currExrcsPutPnL.ToString("0.00")} .");
			}
		
		//*************************************************************************************************
		//**************     GetCorrspndingPut      *********   THIS IS USED FOR DETECTING DIVIDEND-DRIVEN ASSIGNMENT
		//*************************************************************************************************
			private Symbol GetCorrspndngPut()
			{    		
				int indexOfC = this.cSymbol.ToString().LastIndexOf("C");
				char[] charArrayC = this.cSymbol.ToString().ToCharArray();
				char[] charArrayP = charArrayC;
				charArrayP[indexOfC] = 'P';
				string putString = new string(charArrayP);

				return putString;
			}

		//*************************************************************************************************
		//**************     CheckOTMRoll           *******************************************************
		//*************************************************************************************************
		public bool CheckOTMRoll(CollarAlgorithm algo , LookupData LUD, ref decimal stockPrice, ref decimal currCallBidPrice, ref decimal currPutAskPrice) 
		{
			bool killed = false;
			bool annHighFade = false;

			bool isRolled = false;
			// risk of options expiration WITHOUT EXERCISE
			if (LUD.doTracing) algo.Log($" **************  BEGIN OTM OPTIONS CALC FOR  {LUD.uSymbol} ****************");

		try {	
			SymbolData sd = LUD.intType==0 ? algo.symbolDataBySymbol[LUD.uSymbol] : algo.etfDataBySymbol[LUD.uSymbol];
			if (LUD.intType == 0) sd.decOneYearPriceTarget_Current = LUD.decOneYearPriceTarget;
			
			//LUD.loadVEData(algo);
			
			Slice slD = algo.CurrentSlice;

			var oneYearBars = sd.Bars.ToList();
			var lowBar = oneYearBars.Min(b => b.Close);
			//if (stockPrice < lowBar) annHighFade = true;
			
			if(LUD.intType==0 && annHighFade) {
				if (LUD.doTracing) algo.Log($" **************    KILLING {LUD.uSymbol.Value} due to price {stockPrice.ToString()} being below annual low close {lowBar.ToString()} minus 2.");
				killed = isRolled = algo.KillTheCollar(this,  ref LUD, "ABORT OTM ROLL -- PROXIMITY TO ANNUAL LOW " + LUD.uSymbol, false, true );
				if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & killed){
						algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
						//algo.SymbolsToRemove.Add(this.uSymbol);
				}
				return killed;	
			}

			SSQRColumn bestSSQRColumn = new SSQRColumn();
			if (LUD.intType==0) {
				bestSSQRColumn = algo.GetBestCollar(algo, ref LUD);
			} else {
				//algo.Debug($" *** *** *** *** *** THE {LUD.uSymbol.Value} SECURITY LUD.TYPE IS {LUD.intType}");
				 bestSSQRColumn = LUD.SSQRMatrix.FirstOrDefault();
			}
    		
			if (LUD.SSQRMatrix.Count == 0) {
    			if (LUD.daysRemainingC <= 1 | LUD.daysRemainingP <= 1) {

					if (LUD.doTracing) algo.Log($" *** *** *** *** *** KILLING {LUD.uSymbol.Value} on last day because no collars came back.");

    				killed = isRolled = algo.KillTheCollar(this,  ref LUD, $"ABORT OTM ROLL -- NO POT COLLARS FOR {LUD.uSymbol.Value}", false, true);

					if(LUD.intType==0){
						if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & killed){
								algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
								//algo.SymbolsToRemove.Add(this.uSymbol);
						}
					} else if (killed) {
						algo.etfDataBySymbol[this.uSymbol].isRollable = false;	
						
					}
 					LUD.SSQRMatrix.Clear();
					bestSSQRColumn = new SSQRColumn();
					
					if (LUD.doDeepTracing) algo.Log($" **************  END OTM OPTIONS KILL FOR  {LUD.uSymbol.Value} ****************");
					if (LUD.doDeepTracing) algo.Log("-");
					return isRolled;
   				} else {
	    			LUD.SSQRMatrix.Clear();
	    			bestSSQRColumn = new SSQRColumn();
	
	    			algo.Debug($" **************  END OTM OPTIONS CALC -- NO POTCOLS FOR  {LUD.uSymbol.Value} -- LOOP AND TRY AGAIN LATER  ***");
	    			return isRolled;										// if no collars then return and loop around again
	    		}
    		}
    		

    		if (bestSSQRColumn == null || bestSSQRColumn.IsEmpty()) {
    			if (LUD.doTracing) algo.Log($" **************  null bestSSQRColumn in OTM Expiry Approachment FOR  {LUD.uSymbol.Value} *************");
    			if (LUD.doTracing) algo.Log($" **************  END  OTM OPTIONS CALC FOR  {LUD.uSymbol.Value} ****************");
    			
    			if (LUD.daysRemainingC <= 1 | LUD.daysRemainingP <= 1) {
					if (LUD.doTracing) algo.Log($" *** *** *** *** *** KILLING {LUD.uSymbol.Value} on last day because no collars came back.");
    				killed = isRolled = algo.KillTheCollar(this, ref LUD, $"KILLED IN OTM PROCESSING -- NO VIABLE SSQRS FOR {LUD.uSymbol.Value}", false, true);
					if(LUD.intType==0){
						if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & killed){
								algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
								//algo.SymbolsToRemove.Add(this.uSymbol);
						}
					} else if (killed) {
						algo.etfDataBySymbol[this.uSymbol].isRollable = false;	
						//algo.SymbolsToRemove.Add(this.uSymbol);
					}

					LUD.SSQRMatrix.Clear();
					bestSSQRColumn = new SSQRColumn();
							
					if (LUD.doDeepTracing) algo.Log($" **************  END OTM OPTIONS KILL FOR  {LUD.uSymbol} LOOP AND TRY AGAIN LATER ****************");
					if (LUD.doDeepTracing) algo.Log("-----");
					return isRolled;
    			} else {

	    			LUD.SSQRMatrix.Clear();
	    			bestSSQRColumn = new SSQRColumn();
	    			if (LUD.doDeepTracing) algo.Log($" **************  END OTM OPTIONS CALC  FOR  {LUD.uSymbol} -- bestSSQRColumn NULL or EMPTY ***");
	    			return isRolled;													//  exit OnData() and loop around and try again
    			}	
    		} 	// no bestSSQRColumn	
    		
			// IS IT NECESSARY TO SET THESE HERE
			Symbol tradablePut = bestSSQRColumn.putSymbol;
			Symbol tradableCall = bestSSQRColumn.callSymbol;

			//goodThresh = bestSSQRColumn.CCOR >= CCORThresh;
			// bool goodThresh = (LUD.intVERating == 5 & LUD.decOneYearPriceTarget > 1.05m * stockPrice) | (LUD.intVERating > 3 & bestSSQRColumn.upsidePotential >=5);
			bool goodThresh = (((LUD.divdndAmt * 3m) + LUD.decOneYearPriceTarget) < .95m * stockPrice) | algo.symbolDataBySymbol[this.uSymbol].continueTrade;
    		
    		if (goodThresh)				// roll the position forward
			{
				if (LUD.doTracing) algo.Log($" **************  BEGIN OTM OPTIONS ROLL FOR  {LUD.uSymbol}  ****************");
				
				bool bRollable = algo.symbolDataBySymbol[LUD.uSymbol].isRollable;

				if (!annHighFade && bRollable && (this.currSellPnL > 0 | this.uQty * bestSSQRColumn.upsidePotential > Math.Abs(this.currSellPnL) | algo.symbolDataBySymbol[this.uSymbol].continueTrade )){										// only roll the collar if the current record may be closed profitably-- otherwise seek exercise in kill
				//if (currSellPnL > 0) {
					if (algo.RollTheCollar(LUD, this, ref bestSSQRColumn, "ROLLED IN OTM EXPIRATION ")) {
						isRolled = true;
						if (LUD.doDeepTracing) algo.Log($" **************  ROLLED OTM OPTIONS  FOR  {LUD.uSymbol} COMPLETED WITH SSQR: ****************");
						if (LUD.doDeepTracing) algo.Log("-");
						var orderedSSQRMatrix = LUD.SSQRMatrix.OrderBy(p => p.upsidePotential);

   						algo.IterateOrderedSSQRMatrix(orderedSSQRMatrix);
   						
						//didTheTrade = false;
						LUD.SSQRMatrix.Clear();
						bestSSQRColumn = new SSQRColumn();
						
						if (LUD.doDeepTracing) algo.Log($" **************  END SUCCESSFUL OTM OPTIONS ROLL FOR  {LUD.uSymbol}  ****************");
						if (LUD.doDeepTracing) algo.Log("-");
						return isRolled;
   					} else {
		    			if (LUD.daysRemainingC <= 1 | LUD.daysRemainingP <= 1) {
    						if (LUD.doTracing) algo.Log($" **************  KILLING OTM OPTIONS COLLAR  FOR  {LUD.uSymbol}  ON LAST DAY - FAILED ROLL ****************");
    						killed = isRolled = algo.KillTheCollar(this, ref LUD, $"KILLED IN OTM PROCESSING -- FAILED ROLL FOR {LUD.uSymbol}", false, true);
							if(LUD.intType==0){
								if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & killed){
									algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
									//algo.SymbolsToRemove.Add(this.uSymbol);
								}
							} else if (killed) {
								algo.etfDataBySymbol[this.uSymbol].isRollable = false;	
								// algo.SymbolsToRemove.Add(this.uSymbol);
							}
		
							LUD.SSQRMatrix.Clear();
							bestSSQRColumn = new SSQRColumn();
							
							if (LUD.doTracing) algo.Debug($" **************  END OTM OPTIONS KILL  FOR  {LUD.uSymbol}  ON LAST DAY - FAILED ROLL ****************");
							if (LUD.doTracing) algo.Log("-");
							return isRolled;
    					}
						LUD.SSQRMatrix.Clear();
						bestSSQRColumn = new SSQRColumn();
						
						if (LUD.doTracing) algo.Debug($" **************  END OTM OPTIONS ROLL  FOR  {LUD.uSymbol}  -- FAILED ROLL ****************");
						if (LUD.doTracing) algo.Log("-");
						return isRolled;
   					}
				} else if (LUD.daysRemainingC <= 1 | LUD.daysRemainingP <= 1) {			//   CANNOT EXECUTE ROLL PROFITABLY SO KILL THE COLLAR IF ON LAST DAY
					if (annHighFade & LUD.doTracing) algo.Debug($" **************    FADE ROLLING {LUD.uSymbol.Value} due to price {stockPrice.ToString()} being above annual low close {lowBar.ToString()} minus 2.");

					killed = isRolled = algo.KillTheCollar(this,  ref LUD, $"KILLED IN OTM PROCESSING -- UNPROFITABLE ROLL FOR {LUD.uSymbol} ON THE LAST DAY", false, true );

					if(LUD.intType==0){
						if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & killed){
								algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
								//algo.SymbolsToRemove.Add(this.uSymbol);
						}
					} else if (killed) {
						algo.etfDataBySymbol[this.uSymbol].isRollable = false;	
						// algo.SymbolsToRemove.Add(this.uSymbol);
					}

					LUD.SSQRMatrix.Clear();
					bestSSQRColumn = new SSQRColumn();
					
					if (LUD.doTracing) algo.Debug($" **************  END OTM OPTIONS ROLL  FOR  {LUD.uSymbol} WITH KILL ****************");
					if (LUD.doTracing) algo.Log("-");
					return isRolled;
				}

				if (LUD.doTracing) algo.Debug($" **************  END OTM OPTIONS ROLL PROCSSING FOR  {LUD.uSymbol}  ****************");
				if (LUD.doTracing) algo.Log("------------------------------------");
				LUD.SSQRMatrix.Clear();
				bestSSQRColumn = new SSQRColumn();
				return isRolled;

			} else if (LUD.daysRemainingC <= 1 | LUD.daysRemainingP <= 1) {				// IF BADTHRESH
			
				if (LUD.doTracing) algo.Debug($" **************  BEGIN OTM OPTIONS COLLAR KILL FOR  {LUD.uSymbol} ON LAST DAY ****************");
		
				// kill the collar
				killed = isRolled = algo.KillTheCollar(this, ref LUD, "BAD THRESH ON OTM OPTIONS ROLL", false, true);
				if(LUD.intType==0){
					if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & killed){
						algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
						//algo.SymbolsToRemove.Add(this.uSymbol);
					}
				} else if (killed) {
					algo.etfDataBySymbol[this.uSymbol].isRollable = false;	
					// algo.SymbolsToRemove.Add(this.uSymbol);
				}

				LUD.SSQRMatrix.Clear();
				bestSSQRColumn = new SSQRColumn();
				
				if (LUD.doTracing) algo.Debug($" **************  END OTM OPTIONS ROLL WITH KILL ON BAD THRESH FOR  {LUD.uSymbol}  ****************");
				if (LUD.doTracing) algo.Log("-------");
				return isRolled;
			} // goodThresh on rolling OTM Options
			LUD.SSQRMatrix.Clear();
			bestSSQRColumn = new SSQRColumn();
						
			if (LUD.doTracing) algo.Debug($" **************  END OTM OPTIONS ROLL PROCESSING  FOR  {LUD.uSymbol}  ****************");
			if (LUD.doTracing) algo.Log("-");
			return isRolled;
		} catch (Exception errMsg)
		{
			//algo.Debug(" ERROR TradeLogging.CheckOTMRoll.cs " + errMsg );

			return false;													//// 2022-02-15:  replaced return with continue -- could have caused premature exits
		}	
		} /// END OTM OPTIONS ROLL

		//*************************************************************************************************
		//**************     CheckPutRoll          *******************************************************
		//*************************************************************************************************
		public bool CheckPutRoll (CollarAlgorithm algo , LookupData LUD, ref decimal stockPrice, ref decimal currCallBidPrice, ref decimal currPutAskPrice)
		{
		try{
			bool isRolled = false;
			bool annHighFade = false;
			// Determine if it should be rolled forward.
			if (LUD.doTracing) algo.Log($" **************  BEGIN ITM PUT CALC FOR  {LUD.uSymbol} ****************");
			
			Slice slD = algo.CurrentSlice;
			//LUD.loadVEData(algo);										/// load VE Data in Main.cs prior to calling tpr.CheckRolling
			SymbolData sd = LUD.intType==0 ? algo.symbolDataBySymbol[LUD.uSymbol] : algo.etfDataBySymbol[LUD.uSymbol];
			if (LUD.intType==0 ) sd.decOneYearPriceTarget_Current = LUD.decOneYearPriceTarget;		/// set current 1-year price target for stocks.   For ETFs use the price target set on the Last Day of Month						
								
			/*
			var oneYearBars = sd.Bars.ToList();
			var lowBar = oneYearBars.Min(b => b.Close);
			if (sd.decOneYearPriceTarget_Current < lowBar + 2m) annHighFade = true;				//// fading on acheiving 1-year low is not supported by evidence in short trades

			if (LUD.doTracing & annHighFade) algo.Debug($" **************    FADE ROLLING {LUD.uSymbol.Value} due to price {stockPrice.ToString()} being above annual low close {lowBar.ToString()} minus 2.");
		
			if(LUD.intType!=0) {
				LUD.decOneYearPriceTarget = (1m + sd.decMOMP/100m) * stockPrice;
				LUD.stockPrice = stockPrice;
				isRolled = algo.GetETFMatrix(ref LUD, false);
				if (!isRolled) return isRolled;
			}

			//SSQRColumn bestSSQRColumn = LUD.intType==0 ? algo.GetBestCollar(algo, ref LUD) : LUD.SSQRMatrix.FirstOrDefault();
			*/

			SSQRColumn bestSSQRColumn = new SSQRColumn();
			if (LUD.intType==0) {
				bestSSQRColumn = algo.GetBestCollar(algo, ref LUD);
			} else bestSSQRColumn = LUD.SSQRMatrix.FirstOrDefault();


			if (LUD.SSQRMatrix.Count == 0) {
				if (LUD.daysRemainingP <= 1) {
					if (LUD.doTracing) algo.Log($" **************  END ITM PUT FORCED ASSIGNMENT PROCESSING FOR  {LUD.uSymbol} -- NO POTENTIAL COLLARS ON LAST DAY *************");

					isRolled = algo.KillTheCollar(this, ref LUD, "KILL ITM PUT ASSIGNMENT -- NO POTENTIAL COLLARS ON LAST DAY", false, true);
					LUD.SSQRMatrix.Clear();
					bestSSQRColumn = new SSQRColumn();
					if (LUD.intType == 0){
						if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & isRolled){
								algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
								//algo.SymbolsToRemove.Add(this.uSymbol);
						}
					} else if (isRolled) {
						algo.etfDataBySymbol[this.uSymbol].isRollable = false;	
						// algo.SymbolsToRemove.Add(this.uSymbol);
					}
					if (LUD.doTracing) algo.Log($" ************** END CHECK EXPLICIT PUT ASSIGNMENT FOR  {LUD.uSymbol} OOOOOOOOO");
					if (LUD.doTracing) algo.Log("-----");
    			}

				if (LUD.doTracing) algo.Log($" **************  END ITM PUT CALC FOR  {LUD.uSymbol} -- NO POTCOLS ***");
				return isRolled;										// if no collars then return and loop around again
			}
	
			if (bestSSQRColumn == null || bestSSQRColumn.IsEmpty() ) {
				
				if (LUD.daysRemainingP <= 1) {											// if at the last day of put expiration and haven't yet rolled, kill the collar.
					if (LUD.doTracing) algo.Log($" *********  KILL 1st TPR ON LAST DAY OF ITM PUT PROCESSING FOR  {LUD.uSymbol} *************");

					isRolled = algo.KillTheCollar(this, ref LUD, "KILLED IN ITM PUT ASSIGNMENT -- EMPTY BEST COLLARS ON LAST DAY", false, true);
					if (LUD.intType == 0){
						if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & isRolled){
							algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
							//algo.SymbolsToRemove.Add(this.uSymbol);
						}
					} else if (isRolled) {
						algo.etfDataBySymbol[this.uSymbol].isRollable = false;	
						// algo.SymbolsToRemove.Add(this.uSymbol);
					}
					LUD.SSQRMatrix.Clear();
					bestSSQRColumn = new SSQRColumn();

					if (LUD.doTracing) algo.Log($" ************** END CHECK IMPLICIT PUT ASSIGNMENT FOR  {LUD.uSymbol} OOOOOOOOO");
					if (LUD.doTracing) algo.Log("--------");
					return isRolled;

    			} else {
					if (LUD.doTracing) algo.Log($" *********  END ITM PUT FORCED ASSIGNMENT PROCESSING FOR  {LUD.uSymbol} -- bestSSQR null or empty LOOPING TO TRY AGAIN");
					
					LUD.SSQRMatrix.Clear();
					bestSSQRColumn = new SSQRColumn();
					return isRolled;					// loop around and try again
    			}
			}	
					
			Symbol tradablePut = bestSSQRColumn.putSymbol;
			Symbol tradableCall = bestSSQRColumn.callSymbol;
	
			bool goodThresh = ((((LUD.divdndAmt * 3m) + LUD.decOneYearPriceTarget) < .95m * stockPrice) | algo.symbolDataBySymbol[this.uSymbol].continueTrade);			/// roll put before ITM exercise if indicators dictate continue the trade down

			
			if (goodThresh)				// roll the position forward
			{
				// check bestSSQRColumn to make sure we don't roll into a collar that will be subsequently exercised
				// this was fixed in v17+ by adding condition to .where() of LINQ to prevent such options from being returned
				if (currPutAskPrice!=0 && currPutAskPrice + bestSSQRColumn.putStrike > stockPrice) // make sure that no one can buy the option for less than the stock
		        {
			    	if (LUD.doTracing) algo.Log($"@@@@@@@@@@@@@@@@@@@  ITM PUT ROLL ABORT FOR  {LUD.uSymbol} -- IMMEDIATE CALL-EXERCISE PREVENTION FADE @@@@@@@@@@@@@@@@@@@@@@@");
	    			if (LUD.doTracing) algo.Log("@@@@@@@@@@@@@@@@@@@   CALL ASK: " + currPutAskPrice  + " Strike: " + bestSSQRColumn.putStrike + " Stock Price: " + stockPrice +" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 
	    			if (LUD.doTracing) algo.Log("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
					if (LUD.doTracing) algo.Log("------");
					if (LUD.daysRemainingP <= 1) {
						isRolled = algo.KillTheCollar(this,  ref LUD, "ABORT ITM PUT ROLL TO PREVENT SUBSEQUENT PUT ASSIGNMENT", false, true);
						if(LUD.intType==0){
							if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & isRolled){
								algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
								//algo.SymbolsToRemove.Add(this.uSymbol);
							}
						} else if (isRolled) {
							algo.etfDataBySymbol[this.uSymbol].isRollable = false;	
							// algo.SymbolsToRemove.Add(this.uSymbol);
						}
					}
					LUD.SSQRMatrix.Clear();
					bestSSQRColumn = new SSQRColumn();
					return isRolled;   
				}
	
				if (LUD.doTracing) algo.Log($" **************  BEGIN ITM PUT ROLL FOR  {LUD.uSymbol} ****************");
				//if (!newRollDate.Equals(oldRollDate)) {
				bool bRollable = LUD.intType==0 ? algo.symbolDataBySymbol[LUD.uSymbol].isRollable : algo.etfDataBySymbol[LUD.uSymbol].isRollable;
				bool isKilled = false;
				if (!annHighFade && bRollable && (this.currSellPnL > 0 | this.uQty * bestSSQRColumn.upsidePotential > Math.Abs(this.currSellPnL))) {								// Roll solely if we can sell the current collar profitably
				//if (currSellPnL > 0 ) {				// Roll solely if we can sell the current collar profitably
					if (LUD.doTracing) algo.Debug($" *** *** *** *** *** {LUD.uSymbol.Value} selling is more profitable or new collar can make more money than selling.");
					
					if (algo.RollTheCollar(LUD, this, ref bestSSQRColumn, "ROLLED -- ITM PUT NEAR EXPIRATION")) {
						isRolled = true;
						if (LUD.doTracing) algo.Log($" **************  ROLLED ITM PUTS COMPLETED FOR  {LUD.uSymbol} ****************");
						var orderedSSQRMatrix = LUD.SSQRMatrix.OrderBy(p => p.upsidePotential);							// 2021-03-21 -- changed from OrderedByDescending
						algo.IterateOrderedSSQRMatrix(orderedSSQRMatrix);
						// didTheTrade = false;
						LUD.SSQRMatrix.Clear();
						return isRolled;
					} else {
						if (LUD.daysRemainingP <= 1) {
							isKilled = algo.KillTheCollar(this,  ref LUD, "KILLED IN FAILED ITM PUT ROLL", false, true);
							if(LUD.intType==0){
								if( isRolled & algo.symbolDataBySymbol.ContainsKey(this.uSymbol)) {
									algo.symbolDataBySymbol[this.uSymbol].isRollable = false;
									//algo.SymbolsToRemove.Add(this.uSymbol);
								}
							} else if (isKilled) {
								algo.etfDataBySymbol[this.uSymbol].isRollable = false;	
								// algo.SymbolsToRemove.Add(this.uSymbol);
							}
							LUD.SSQRMatrix.Clear();
							return isKilled;
						}
					}
										
				} else {													// un profitable roll
					if (LUD.daysRemainingP <= 1) {
						//if (annHighFade & LUD.doTracing) algo.Debug($" **************    FADE ROLLING {LUD.uSymbol.Value} due to price {stockPrice.ToString()} being below annual low close {lowBar.ToString()} minus 2.");
						if (LUD.doTracing) algo.Log($" ************** UNPROFITABLE ITM PUT ROLL FOR  {LUD.uSymbol} ON LAST DAY -- ATTEMPT KILL");
						isKilled = algo.KillTheCollar(this, ref LUD, "KILL- LOSS IN 1st TPR IN ITM PUT ROLL", false, true);			
						if(LUD.intType==0){
							if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & isRolled){
								algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
								//algo.SymbolsToRemove.Add(this.uSymbol);
							}
						} else if (isKilled) {
							algo.etfDataBySymbol[this.uSymbol].isRollable = false;	
							// algo.SymbolsToRemove.Add(this.uSymbol);
						}
					}
					LUD.SSQRMatrix.Clear();
					bestSSQRColumn = new SSQRColumn();
					if (LUD.doTracing) algo.Log($" TT END CHECK IMPLICIT PUT ASSIGNMENT FOR  {LUD.uSymbol} OOOOOOOOO");
					if (LUD.doTracing) algo.Log("-----");
				}
				return isRolled;																				// exit OnData and try again until last day

			} else {							// bad threshhold on ITM PUT ROLL -- EXERCISE IT
				
				if (LUD.daysRemainingP <= 1) {
					if (LUD.doTracing) algo.Log($" ************** BAD SSQR THRESHOLD IN ITM PUT ROLL FOR  {LUD.uSymbol} ON LAST DAY -- ATTEMPT KILL");
					isRolled = algo.KillTheCollar(this, ref LUD, "KILL ON LAST DAY OF ITM PUT ", false, true);
					if (LUD.intType==0){
						if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & isRolled){
							algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
							//algo.SymbolsToRemove.Add(this.uSymbol);
						}
					} else if(isRolled){
						algo.etfDataBySymbol[this.uSymbol].isRollable = false;
						// algo.SymbolsToRemove.Add(this.uSymbol);
					}

				}
				LUD.SSQRMatrix.Clear();
				bestSSQRColumn = new SSQRColumn();

				if (LUD.doTracing) algo.Log($" **************  END ITM PUT CALC FOR  {LUD.uSymbol} ****************");
				if (LUD.doTracing) algo.Log("---------");
				return isRolled;					// roll around and try again
			}

	
		} catch (Exception errMsg)
		{
			algo.Debug(" ERROR in TradeLogging.CheckPutRoll.cs " + errMsg );

			return false;													//// 2022-02-15:  replaced return with continue -- could have caused premature exits
		}	
		}	// end CheckPutRoll

		//*************************************************************************************************
		//**************     CheckDividendRoll      *******************************************************
		//*************************************************************************************************
		private bool CheckDivRoll(CollarAlgorithm algo, ref decimal stockPrice, LookupData LUD)
		{
		int daysRemaining = LUD.daysRemainingDiv;
		bool annHighFade = false;
		if (LUD.doTracing) algo.Debug("//**************     CheckDividendRoll      ****************************" );

		try{	
			Slice slc = algo.CurrentSlice;
			bool isRolled = false;
			string strCorrSpndngPut = this.GetCorrspndngPut();
			Symbol symbCorrSpndngPut = strCorrSpndngPut;	
			decimal decCrrSpndgPutPrice = 0m;

			if (algo.Securities.TryGetValue(symbCorrSpndngPut, out var cpSecurity))
			{
				if (!cpSecurity.IsTradable)
				{
					if (LUD.doTracing) algo.Log(" **************  TPR CheckDividendRol found no tradable corresponding put " + symbCorrSpndngPut.Value + "  on " + slc.Time.ToString() );		
					if (LUD.doTracing) algo.Log(" **************  *************** Adding Put Contract" );						
					algo.AddOptionContract(symbCorrSpndngPut, Resolution.Minute, true, 0m, false);
					return false;				
				}
				//if (LUD.doTracing) algo.Log(" **************  TPR CheckDividendRol found tradable corresponding put " + symbCorrSpndngPut.Value + "  on " + slc.Time.ToString() + " @ " + string.Format("{0,5:C2}", decCrrSpndgPutPrice )   );		
				decCrrSpndgPutPrice = cpSecurity.AskPrice;
				//if (LUD.doTracing) algo.Log(" **************  TPR CheckDividendRol found tradable corresponding put " + symbCorrSpndngPut.Value + "  on " + slc.Time.ToString() + " @ " + decCrrSpndgPutPrice )   ;		
			} else {
				if (LUD.doTracing) algo.Log(" **************  TPR CheckDividendRol found no Put securities data for corresponding put " + symbCorrSpndngPut.Value + "  on " + slc.Time.ToString() );						
				if (LUD.doTracing) algo.Log(" **************  *************** Adding Put Contract" );						
				algo.AddOptionContract(symbCorrSpndngPut, Resolution.Minute, true, 0m, false);
				return false;
			}

			if (decCrrSpndgPutPrice < LUD.divdndAmt)
			{
			
				if (LUD.doTracing) algo.Log(" **************  BEGIN APPROACHMENT CALC FOR " + this.uSymbol + " priced @" );					///+ algo.Securities[this.uSymbol].Price );
				if (LUD.doTracing) algo.Log(" **************  EX-Date: " + LUD.exDivdnDate.ToString() );
				if (LUD.doTracing) algo.Log(" **************  DIVIDEND " + LUD.divdndAmt.ToString() + " Extrinsic Value: " + decCrrSpndgPutPrice.ToString() );

    			//bestSSQRColumn = GetBestSSQR(data, LUD.uSymbol, nextExDate);				// this is the normal route of non-delta execution

				SSQRColumn bestSSQRColumn = new SSQRColumn();
				LUD.loadVEData(algo);
				SymbolData sd = algo.symbolDataBySymbol[LUD.uSymbol];
				sd.decOneYearPriceTarget_Current = LUD.decOneYearPriceTarget;
			
				var oneYearBars = sd.Bars.ToList();
				var highBar = oneYearBars.Max(b => b.Close);
				if (LUD.decOneYearPriceTarget > highBar - 2m) annHighFade = true;
			
				bestSSQRColumn = algo.GetBestCollar(algo, ref LUD);
    		

    			if (bestSSQRColumn == null || bestSSQRColumn.IsEmpty())
    			{
    				if (daysRemaining <= 1) 															// Risk of Dividend Assignment too high at Ex-Dividend Date so if haven't been able to get at RollTheCollar, kill it here
    				{
    					if (LUD.doTracing) algo.Log(" OOOOOOOOOOOO NO bestSSQR ON LAST DAY OF DIVIDEND-FORCED EXERCISE -- KILL THE COLLAR  OOOOOOOOOO");
						isRolled = algo.KillTheCollar(this, ref LUD, "KILLED -- NO bestSSQR ON LAST DAY OF DIVIDEND-APPROACHMENT", false, true );
						LUD.SSQRMatrix.Clear();
						bestSSQRColumn = new SSQRColumn();
						if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & isRolled){
								algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
								//algo.SymbolsToRemove.Add(this.uSymbol);
						}
	
						if (LUD.doTracing) algo.Log("**************  END APPROACHMENT PROCESSING ******************");
						if (LUD.doTracing) algo.Log("-");

						return isRolled;					// Don't execute further processing in this slice if rolled due to dividend approachment
	
    				} else {
    				
    					if (LUD.doTracing) algo.Log("**************  END DIV APPROACHMENT PROCESSING -- NULL bestSSQR -- TRY AGAIN ******************");
						if (LUD.doTracing) algo.Log("-");	
						return isRolled;								// Exit CheckDivRoll if there's no SSQR Column to process   but don't move onto CallExpiryEvaluation for this reason
    				}
    			} 

				if (!bestSSQRColumn.IsEmpty() )
				{
					//TimeSpan expireDateDeltaSSQR = bestSSQRColumn.putExpiry.Subtract(slD.Time);
			
					//goodThresh = (bestSSQRColumn.CCOR >= CCORThresh);
					//bool goodThresh = (LUD.intVERating == 5 & LUD.decOneYearPriceTarget > 1.05m * stockPrice) | (LUD.intVERating > 3 & bestSSQRColumn.upsidePotential >=5);
					bool goodThresh = (((LUD.divdndAmt * 3m) + LUD.decOneYearPriceTarget) < .95m * stockPrice);

					if (goodThresh)				// roll the position forward
					{
						
						TimeSpan expireDateDeltaSSQR = bestSSQRColumn.putExpiry.Subtract(slc.Time);
												
						if ((bestSSQRColumn.callStrike < stockPrice) && expireDateDeltaSSQR.Days <= 10 ) // make sure that the collar won't be assigned because we're in the options danger zone
						    		    	
					    {											/////// THIS SHOULD NOT HAPPEN IN v17 AND BEYOND BECAUSE LINQ WAS AMENDED TO PREVENT THESE OPTIONS
				    		if (LUD.doTracing) algo.Log(" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  DIVIDEND EXERCISE ROLL ABORT -- CALL PREVENTION @@@@@@@@@@@@@@@@@@@@@@@");
				        	if (LUD.doTracing) algo.Log(" @@@@@@@@@@@@@@@@@@@  CALL ASK: " + slc[this.cSymbol].Price  + " Strike: " + bestSSQRColumn.callStrike + " Stock Price: " + slc[LUD.uSymbol].Price +" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 
			    	    	if (LUD.doTracing) algo.Log(" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@");
							if (LUD.doTracing) algo.Log("-");
			        		if (LUD.doTracing) algo.Log("**************  END APPROACHMENT PROCESSING ******************");
							if (LUD.doTracing) algo.Log("-");
							LUD.SSQRMatrix.Clear();
							bestSSQRColumn = new SSQRColumn();
			        		// DO NOT KILL THE COLLAR HERE.   
			        						
				       		return isRolled;							// Exit CheckDivRoll if there's no SSQR Column to process   but don't move onto CallExpiryEvaluation for this reason  
				       	} 
						
						if (LUD.doTracing) algo.Log(" **************  BEGIN DIV APPROACHMENT ROLL ****************");
						
						//iterate potetialCollars here solely when executing a trade
											
						
						if (!annHighFade && (this.currSellPnL > 0 | this.uQty * bestSSQRColumn.upsidePotential > Math.Abs(this.currSellPnL))) {								// Roll solely if we can sell the current collar profitably
							bool didTheTrade = algo.RollTheCollar(LUD, this, ref bestSSQRColumn, "ROLLED ON DIVIDEND APPROACHMENT");
									
							if (didTheTrade) {
								isRolled = true;
								//oldRollDate = slc.Time.Date;						// set the oldRollDate to Date of Roll
								if (LUD.doTracing) algo.Log(" **************  SUCCESSFUL DIV APPROACHMENT ROLL WITH SSQR: ");
								var orderedSSQRMatrix = LUD.SSQRMatrix.OrderByDescending(p => p.upsidePotential);
		    	    			algo.IterateOrderedSSQRMatrix(orderedSSQRMatrix);
		    	    			didTheTrade = false;
	    	    			} else if (daysRemaining <= 1) 															// Risk of Dividend Assignment too high at Ex-Dividend Date so if haven't been able to get at RollTheCollar, kill it here
							{
								isRolled = algo.KillTheCollar(this, ref LUD, "KILL- FAILED ROLL 1ST TPR IN DIVIDEND-FORCED EXERCISE ON LAST DAY", false, true );	// KillTheCollar may return to try again as well
								if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & isRolled){
										algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
										//algo.SymbolsToRemove.Add(this.uSymbol);
								}

							}

							return didTheTrade;

						} else if (annHighFade & LUD.doTracing) algo.Debug($" **************    FADE ROLLING {LUD.uSymbol.Value} due to price {stockPrice.ToString()} being above annual low close {highBar.ToString()} minus 2.");

						LUD.SSQRMatrix.Clear();
						bestSSQRColumn = new SSQRColumn();
						if (LUD.doTracing) algo.Log("**************  END APPROACHMENT PROCESSING ******************");
						if (LUD.doTracing) algo.Log("-");

						if (LUD.doTracing) algo.Log(" **************  END DIV APPROACHMENT ROLL ****************");
						if (LUD.doTracing) algo.Log("-");
						return isRolled; 				// get out of this Slice
				       									// prevent immediate call assignment
				        
					} else {							// NOT goodThresh --- kill the collar
						if (daysRemaining <= 1) 		// Risk of Dividend Assignment too high at Ex-Dividend Date so if haven't been able to get at RollTheCollar, kill it here
    					{
							if (LUD.doTracing) algo.Log(" OOOOOOOOOOOO BAD THRESH ON DIVIDEND-FORCED EXERCISE -- KILL THE COLLAR ON LAST DAY OOOOOOOOOO");
							isRolled = algo.KillTheCollar(this, ref LUD, "KILL- LAST DAY BAD THRESH ON DIVIDEND-FORCED EXERCISE", false, true );							// KillTheCollar may return to try again as well
							LUD.SSQRMatrix.Clear();
							bestSSQRColumn = new SSQRColumn();
							if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & isRolled){
									algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
									//algo.SymbolsToRemove.Add(this.uSymbol);
							}
		
							if (LUD.doTracing) algo.Log("**************  END APPROACHMENT PROCESSING ******************");
							if (LUD.doTracing) algo.Log("-");

							return isRolled;
    					} else {
    						if (LUD.doTracing) algo.Log(" OOOOOOOOOOOO BAD THRESH ON DIVIDEND-FORCED EXERCISE -- RETURN AND TRY AGAIN");
    						
    					}
						LUD.SSQRMatrix.Clear();
						bestSSQRColumn = new SSQRColumn();
						if (LUD.doTracing) algo.Log("**************  END APPROACHMENT PROCESSING ******************");
						if (LUD.doTracing) algo.Log("-");

						return isRolled;					// Don't execute further processing in this slice if rolled due to dividend approachment
					} // not goodThresh
					
					
				}  // !bestSSQRColumn /// there was no bestSSQRColumn	

			} /// *** decCrrSpndgPutPrice > LUD.divdndAmt
			
			return isRolled;
			
			} catch (Exception errMsg)
			{
				// algo.Debug(" ERROR TradeLogging.CheckDivRoll.cs " + errMsg );

				return false;													//// 2022-02-15:  replaced return with continue -- could have caused premature exits
			}	
		}	
		

		//*************************************************************************************************
		//**************     CheckCallRoll          *******************************************************
		//*************************************************************************************************
		public bool CheckCallRoll (CollarAlgorithm algo, LookupData LUD, ref decimal stockPrice, ref decimal currCallAskPrice, ref decimal currPutBidPrice)
		{
		try{
		// Determine if it should be rolled forward.
			bool isRolled = false;
			bool killed = false;
			bool annHighFade = false;

			Slice slD = algo.CurrentSlice;
			if (LUD.doTracing) algo.Log($" **************  BEGIN ITM CALL CALC FOR  {LUD.uSymbol} ****************");

			//LUD.loadVEData(algo);					////**** **** **** 2023-03-12 do this in Main.cs for stocks only.   For ETFs there is no VE data
			SymbolData sd = LUD.intType==0 ? algo.symbolDataBySymbol[LUD.uSymbol] : algo.etfDataBySymbol[LUD.uSymbol];
			if(LUD.intType==0) sd.decOneYearPriceTarget_Current = LUD.decOneYearPriceTarget;

 			var oneYearBars = sd.Bars.ToList();
			var highBar = oneYearBars.Max(b => b.Close);
			if (sd.decOneYearPriceTarget_Current > highBar - 2m) annHighFade = true;
			
	   		if(LUD.intType!=0) {
				LUD.decOneYearPriceTarget = (1m + sd.decMOMP/100m) * stockPrice;
				LUD.stockPrice = stockPrice;
				isRolled = algo.GetETFMatrix(ref LUD, false);
				if (!isRolled) return isRolled;
			}


			//bestSSQRColumn = GetBestSSQR(data, LUD.uSymbol, nextExDate);
    		//SSQRColumn bestSSQRColumn = LUD.intType==0 ? algo.GetBestCollar(algo, ref LUD) : LUD.SSQRMatrix.FirstOrDefault();
			SSQRColumn bestSSQRColumn = new SSQRColumn();
			if (LUD.intType==0) {
				bestSSQRColumn = algo.GetBestCollar(algo, ref LUD);
			} else bestSSQRColumn = LUD.SSQRMatrix.FirstOrDefault();


    		
			if (LUD.SSQRMatrix.Count == 0) {
    			if (LUD.daysRemainingC <= 1) {											// if at the last day of call expiration and haven't yet rolled, kill the collar.
					if (LUD.doTracing) algo.Log($" *********  END ITM CALL EVALUATION PROCESSING FOR  {LUD.uSymbol} -- NO POTENTIAL COLLARS ON LAST DAY *************");

					isRolled = killed = algo.KillTheCollar(this, ref LUD, "KILL IN ITM CALL EVALUATION -- NO POTENTIAL COLLARS ON LAST DAY", false, true);
					
					LUD.SSQRMatrix.Clear();
					bestSSQRColumn = new SSQRColumn();
					if (LUD.intType == 0){
						if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & isRolled){
								algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
								//algo.SymbolsToRemove.Add(this.uSymbol);
						}
					} else if (killed) {
						algo.etfDataBySymbol[this.uSymbol].isRollable = false;	
						// algo.SymbolsToRemove.Add(this.uSymbol);
					}

					if (LUD.doTracing) algo.Log($" OOOOOOOOO TT END CHECK EVALUATION  FOR  {LUD.uSymbol} OOOOOOOOO");
					if (LUD.doTracing) algo.Log("-----");

    			}

    			// algo.Log($" **************  END ITM CALL CALC PROCESSING FOR  {LUD.uSymbol} -- NO MATRICES ***");
    			return isRolled;										// if no collars then return and loop around again
    		}

    		if (bestSSQRColumn == null || bestSSQRColumn.IsEmpty() ) {
    			if (LUD.daysRemainingC <= 1) 
    			{											// if at the last day of call expiration and haven't yet rolled, kill the collar.
					if (LUD.doTracing) algo.Log($" *********  END ITM CALL FORCED ASSIGNMENT PROCESSING FOR  {LUD.uSymbol} -- bestSSQR null or empty ON LAST DAY");

						killed = algo.KillTheCollar(this, ref LUD, "KILL IN ITM CALL EVALUATION -- EMPTY BEST COLLAR ON LAST DAY", false, true);
						LUD.SSQRMatrix.Clear();
						if(LUD.intType==0){
							if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & isRolled){
									algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
									//algo.SymbolsToRemove.Add(this.uSymbol);
							}
						} else if (killed) {
							algo.etfDataBySymbol[this.uSymbol].isRollable = false;	
							// algo.SymbolsToRemove.Add(this.uSymbol);
						}
	
						bestSSQRColumn = new SSQRColumn();
						if (LUD.doTracing) algo.Log($" TT END CHECK CALL EVALUATION FOR  {LUD.uSymbol} OOOOOOOOO");
						if (LUD.doTracing) algo.Log("-");

    			} else 
    			{
					if (LUD.doTracing) algo.Log($" **************  END ITM CALL CALC FOR  {LUD.uSymbol} -- null bestSSQRColumn **** -- RETURN AND TRY AGAIN -- *********");
    				LUD.SSQRMatrix.Clear();
    				bestSSQRColumn = new SSQRColumn();
    			}
    			return isRolled;													// return and exit OnData()
    			
    		}		

			//bool goodThresh = (LUD.intVERating == 5 & LUD.decOneYearPriceTarget > 1.05m * stockPrice) | (LUD.intVERating > 3 & bestSSQRColumn.upsidePotential >=5);
			bool goodThresh = (((LUD.divdndAmt * 3m) + LUD.decOneYearPriceTarget) < .95m * stockPrice);

			
    		if (goodThresh)				// roll the position forward
			{
				if (LUD.doTracing) algo.Log($" **************  BEGIN ITM CALL ROLL FOR  {LUD.uSymbol} ****************");
				
				//decimal stockPrice = slD[bestSSQRColumn.uSymbol].Price;

																				// check to make sure we don't roll into a collar that will be exercised
																				// SHOULD NOT EXECUTE IN v17+ BECAUSE LINQ MODIDFIED TO PREVENT SUCH OPTIONS
				if (currPutBidPrice!=0 &&bestSSQRColumn.putStrike  - currPutBidPrice > stockPrice) // make sure that no one can buy the option for less than the stock
        		{
	    			if (LUD.doTracing) algo.Log($"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  ABORT ITM CALL ROLL TO PREVENT EXERCISE FOR  {LUD.uSymbol} @@@@@@@@@@@");
					if (LUD.doTracing) algo.Log("@@@@@@@@@@@@@@@@@@@  CALL ASK: " + slD[bestSSQRColumn.putSymbol].Price  + " Strike: " + bestSSQRColumn.putStrike + " Stock Price: " + stockPrice +" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 
					if (LUD.doTracing) algo.Log("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
					if (LUD.doTracing) algo.Log("-");
					
					if (LUD.daysRemainingC <= 1) 															// Risk of Dividend Assignment too high at Ex-Dividend Date so if haven't been able to get at RollTheCollar, kill it here
					{
						isRolled = killed = algo.KillTheCollar(this, ref LUD, "ABORT ITM CALL ROLL TO PREVENT EXERCISE ON LAST DAY", false, true );	// KillTheCollar may return to try again as well
						if(LUD.intType==0){
							if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & isRolled){
								algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
								//algo.SymbolsToRemove.Add(this.uSymbol);
							}
						} else if (killed) {
							algo.etfDataBySymbol[this.uSymbol].isRollable = false;	
							// algo.SymbolsToRemove.Add(this.uSymbol);
						}
					}

					LUD.SSQRMatrix.Clear();
					bestSSQRColumn = new SSQRColumn();
					if (LUD.doTracing) algo.Log($" TT END CHECK IMPLICIT CALL EXERCISE FOR  {LUD.uSymbol} OOOOOOOOO");
					if (LUD.doTracing) algo.Log($" ------------------------------------------------ ");
					return isRolled;   
				}

				bool bRollable = LUD.intType==0 ? algo.symbolDataBySymbol[LUD.uSymbol].isRollable : algo.etfDataBySymbol[LUD.uSymbol].isRollable;
				
				if (!annHighFade && bRollable && (this.currSellPnL > 0 | this.uQty * bestSSQRColumn.upsidePotential > Math.Abs(this.currSellPnL)))		// only roll the collar if the current record may be closed profitably-- otherwise seek exercise in kill
				//if (currSellPnL > 0 )
				{	/// Roll the Collar if the bestSSQRColumn won't be subsequently exercised.
					
					
					isRolled = algo.RollTheCollar(LUD, this,ref bestSSQRColumn, "ROLLED -- ITM CALL EXPIRATION APPROACHMENT");
					if (isRolled) {
						
						if (LUD.doTracing) algo.Log($" **************  ROLLED ITM CALLS COMPLETED FOR  {LUD.uSymbol}*************");
							var orderedSSQRMatrix = LUD.SSQRMatrix.OrderBy(p => p.upsidePotential);
								
							algo.IterateOrderedSSQRMatrix(orderedSSQRMatrix);
							//didTheTrade = false;
							LUD.SSQRMatrix.Clear();
							bestSSQRColumn = new SSQRColumn();
							return isRolled;
					}	else if (LUD.daysRemainingC <= 1) {
							if (LUD.doTracing) algo.Log($"  ************** UNSUCCESSFUL ROLL FOR  {LUD.uSymbol} -- KILL ITM PUT COLLAR ON LAST DAY  **************");
							killed = algo.KillTheCollar(this, ref LUD, "KILL- FAILED ROLL ON LAST DAY" , false, true);			// Goto KillTheCollar and determine whether to close or allow CALL EXERCISE there
							LUD.SSQRMatrix.Clear();
							bestSSQRColumn = new SSQRColumn();
							if(LUD.intType==0){
								if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & isRolled){
									algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
									//algo.SymbolsToRemove.Add(this.uSymbol);
								}
							} else if (killed) {
								algo.etfDataBySymbol[this.uSymbol].isRollable = false;	
								// algo.SymbolsToRemove.Add(this.uSymbol);
							}

							if (LUD.doTracing) algo.Log($" TT END CHECK ITM CALL EVALUATION FOR  {LUD.uSymbol} OOOOOOOOO");
							if (LUD.doTracing) algo.Log($" ------------------------------------------------ ");
							return isRolled;
					} else if (LUD.daysRemainingC <= 1) {
						if (LUD.doTracing) algo.Log($"  ************** UNPROFITABLE ROLL FOR  {LUD.uSymbol} -- KILL ITM PUT COLLAR ON LAST DAY  **************");
						killed = algo.KillTheCollar(this, ref LUD, "KILL- LOSS IN 1st PACKAGE AND UNPROFITABLE 2nd PACKAGE" , false, true);			// Goto KillTheCollar and determine whether to close or allow CALL EXERCISE there
						LUD.SSQRMatrix.Clear();
						bestSSQRColumn = new SSQRColumn();
						if(LUD.intType==0) {
							if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & isRolled){
									algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
									//algo.SymbolsToRemove.Add(this.uSymbol);
							}
						} else if (killed ){
							algo.etfDataBySymbol[this.uSymbol].isRollable = false;	
							// algo.SymbolsToRemove.Add(this.uSymbol);
						}
						if (LUD.doTracing) algo.Log($" TT END CHECK ITM CALL EVALUATION FOR  {LUD.uSymbol} OOOOOOOOO");
						if (LUD.doTracing) algo.Log($" ------------------------------------------------ ");
						return isRolled;
					}
				} else	                						// If not goodThresh, but expireDateDelta<=1, may get assigned on ITM calls
				{												// This programmatic flow allows days expireDateDelta -9 through -2 to be evaluated sequentially
																// because stock price fluctuations may trigger assignment in that date range.  But on the last day
																// and the call is ITM, assignment will probably happen. If goodThresh above, RollTheCollar was called
																//  Put options should expire without value but sell them and capture whatever value possible
					// exercise the calls here while puts may be sold for some value, even a penny.  <4¢ can be sold for 0 commission
					// capture the ending prices and close the TradePerformanceRecord by removing the old instance and inserting the updated copy
					
					if (LUD.doTracing) algo.Log($" **************  END ITM CALL EVALUATION PROCESSING FOR  {LUD.uSymbol}-- BAD THRESH ****************");
					if (LUD.daysRemainingC <= 1) {
						if (LUD.doTracing) algo.Log($" **************  KILL COLLAR IN ITM CALL EVALUTAION PROCESSING FOR  {LUD.uSymbol} -- BAD THRESH ON LAST DAY");
						algo.KillTheCollar(this, ref LUD, "KILL ITM CALL -- PREVENT PUT ASSIGNMENT", false, true);
						if(LUD.intType==0){
							if(algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & isRolled){
									algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
									//algo.SymbolsToRemove.Add(this.uSymbol);
							}
						} else if (isRolled) {
							algo.etfDataBySymbol[this.uSymbol].isRollable = false;	
							// algo.SymbolsToRemove.Add(this.uSymbol);
						}
					}

					LUD.SSQRMatrix.Clear();if (LUD.doTracing)  
					bestSSQRColumn = new SSQRColumn();
					if (LUD.doTracing) algo.Log($" TT END CHECK IMPLICIT PUT EXERCISE FOR  {LUD.uSymbol} OOOOOOOOO");
					if (LUD.doTracing) algo.Log($" ------------------------------------------------ ");
					return isRolled;										// exit OnData() and loop around again until last day.   May get assigned!
		
				//ExerciseOption(shortedCallSymbol, Decimal.ToInt32(this.cQty));   // LEAN error, cannot exercise short options

				}

			} // end CheckCallRoll function
			LUD.SSQRMatrix.Clear();
			bestSSQRColumn = new SSQRColumn();
			if (LUD.doTracing) algo.Log($" TT END CHECK IMPLICIT CALL EXERCISE FOR  {LUD.uSymbol} OOOOOOOOO");
			if (LUD.doTracing) algo.Log($" ------------------------------------------------ ");
			return false;	
			// endif ITM calls -10 -> Expiry.    Probably do an elseif {ITM puts here to definitively trap all assignments
		} catch (Exception errMsg)
		{
			algo.Debug(" ERROR TradeLogging.CheckCallRoll.cs " + errMsg );

			return false;													//// 2022-02-15:  replaced return with continue -- could have caused premature exits
		}	
		}	/// end CheckCallRoll
			
		//*************************************************************************************************
		//**************     CloseTPR          *******************************************************
		//*************************************************************************************************
		public void CloseTPR()
		{
			this.isOpen = false;				// effectively closes the tpr.   called in looping through tprsToClose after processing them.

		}

		//*************************************************************************************************
		//**************     CloseTPR          *******************************************************
		//*************************************************************************************************
		public void OpenTPR()
		{
			this.isOpen = true;					// effectively opens the tpr.  called in looping through tprsToOpen after processing them.
		}
		
		
		}  /// *****   END CLASS TradePerformanceRecord
	
		public class OpenLimitOrder 
		{
			public OrderTicket oTicket;				// order ticket
			public TradePerfRec tpr;				// Trade performance record for transaction
			public OptionRight oRight;				// Option Right (OptionRight.Call or OptionRight.Put)
			public bool isWingCall = false;			// is this oLO a Wing Call
			public int counter = 0;					// iterate this for processing

		}
	


	}
}
#region imports
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Globalization;
    using System.Drawing;
    using QuantConnect;
    using QuantConnect.Algorithm.Framework;
    using QuantConnect.Algorithm.Framework.Selection;
    using QuantConnect.Algorithm.Framework.Alphas;
    using QuantConnect.Algorithm.Framework.Portfolio;
    using QuantConnect.Algorithm.Framework.Execution;
    using QuantConnect.Algorithm.Framework.Risk;
    using QuantConnect.Parameters;
    using QuantConnect.Benchmarks;
    using QuantConnect.Brokerages;
    using QuantConnect.Util;
    using QuantConnect.Interfaces;
    using QuantConnect.Algorithm;
    using QuantConnect.Indicators;
    using QuantConnect.Data;
    using QuantConnect.Data.Consolidators;
    using QuantConnect.Data.Custom;
    using QuantConnect.DataSource;
    using QuantConnect.Data.Fundamental;
    using QuantConnect.Data.Market;
    using QuantConnect.Data.UniverseSelection;
    using QuantConnect.Notifications;
    using QuantConnect.Orders;
    using QuantConnect.Orders.Fees;
    using QuantConnect.Orders.Fills;
    using QuantConnect.Orders.Slippage;
    using QuantConnect.Scheduling;
    using QuantConnect.Securities;
    using QuantConnect.Securities.Equity;
    using QuantConnect.Securities.Future;
    using QuantConnect.Securities.Option;
    using QuantConnect.Securities.Forex;
    using QuantConnect.Securities.Crypto;
    using QuantConnect.Securities.Interfaces;
    using QuantConnect.Storage;
    using QuantConnect.Data.Custom.AlphaStreams;
    using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
    using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
#endregion


namespace QuantConnect.Algorithm.CSharp {
   	public partial class CollarAlgorithm : QCAlgorithm
	{
        public bool IsOrphaned(Symbol testSymb){
			Option testOpt;
			foreach(var kvp in Portfolio.Securities)
			{
				var security = kvp.Value;
				if (security.Invested && security.Type == SecurityType.Option )
				{
					Debug($" |||| |||| ||||  Testing: {security.Symbol} : {security.Holdings.Quantity} @ {security.BidPrice} by {security.AskPrice}");
					testOpt = (Option)security;
					if (testOpt.Right == OptionRight.Call & testOpt.Underlying.Symbol.Equals(testSymb)){
						Debug($" |||| |||| ||||  HOLDINGS: {testOpt.Right}: has underlying {testOpt.Underlying.Symbol.Value} not orphaned.");
						return false;
					}
				}
			}
			Debug($" |||| **** ||||| **** {testSymb.Value} is Orphaned.");
			return true;
		}
		
		public partial class TradePerfRec
        {
			public bool CheckRollingDown(CollarAlgorithm algo, LookupData LD){

			try{
			bool hasPut = false;
			bool hasCall = false;
			bool isCollar = false;

			Slice slc = algo.CurrentSlice;
			Symbol symbUndr = this.uSymbol;
			LD.uSymbol = this.uSymbol;

			int  putCount = 0;
			decimal strikeStep = 0;

			//if (symbUndr.Value == "CNP") {
			//	algo.Debug(" --- --- This is CNP Processing");
			//}
			string strTckr = symbUndr.Value;

			decimal stkPrc = 0m;
			decimal putPrc = 0m;
			decimal callPrc = 0m;

			int monthsRemaining = 0;
			if(LD.intType!=0 && !algo.symbolDataBySymbol.ContainsKey(this.uSymbol)) {
				if(LD.doTracing) algo.Log($"  **************  Symbol {this.uSymbol.Value} is no longer in SDBS!  *^*^*^*^ Check order flow *^*^*^");
				algo.tprsToClose.Add(this);
				return false;
			}
			if(LD.intType!=0 && !algo.symbolDataBySymbol[this.uSymbol].isRollable){
				if(LD.doTracing) algo.Log($"  **************  Symbol {this.uSymbol.Value} is not rollable Down -- check if this TPR is going to be closed.");
				return false;
			}
			if (LD.doTracing) algo.Log($" **************  TPR CheckRollingDown for  {symbUndr.Value} @ {slc.Time.ToString()}" );						

			//if (slc.ContainsKey(symbUndr) )
			if (algo.Securities.ContainsKey(symbUndr) )
			{ 
				// var tryPrice = slc[symbUndr].Price;   
				var tryPrice = algo.Securities[symbUndr].Price;
				if (tryPrice == null) 
				{
					if (LD.doTracing) algo.Log(" **************  TPR CheckRollingDown found no price data for  " + symbUndr.Value + "  @" + slc.Time.ToString() );						
					return false;
				}
				stkPrc = Convert.ToDecimal(tryPrice);
			} else if(LD.doTracing){
				algo.Log(" **************  TPR CheckRollingDown found no slice data for  " + symbUndr.Value + "  @" + slc.Time.ToString() );	
				return false;
			}

			if (this.cQty != 0){
				if (!slc.OptionChains.TryGetValue(this.cSymbol.Canonical, out var cOptChain))
				{
					if (LD.doTracing) algo.Log(" **************  TPR CheckRollingDown found no Call chains data for  " + this.cSymbol.Value + "  @" + slc.Time.ToString() );						
					return false;
				} else if (cOptChain.Contracts.TryGetValue(this.cSymbol, out var cContract)){
					callPrc = cContract.BidPrice;
				} else {
					if (LD.doTracing) algo.Log(" **************  TPR CheckRollingDown found no Call Contract data for  " + this.cSymbol.Value + "  @" + slc.Time.ToString() );						
					return false;
				}
				hasCall = true;

			}

			if (this.pQty != 0){
				if (!slc.OptionChains.TryGetValue(this.pSymbol.Canonical, out var pOptChain))
				{
					if (LD.doTracing) algo.Log(" **************  TPR CheckRollingDown found no PUT chains data for  " + this.pSymbol.Value + "  @" + slc.Time.ToString() );						
					return false;
				} else if (pOptChain.Contracts.TryGetValue(this.pSymbol, out var pContract)){
					putPrc = pContract.AskPrice;
				} else {
					if (LD.doTracing) algo.Log(" **************  TPR CheckRollingDown found no PUT Contract data for  " + this.pSymbol.Value + "  @" + slc.Time.ToString() );						
					return false;
				}
				if (LD.doTracing) algo.Log(" **************  TPR CheckRollingDown HAS A PUT Contract data for  " + this.pSymbol.Value + "  @" + slc.Time.ToString() );						
				hasPut = true;
			}

			var upChains = slc.OptionChains.get(algo.symbolDataBySymbol[this.uSymbol].optSymbol);
			if(upChains == null) {
				if(LD.doTracing) algo.Log($" **************  TPR CheckRollingDown failed to locate Contract data for {symbUndr.Value}  @ {slc.Time.ToString()}" );
				return false;
			}

			var atmCall = upChains.Where(o=>o.Right == OptionRight.Call)
								.OrderBy(o=> Math.Abs(o.Strike - stkPrc))
								.FirstOrDefault();
			
			var highCall = upChains.Where(o=>o.Right == OptionRight.Call)
									.OrderByDescending(o=>o.Strike)
									.FirstOrDefault();

			var lowCall = upChains.Where(o=>o.Right == OptionRight.Call)
									.OrderBy(o=>o.Strike)
									.FirstOrDefault();
			
			var strikesList = upChains.DistinctBy(o => o.Strike).ToList();

			if (strikesList.Count() == 1){
				strikeStep = 2.5m;
			} else { 
				strikeStep = (highCall.Strike - lowCall.Strike)/(strikesList.Count() - 1m);
			}
			if (strikeStep % 0.5m != 0) strikeStep = Math.Round(strikeStep/0.5m) * 0.5m;
			if (strikeStep == 1) strikeStep = 2;

			if(LD.doTracing) algo.Log($" ***tprtprtpr****  TPR strikeStep equals: {strikeStep.ToString()}" );
									
			if(algo.symbolDataBySymbol[this.uSymbol].initialTargetEndDate.Year > this.endDate.Year)
			{
				monthsRemaining = 	algo.symbolDataBySymbol[this.uSymbol].initialTargetEndDate.Month + 12 - this.endDate.Month;
			} else {
				monthsRemaining = 	algo.symbolDataBySymbol[this.uSymbol].initialTargetEndDate.Month - this.endDate.Month;
			}

			//LD.clearLD(algo);								/// //// Already cleared in Main.cs OnData()... Don't clear here, will reset intType
			LD.loadVEData(algo);
			decimal oneYrTrgtPrice_Current = algo.symbolDataBySymbol[this.uSymbol].decOneYearPriceTarget_Current = LD.decOneYearPriceTarget;

			decimal oneYrTrgtPrice_Initial = algo.symbolDataBySymbol[this.uSymbol].decOneYearPriceTarget_Initial;
			decimal trgtPrice = oneYrTrgtPrice_Initial < oneYrTrgtPrice_Current ? oneYrTrgtPrice_Initial : oneYrTrgtPrice_Current;

			if (trgtPrice == 0) trgtPrice = oneYrTrgtPrice_Initial;	/// //// Occasionally the VE file does not have a target price prediction
			
			/// //// //// //// SHOULD THIS CODE AND CHECK PRECEDE THE TRYGET OPTIONS CHAIN CODE /// /// /// ///// ??? ??? ??? 
			
			if (stkPrc < 1.03M * trgtPrice && !algo.symbolDataBySymbol[this.uSymbol].ContinueTrade(algo)){			// /// // THE STOCK HAS DEPRECIATED TO WITHIN 97% OF INITIAL OR CURRENT TARGET PRICE (BELOW 1.03*TARGET) AND ALMA-X AND TSI CROSSED UP
			
				bool forceKill = true;
				this.GetPnLs(algo, LD, ref stkPrc, ref callPrc, ref putPrc);
	
				if(LD.doTracing) algo.Log($" ************** ************** {this.uSymbol.Value} decreased more than 97% of {trgtPrice.ToString("0.00")} target and no continuing");
				
				bool didKill = algo.KillTheCollar(this, ref LD, "Attained 97% of current target price and no indication to continue.", forceKill, true);
				if(LD.intType==0 && algo.symbolDataBySymbol.ContainsKey(this.uSymbol) & didKill){
						algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
						algo.SymbolsToRemove.Add(this.uSymbol);
						if (LD.doTracing) algo.Log( " tprtprtprtprtprtprtpr ---  logging RollDown Attainment Kill  --- tprtprtrptrptrptpr");
						if (LD.doTracing) algo.Log( $" tprtprtprtprtprtprtpr --- ----  TPR.EndDate {this.expDate.ToString()} StockPrice: {stkPrc.ToFinancialFigures()} ");
				}
				return true;
			}
			OptionContract perkPut = null;

			if (monthsRemaining > 2) monthsRemaining = 2;

			if (LD.doTracing) algo.Log(" tprtprtprtprtprtprtpr ---  logging downChains --- tprtprtrptrptrptpr");
			if (LD.doTracing) algo.Log($" tprtprtprtprtprtprtpr --- ----  TPR.EndDate {this.expDate.ToString()} StockPrice: {stkPrc.ToFinancialFigures()} ");
			
			// foreach (var option in upChains){
				// algo.Log($" symbol: {option.Symbol.Value} Expiry:    {option.Expiry} Strike: {option.Strike}");
			// }
			
			var targetCall = upChains.Where(o=>o.Right == OptionRight.Call &
									o.Strike >= atmCall.Strike &
									o.Strike < this.cStrike - strikeStep &												/// //// 2024-01-7 :: make sure new strike is less than
									DateTime.Compare(o.Expiry, slc.Time.AddMonths(monthsRemaining))<=0 &
									o.AskPrice - callPrc <= 0.3m * (this.uStartPrice - stkPrc))
									.OrderByDescending(o=>o.Expiry)
									.ThenBy(o=>Math.Abs(atmCall.Strike - o.Strike))
									.FirstOrDefault();


			if (targetCall == null || targetCall.Strike >= this.cStrike){
				if (LD.doDeepTracing) algo.Log(" tprtprtprtprtprtprtpr --- ---- ---- Roll Down Target call not found 1X -- decrement to 1 month.");

				targetCall = upChains.Where(o=>o.Right == OptionRight.Call &
									o.Strike >= atmCall.Strike &
									o.Strike < this.cStrike &												/// //// 2024-01-7 :: make sure new strike is less than
									DateTime.Compare(o.Expiry, slc.Time.AddMonths(1))>=0 &
									o.AskPrice - callPrc <= 0.3m * (this.uStartPrice - stkPrc))
								.OrderByDescending(o=>o.Expiry)
								.ThenBy(o=>Math.Abs(atmCall.Strike - o.Strike))
								.FirstOrDefault();
	
					if (targetCall == null){
						if (LD.doDeepTracing) algo.Log(" tprtprtprtprtprtprtpr --- ---- ---- Roll Down Target Call not found 2X -- exit.");
						return false;
					}
			}
			
			if( this.cSymbol.Equals(targetCall) | (hasPut && (this.pStrike > stkPrc | DateTime.Compare(this.pSymbol.ID.Date, algo.symbolDataBySymbol[this.uSymbol].initialTargetEndDate) < 0))) {	// have an ITM put or a put that has a short expiry

				perkPut =	upChains.Where( o=> o.Right == OptionRight.Put &&
									DateTime.Compare(o.Expiry, algo.symbolDataBySymbol[this.uSymbol].initialTargetEndDate) <= 0 &							// get close, but don't exceed 1 year target.    Get as much call premium (theta) as possible
									o.Strike >= oneYrTrgtPrice_Current + strikeStep &
									o.Strike < targetCall.Strike)
									.OrderByDescending(o=>o.Expiry)
									.ThenBy(o=>Math.Abs(o.Strike-trgtPrice)).FirstOrDefault();
				isCollar = true;

			} else if (!hasPut) {	   
				if (stkPrc < 1.03m * trgtPrice & !hasPut)																									/// //// changed criteria from trgtPrice+4 to 1.03*
				{
					if(LD.doTracing) algo.Log($" ************** ************** {this.uSymbol.Value} is within 2 months of PUT expiration adn  attained greater than 105% of {trgtPrice.ToString("0.00")}.  Write a put, capture profit ");
					perkPut = 	upChains.Where(o=>o.Right == OptionRight.Put &
								o.Strike <= stkPrc & o.Strike >= .95M * trgtPrice & 
								o.Strike < targetCall.Strike &
								DateTime.Compare(o.Expiry, algo.symbolDataBySymbol[this.uSymbol].initialTargetEndDate)<0)
								.OrderByDescending(o=>o.Expiry)
								.ThenByDescending(o=> Math.Abs(o.Strike - trgtPrice))
								.FirstOrDefault();
					//if(perkPut != null) hasPut = true;
				}
			
			}

			SSQRColumn thisSSQRColumn = algo.buildSSQRColumn(perkPut, targetCall, algo, LD);
			if (thisSSQRColumn != null) 
			{
				if (thisSSQRColumn.callSymbol == null){
					algo.Log($" @@@@@ @@@@@ tprtprtprtpr -- null put in column {targetCall.Symbol.Value} for {this.uSymbol.Value}");
					return false;

				}
				LD.SSQRMatrix.Add(thisSSQRColumn);
			} else if(LD.doTracing) algo.Debug($"@@@@@ @@@@@ -- failed to get VE4 SSSQRColumn for {LD.uSymbol}.");


			if (LD.doDeepTracing) algo.Log($" tprtprtprtprtprtprtpr --- ---- Calling RollCallDown for {LD.uSymbol} because the current price {stkPrc.ToString()} is less than start: {this.uStartPrice.ToString()}. ");
			//algo.RollPutUp(targetPut, perkCall, ref LD, this, stkPrc);
			algo.didTheTrade = algo.RollCallDown(thisSSQRColumn, ref LD, this, stkPrc, isCollar);
			//var orderedSSQRMatrix = LD.SSQRMatrix.OrderBy(p => p.upsidePotential);
			var orderedSSQRMatrix = LD.SSQRMatrix.OrderBy(p => p.ROR);

			 algo.IterateOrderedSSQRMatrix(orderedSSQRMatrix);
			
			return algo.didTheTrade;

			} catch(Exception e) {
				algo.Debug($" CheckRollingDown Error {e}");
				return algo.didTheTrade;

			}

		}

			public bool HandleCollapse(CollarAlgorithm algo, LookupData LUD, SymbolData sd){
				bool hasPut = false;
				bool hasCall = false;
				bool isCollar = false;

				OrderTicket handlePutTicket;
				OrderTicket handleCallTicket;
				OrderTicket handleStockTicket;

				Slice slc = algo.CurrentSlice;
				Symbol symbUndr = this.uSymbol;
				LUD.uSymbol = this.uSymbol;

				string strTckr = symbUndr.Value;

				decimal stkPrc = 0m;
				decimal putPrc = 0m;
				decimal callPrc = 0m;

				if(LUD.doTracing) algo.Log($"  ****** Handling Symbol {this.uSymbol.Value} price rise. {algo.Securities[this.uSymbol].Price} is more than {this.cStrike.ToString("0.00")}");
				
				if(LUD.intType !=0 &&!algo.symbolDataBySymbol.ContainsKey(this.uSymbol)) {
					if(LUD.doTracing) algo.Log($"  **************  Symbol {this.uSymbol.Value} is no longer in SDBS!  *^*^*^*^ Check order flow *^*^*^");
					algo.tprsToClose.Add(this);
					return false;
				}
				stkPrc = LUD.stockPrice;

				if(this.pSymbol != null){
					var tryPPrice = algo.Securities[this.pSymbol].Price;
		
					if (tryPPrice == null) 
					{
						if (LUD.doTracing) algo.Log(" **************  TPR Handle Meltup found no put price data for  " + symbUndr.Value + "  @" + slc.Time.ToString() );						
						return false;
					}
					putPrc = Convert.ToDecimal(tryPPrice);
					hasPut = true;
				}

	
				if(this.cSymbol != null){
					var tryCPrice = algo.Securities[this.cSymbol].Price;
					if (tryCPrice == null) 
					{
						if (LUD.doTracing) algo.Log(" **************  TPR Handle Meltup found no call price data for  " + symbUndr.Value + "  @" + slc.Time.ToString() );						
						return false;
					}
					callPrc = Convert.ToDecimal(tryCPrice);
					hasCall = true;
				}
				LUD.dtTst = slc.Time;
			
				//LUD.GetNextExDate(algo);																////  get NextExDate for this symbol
				LUD.getNxtIExDt(this.cSymbol.Value, algo);
			
				this.GetPnLs(algo, LUD, ref stkPrc, ref callPrc, ref putPrc);
				
					string sSBB = sd.bbTrigger.ToString();
					string sVDI = sd.VDI_Signal.ToString();
					string sTSI = sd.tsi_trigger.ToString();
					string sALMA = sd.almas_crossing.ToString();
					string sTriggers = " SBB " + sSBB + " VDI " + sVDI + " TSI " + sTSI + " ALMA " + sALMA;

				if (hasCall){																			//// sell or exercise put
					if (this.currExrcsCallPnL > this.currSellPnL) {
						if (LUD.doDeepTracing) algo.Log($" HC ** HC ** HC ** EXERCISE {this.cQty.ToString()} CALL contracts of {this.cSymbol} at {this.cStrike.ToString()}.");
						handleCallTicket = algo.ExerciseOption(this.cSymbol, this.cQty);						/// underlying and calls will be closed in onOrder() event
						this.grossPnL = this.currExrcsCallPnL;													/// log the PnL used in runtime decision
						this.uEndPrice = this.cStrike;														/// 2023-04-15 :: forgot this in previous versions
						this.endDate = slc.Time;


						this.reasonForClose = $" Underlying Meltup EXERCISE CALL {this.uEndPrice.ToString()} above {this.cStrike.ToString()}."  + sTriggers;
						if (hasPut){
							if(algo.Securities[this.pSymbol].Price > 0.05m) {
								handlePutTicket = algo.MarketOrder(this.pSymbol, -this.pQty);
								if (handlePutTicket.Status == OrderStatus.Filled)
								{
									this.pEndPrice = handleCallTicket.AverageFillPrice;
								}
								if (LUD.doDeepTracing) algo.Log($" HC ** HC ** HC ** MARKET ORDER TO BUY {this.pQty.ToString()} contracts of {this.pSymbol.Value} at the market. Filled at {handleCallTicket.AverageFillPrice.ToString("0.00")}");
								if (LUD.doDeepTracing) algo.Log("-");

							}
						}
						
						return true;

					} else {
						handleCallTicket = algo.MarketOrder(this.cSymbol, -this.cQty);  					// buy the calls	
						if (LUD.doDeepTracing) algo.Log(" HC ** HC ** HC ** MARKET ORDER TO SELL " + this.pQty.ToString() + " contracts of " + this.cSymbol + " at the market.");
						if (LUD.doDeepTracing) algo.Log("-");

						if (handleCallTicket.Status == OrderStatus.Filled)
						{
							this.cEndPrice = handleCallTicket.AverageFillPrice;
							this.grossPnL = this.currSellPnL;													/// log the PnL used in runtime decision

						}
						if (LUD.doDeepTracing) algo.Log(" HC ** HC ** HC ** MARKET ORDER TO SELL " + this.uQty.ToString() + " shares of " + this.uSymbol + " at the market.");

						handleStockTicket = algo.MarketOrder(this.uSymbol, -this.uQty);
						if (handleStockTicket.Status == OrderStatus.Filled) {
							this.uEndPrice = handleStockTicket.AverageFillPrice;
							this.reasonForClose = $" Underlying MeltUp SELL CALLS {this.uEndPrice.ToString()} below {this.pStrike.ToString()}." + sTriggers;
							this.endDate = slc.Time;

						}
						if (hasPut){																			//// buy back short call
							handlePutTicket = algo.MarketOrder(this.pSymbol, -this.pQty);  // buy the calls	
							if (LUD.doDeepTracing) algo.Log(" HC ** HC ** HC ** MARKET ORDER TO BUY " + this.pQty.ToString() + " contracts of " + this.pSymbol + " at the market.");
							if (LUD.doDeepTracing) algo.Log("-");

							if (handlePutTicket.Status == OrderStatus.Filled)
							{
								this.pEndPrice = handlePutTicket.AverageFillPrice;
							}
						}
					}
				}
				if(LUD.intType !=0 && algo.symbolDataBySymbol.ContainsKey(this.uSymbol)){
					algo.symbolDataBySymbol[this.uSymbol].isRollable = false;	
					algo.symbolDataBySymbol[this.uSymbol].isTriggered = false;
					algo.SymbolsToRemove.Add(this.uSymbol);
				}

				algo.tprsToClose.Add(this);
				
				return true;
			}

		}
		public string ConvertTradePerfRec(List<TradePerfRec> tPR)
		{
			string tPRString = "";
			string jasonString = "";
			int counter = 0;
			
			jasonString = "{";
			tPRString = ",^^^";
			TradePerfRec tprLister = new TradePerfRec();
			var tprType = tprLister.GetType();
			var tprProperties = tprType.GetFields();

			foreach (var property in tprProperties)	
			{
				
				tPRString = tPRString + ", " + property.Name;
			}
			Debug(tPRString);
				
			var tPREnum = tPR.GetEnumerator();
			
			///////  NOTE:  Have to get the JASON formatted correctly.   Need one long string.  CHECK THIS
			while (tPREnum.MoveNext())
			{
				
				try {
					counter += 1;
					TradePerfRec thisPerfRec = tPREnum.Current;
					
					//Debug(String.Format("{0:000}: ", counter) + thisPerfRec.uSymbol.Value);
	
					jasonString = "{";
					tPRString = ",^^^";
					
					tPRString = tPRString + ", " + thisPerfRec.uSymbol.Value;
					tPRString = tPRString + ", " + thisPerfRec.index;
					tPRString = tPRString + ", " + thisPerfRec.isOpen;
					tPRString = tPRString + ", " + thisPerfRec.isInitializer;
					tPRString = tPRString + ", " + thisPerfRec.isSecondary;
					tPRString = tPRString + ", " + thisPerfRec.isTheta;
					tPRString = tPRString + ", " + thisPerfRec.tradeRecCount;
					tPRString = tPRString + ", " + String.Format("{0:MM/dd/yy H:mm:ss}", thisPerfRec.startDate);
					tPRString = tPRString + ", " + String.Format("{0:MM/dd/yy H:mm:ss}", thisPerfRec.endDate);
					tPRString = tPRString + ", " + thisPerfRec.strtngCndtn;
					tPRString = tPRString + ", " + thisPerfRec.reasonForClose;
					tPRString = tPRString + ", " + String.Format("{0:MM/dd/yy H:mm:ss}", thisPerfRec.expDate);
					tPRString = tPRString + ", " + "-no theta-";
					
					if (thisPerfRec.pSymbol != null){
						tPRString = tPRString + ", " + thisPerfRec.pSymbol.Value;
					} else {
						tPRString = tPRString + ", " + "-null-";
					}
					
					if (thisPerfRec.cSymbol != null) {
						tPRString = tPRString + ", " + thisPerfRec.cSymbol.Value;
					} else {
						tPRString = tPRString + ", " + "-null-";
					}   

						// tPRString = tPRString + ", " + thisPerfRec.cSymbol!=null ? thisPerfRec.cSymbol.Value : "-null-";
					tPRString = tPRString + ", " + "-null-";
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.pStrike);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.cStrike);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.wcStrike);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.pDelta);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.cDelta);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.wcDelta);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.pGamma);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.cGamma);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.wcGamma);
					tPRString = tPRString + ", " + thisPerfRec.uQty;
					tPRString = tPRString + ", " + thisPerfRec.pQty;
					tPRString = tPRString + ", " + thisPerfRec.cQty;
					tPRString = tPRString + ", " + thisPerfRec.wcQty;
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.uStartPrice);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.pStartPrice);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.cStartPrice);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.wcStartPrice);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.uEndPrice);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.pEndPrice);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.cEndPrice);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.wcEndPrice);
					tPRString = tPRString + ", " + thisPerfRec.numDividends;
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.divIncome);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.betaValue);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.RORThresh);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.ROCThresh);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.CCORThresh);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.tradeCriteria);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.ROR);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.ROC);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.CCOR);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.stockADX);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.stockADXR);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.stockOBV);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.stockAD);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.stockADOSC);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.stockSTO);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.stockVariance);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.currSellPnL);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.currExrcsPutPnL);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.currExrcsCallPnL);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.grossPnL);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.SSQRnetProfit);
					tPRString = tPRString + ", " + String.Format("{0:0 }", thisPerfRec.VERating);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.momentum);
					tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.oneYearPriceTarget);
					tPRString = tPRString + ", " + String.Format("{0:0 }", thisPerfRec.momentumRank);
					
					
					/*foreach (var field in typeof(TradePerfRec).GetFields())	
					{
						if (field is decimal) {
							//tPRString = tPRString + "," + String.Format("{0:0.00}", field.GetValue(thisPerfRec));
							tPRString = tPRString + "," + String.Format("{0:0.00}", field.GetValue(thisPerfRec));
						}
						else if (field is int) {
							tPRString = tPRString + "," + String.Format("{0}", field.GetValue(thisPerfRec));
						}
						else if (field is DateTime) {
							tPRString = tPRString + "," + String.Format("{0:MM/dd/yy H:mm:ss}", field.GetValue(thisPerfRec));
							
						}
						else if (field is bool) {
							tPRString = tPRString + ", " + field.GetValue(thisPerfRec);
							
						} else {
							//Console.WriteLine("{0} = {1}", field.Name, field.GetValue(thisPerfRec));
							tPRString = tPRString  + ", " + field.GetValue(thisPerfRec).ToString();
						}
						
						jasonString = jasonString + "\"" + field.Name + "\":\"" + field.GetValue(thisPerfRec) + "\"";
						
					} ^/
	
					/*foreach (var field in typeof(TradePerfRec).GetFields())	
					{
						if (field.GetType() == typeof(decimal)) {
							//tPRString = tPRString + "," + String.Format("{0:0.00}", field.GetValue(thisPerfRec));
							tPRString = tPRString + "," + String.Format("{0:0.00}", field);
						}
						else if (field.GetType() == typeof(DateTime)) {
							tPRString = tPRString + "," + String.Format("{0:MM/dd/yy H:mm:ss}", field.GetValue(thisPerfRec));
							
						}
						else if (field.GetType() == typeof(Symbol)) {
							
							tPRString = tPRString + ", " + field;
							
						} else {
							//Console.WriteLine("{0} = {1}", field.Name, field.GetValue(thisPerfRec));
							tPRString = tPRString  + ", " + field.GetValue(thisPerfRec);
						}
						
						jasonString = jasonString + "\"" + field.Name + "\":\"" + field.GetValue(thisPerfRec) + "\"";
						
					} */
					jasonString = jasonString + "}," + Environment.NewLine;
					
					Debug(tPRString);
				} catch (Exception errMsg)
	        	{
	        		Debug(" ERROR Converting TPR to JSON  " + errMsg);
	        		continue;
	        	}
			}
			
			return jasonString;
		}

    }
}
#region imports
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using QuantConnect.Data;
#endregion

namespace QuantConnect
{    
    class StockDataSource : BaseData
    {
        private const string LiveUrl = @"https://www.dropbox.com/s/rfvg9ce040she5y/ValuEngine%20Annual%20Universes.csv?dl=1";
        private const string BacktestUrl = @"https://www.dropbox.com/s/rfvg9ce040she5y/ValuEngine%20Annual%20Universes.csv?dl=1";

        /// <summary>
        /// The symbols to be selected
        /// </summary>
        public List<string> Symbols { get; set; }

        /// <summary>
        /// Required default constructor
        /// </summary>
        public StockDataSource()
        {
            // initialize our list to empty
            Symbols = new List<string>();
        }

        /// <summary>
        /// Return the URL string source of the file. This will be converted to a stream
        /// </summary>
        /// <param name="config">Configuration object</param>
        /// <param name="date">Date of this source file</param>
        /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
        /// <returns>String URL of source file.</returns>
        public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
        {
            //var url = isLiveMode ? LiveUrl : BacktestUrl;
            var url = BacktestUrl;
            return new SubscriptionDataSource(url, SubscriptionTransportMedium.RemoteFile);
        }

        /// <summary>
        /// Reader converts each line of the data source into BaseData objects. Each data type creates its own factory method, and returns a new instance of the object
        /// each time it is called. The returned object is assumed to be time stamped in the config.ExchangeTimeZone.
        /// </summary>
        /// <param name="config">Subscription data config setup object</param>
        /// <param name="line">Line of the source document</param>
        /// <param name="date">Date of the requested data</param>
        /// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
        /// <returns>Instance of the T:BaseData object generated by this line of the CSV</returns>
        public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
        {
            try
            {
                // create a new StockDataSource and set the symbol using config.Symbol
                var stocks = new StockDataSource {Symbol = config.Symbol};
                // break our line into csv pieces
                var csv = line.ToCsv();
                if (isLiveMode)
                {
                    // our live mode format does not have a date in the first column, so use date parameter
                    stocks.Time = date;
                    stocks.Symbols.AddRange(csv);
                }
                else
                {
                    // our backtest mode format has the first column as date, parse it
                    stocks.Time = DateTime.ParseExact(csv[0], "yyyyMMdd", null);
                    // any following comma separated values are symbols, save them off
                    stocks.Symbols.AddRange(csv.Skip(1));
                }
                return stocks;
            }
            // return null if we encounter any errors
            catch { return null; }
        }
    }
}
#region imports
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Globalization;
    using System.Drawing;
    using QuantConnect;
    using QuantConnect.Algorithm.Framework;
    using QuantConnect.Algorithm.Framework.Selection;
    using QuantConnect.Algorithm.Framework.Alphas;
    using QuantConnect.Algorithm.Framework.Portfolio;
    using QuantConnect.Algorithm.Framework.Execution;
    using QuantConnect.Algorithm.Framework.Risk;
    using QuantConnect.Parameters;
    using QuantConnect.Benchmarks;
    using QuantConnect.Brokerages;
    using QuantConnect.Util;
    using QuantConnect.Interfaces;
    using QuantConnect.Algorithm;
    using QuantConnect.Indicators;
    using QuantConnect.Data;
    using QuantConnect.Data.Consolidators;
    using QuantConnect.Data.Custom;
    using QuantConnect.DataSource;
    using QuantConnect.Data.Fundamental;
    using QuantConnect.Data.Market;
    using QuantConnect.Data.UniverseSelection;
    using QuantConnect.Notifications;
    using QuantConnect.Orders;
    using QuantConnect.Orders.Fees;
    using QuantConnect.Orders.Fills;
    using QuantConnect.Orders.Slippage;
    using QuantConnect.Scheduling;
    using QuantConnect.Securities;
    using QuantConnect.Securities.Equity;
    using QuantConnect.Securities.Future;
    using QuantConnect.Securities.Option;
    using QuantConnect.Securities.Forex;
    using QuantConnect.Securities.Crypto;
    using QuantConnect.Securities.Interfaces;
    using QuantConnect.Storage;
    using QuantConnect.Data.Custom.AlphaStreams;
    using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
    using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
#endregion


namespace QuantConnect.Algorithm.CSharp
{
    public partial class CollarAlgorithm : QCAlgorithm
    {
		public void ProcessOpenLimitOrders(Slice sld)
		{
			try {

				if (doDeepTracing) Debug(" |()|()|()        We have " + oLOs.Count+ " open limit order tickets");
				List<int> RemovableLOs = new List<int>();
				foreach (OpenLimitOrder olo in oLOs) {					
					int currentLO = oLOs.IndexOf(olo);
					var option = (Option)Securities[olo.oTicket.Symbol];
					string oloMsg = "";
					if (doTracing) Debug($" |()|()|() Found a {olo.oTicket.Status} limit order for {olo.oTicket.Symbol} with limit price and  {olo.oTicket.OrderEvents.ToString()}   ");
					if (doTracing) Debug($" |()|()|() Limit Price {olo.oTicket.Get(OrderField.LimitPrice)} Last Price {Securities[olo.oTicket.Symbol].Price} ");
					olo.counter += 1;							/// increment processing counter
					
					if(olo.oTicket.Status == OrderStatus.Canceled | olo.oTicket.Status == OrderStatus.Invalid ) {
						if (doTracing) Debug(" |()|()|()   Found a " + olo.oTicket.Status + " order and marking olo record index " + currentLO + " for removal.");
						RemovableLOs.Add(currentLO);
						continue;
					}

					TradePerfRec updateTPR = olo.tpr;
					
					if (olo.oTicket.Status == OrderStatus.Filled) {
						if (olo.oRight == OptionRight.Call) {
							if (olo.isWingCall) {
								if (doTracing) Debug(" |()|()|()   Found a filled wing call closing limit order and updated end price to " + olo.oTicket.AverageFillPrice);
								updateTPR.wcEndPrice = olo.oTicket.AverageFillPrice;
							} else { 
								if (olo.oTicket.Quantity < 0 ) {								// Sell order for closing Long Call -- rolling or closing a collar
									updateTPR.cEndPrice = olo.oTicket.AverageFillPrice;
									if (doTracing) Debug(" |()|()|()   Found a filled collar closing long call limit order and updated end price to " + olo.oTicket.AverageFillPrice);
								} else {
									updateTPR.cStartPrice = olo.oTicket.AverageFillPrice;	
									if (doTracing) Debug(" |()|()|()   Found a filled collar opening long call limit order and updated end price to " + olo.oTicket.AverageFillPrice);
								}
							}
							
						} else if (olo.oRight == OptionRight.Put) {								// never buy ITM Puts -- forces taxable "synthetic sale" of underlying
							//updateTPR.pEndPrice = olo.oTicket.AverageFillPrice;
							//if (doTracing) Debug(" |()|()|()   Found a filled collar ending long put limit order and updated end price to " + olo.oTicket.AverageFillPrice);
							if (olo.oTicket.Quantity < 0 ) {								// Sell order for Short Put -- initializing a collar
								updateTPR.pStartPrice = olo.oTicket.AverageFillPrice;
								if (doTracing) Debug(" |()|()|()   Found a filled collar initiating short put limit order and updated start price to " + olo.oTicket.AverageFillPrice);
							} else {
								updateTPR.pEndPrice = olo.oTicket.AverageFillPrice;	
								if (doTracing) Debug(" |()|()|()   Found a filled collar ending short put limit order and updated end price to " + olo.oTicket.AverageFillPrice);
							}
						}
						
						if (doTracing) Debug(" |()|()|()   marking olo at " + currentLO + " for removal ");
						RemovableLOs.Add(currentLO);
						continue;

						///// ///// ///// ----  Convert Limit Orders to Market Orders if they are more than 15 minutes old
					} else if (olo.oTicket.Status == OrderStatus.Submitted & ((int)sld.Time.Subtract(olo.oTicket.Time).TotalMinutes > 5) | olo.counter >= 5 ){			
						if (olo.oRight == OptionRight.Call) {
							//var option = (Option)Securities[olo.oTicket.Symbol];
							var orderUnderlyingPrice = option.Underlying.Price;
							var orderLimitPrice = olo.oTicket.Get(OrderField.LimitPrice);
							var orderStrikePrice = olo.oTicket.Symbol.ID.StrikePrice;
							var lPrice = orderUnderlyingPrice - orderStrikePrice + 0.10M;


							if (orderLimitPrice < orderUnderlyingPrice - orderStrikePrice + 0.10M) {				/// this is the criteria for placing a call sellback limit order.   This contition will exist if the underlying price has moved up
								olo.oTicket.Cancel();
								if (doTracing) Debug(" |()|()|()        With " + olo.oTicket.Symbol.ID.Underlying.Symbol + " trading at " + orderUnderlyingPrice + " and limit price set to " + orderLimitPrice);
								if (doTracing) Debug(" |()|()|()        updating " + olo.oTicket.Quantity + " of " + olo.oTicket.Symbol + " limit order to market order");
								if (doTracing) Debug(" |()|()|() OLO HANDLING");
								var callTkt = MarketOrder(olo.oTicket.Symbol, olo.oTicket.Quantity);
								if (callTkt.Status == OrderStatus.Filled ){
									if (olo.isWingCall) {
										updateTPR.wcEndPrice = callTkt.AverageFillPrice;
										if (doTracing) Debug(" |()|()|()   Converted and filled wing call limit to market order and updated end price to " + callTkt.AverageFillPrice);
									} else {
										if (olo.oTicket.Quantity < 0){					// this is a collar closing or rolling sell long call order
											updateTPR.cEndPrice = callTkt.AverageFillPrice;
											if (doTracing) Debug(" |()|()|()   Converted and filled long call SELL TO CLOSE limit to market order and updated END price to " + callTkt.AverageFillPrice);
										} else {
											updateTPR.cEndPrice = callTkt.AverageFillPrice;
											if (doTracing) Debug(" |()|()|()   Converted and filled short call BUY TO OPEN limit to market order and updated end price to " + callTkt.AverageFillPrice);
										}
									}
									if (doTracing) Debug(" |()|()|()   marking converted olo at " + currentLO + " for removal ");
									RemovableLOs.Add(currentLO);
								}
							} else {
								if (doTracing) Debug(" |()|()|()       With " + olo.oTicket.Symbol.ID.Underlying.Symbol + " trading at " + orderUnderlyingPrice + " and limit price set to " + orderLimitPrice);
								if (doTracing) Debug(" |()|()|()		waiting on " + olo.oTicket.Quantity + " of " + olo.oTicket.Symbol + "limit order fulfillment.");
							}
						
							
						} else if (olo.oRight == OptionRight.Put) {
							//var option = (Option)Securities[olo.oTicket.Symbol];
							var orderUnderlyingPrice = option.Underlying.Price;
							var orderLimitPrice = olo.oTicket.Get(OrderField.LimitPrice);
							var orderStrikePrice = olo.oTicket.Symbol.ID.StrikePrice;
							var lPrice = orderStrikePrice - orderUnderlyingPrice + 0.10M;
							if (orderLimitPrice > orderStrikePrice - orderUnderlyingPrice + 0.10M) {				/// this is the criteria for placing a call buyback limit order.   This contition will exist if the underlying price has moved up
								olo.oTicket.Cancel();
								if (doTracing) Debug(" |()|()|()       With " + olo.oTicket.Symbol.ID.Underlying.Symbol + " trading at " + orderUnderlyingPrice + " and limit price set to " + orderLimitPrice);
								if (doTracing) Debug(" |()|()|()       converting " + olo.oTicket.Quantity + " of " + olo.oTicket.Symbol + " limit order to market order");
								if (doTracing) Debug(" |()|()|() IN OLO PROCESSING");
								var sellPutTkt = MarketOrder(olo.oTicket.Symbol, olo.oTicket.Quantity);
								if (sellPutTkt.Status == OrderStatus.Filled ){
									updateTPR.pEndPrice = sellPutTkt.AverageFillPrice;
									if (doTracing) Debug(" |()|()|()   Converted and filled long put SELL TO OPEN limit to market order and updated end price to " + sellPutTkt.AverageFillPrice);
									if (doTracing) Debug(" |()|()|()   marking converted olo at " + currentLO + " for removal ");
									RemovableLOs.Add(currentLO);
								}
							} else {
								if (doTracing) Debug(" |()|()|()       With " + olo.oTicket.Symbol.ID.Underlying.Symbol + " trading at " + orderUnderlyingPrice + " and limit price set to " + orderLimitPrice);
								if (doTracing) Debug(" |()|()|()		waiting on " + olo.oTicket.Quantity + " of " + olo.oTicket.Symbol + "limit order fulfillment.");
							}
						}		// OptionRight == call or put
											// ticket = submitted			
					} else if (olo.oTicket.Status == OrderStatus.Submitted) {
						if (olo.oRight == OptionRight.Call) {
							//var option = (Option)Securities[olo.oTicket.Symbol];
							var orderUnderlyingPrice = option.Underlying.Price;
							var orderLimitPrice = olo.oTicket.Get(OrderField.LimitPrice);
							var orderStrikePrice = olo.oTicket.Symbol.ID.StrikePrice;
							// var lPrice = orderUnderlyingPrice - orderStrikePrice + 0.10M;
							var lPrice = option.BidPrice + ((option.AskPrice - option.BidPrice)/2);

							if (orderLimitPrice < orderUnderlyingPrice - orderStrikePrice + 0.50M) {				/// this is the criteria for placing a call buyback limit order.   This contition will exist if the underlying price has moved up by 50cents
								olo.oTicket.UpdateLimitPrice(lPrice);												/// update the limit price for the order to track the market.
								olo.counter = 0;																	/// reset the counter to try new limit price
								if (doTracing) Debug(" |()|()|()        With " + olo.oTicket.Symbol.ID.Underlying.Symbol + " trading at " + orderUnderlyingPrice + " and limit price set to " + orderLimitPrice);
								if (doTracing) Debug(" |()|()|()        updating " + olo.oTicket.Quantity + " of " + olo.oTicket.Symbol + " limit order to market order");
								if (doTracing) Debug(" |()|()|() IN MAIN INVESTING");
							}

						} else if (olo.oRight == OptionRight.Put) {
							//var option = (Option)Securities[olo.oTicket.Symbol];
							var orderUnderlyingPrice = option.Underlying.Price;
							var orderLimitPrice = olo.oTicket.Get(OrderField.LimitPrice);
							var orderStrikePrice = olo.oTicket.Symbol.ID.StrikePrice;
							// var lPrice = orderStrikePrice - orderUnderlyingPrice + 0.10M;
							var lPrice = option.BidPrice + ((option.AskPrice - option.BidPrice)/2);
							if (orderLimitPrice > orderStrikePrice - orderUnderlyingPrice + 0.50M) {
								olo.oTicket.UpdateLimitPrice(lPrice);
								olo.counter = 0;																	/// reset the counter to try new limit price
								if (doTracing) Debug(" |()|()|()        With " + olo.oTicket.Symbol.ID.Underlying.Symbol + " trading at " + orderUnderlyingPrice + " and limit price set to " + orderLimitPrice);
								if (doTracing) Debug(" |()|()|()        updating " + olo.oTicket.Quantity + " of " + olo.oTicket.Symbol + " limit order to market order");
								if (doTracing) Debug(" |()|()|()    IN MAIN INVESTING");
							}
						}
					}
				}				// for each OLO in oLOs
				
				if (haltProcessing) {
					Debug("			HALTED IN OLO PROCESSING");
				}
				
				if (RemovableLOs.Count > 0 ) {
					if (haltProcessing) {
						Debug(" ************   HALT IN REMOVABLE OLO PROCESSING ");
					}
					
					if (doTracing) Debug("  |||| |||| |||| ITERATING REMOVABLE LOs DIAGNOSTICALLY " );
					
					foreach (int r  in RemovableLOs ) {
						if (doTracing) Debug("       |||| |||| RemovableOLO @ index:" + r + " = " + oLOs[r].oTicket.Symbol);
					}
					
					for (int i = RemovableLOs.Count - 1; i > -1; i--)				// have to count down because deleting olo[0] resets next olo to 0.
					{
						int rOLO = RemovableLOs[i];
						if (doTracing) Debug("       |||| |||| Removing OLO @ index " + RemovableLOs[i] + " for " + oLOs[rOLO].oTicket.Symbol);
						oLOs.RemoveAt(rOLO);
					}
				}
			} catch (Exception errMsg)
			{
				Debug(" ERROR  " + errMsg );
				if (errMsg.Data.Count > 0) {
					Debug("  Extra details:");
					foreach (DictionaryEntry de in errMsg.Data)
						Debug("    Key: {0,-20}      Value: {1}'" + de.Key.ToString() + "'" + de.Value);
				}
			}				
		}




    }
}