Overall Statistics
Total Trades
4
Average Win
0%
Average Loss
0%
Compounding Annual Return
-0.042%
Drawdown
0.100%
Expectancy
0
Net Profit
-0.010%
Sharpe Ratio
-0.908
Probabilistic Sharpe Ratio
17.087%
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
-0.401
Tracking Error
0.184
Treynor Ratio
1.443
Total Fees
$23.00
Estimated Strategy Capacity
$2900000000.00
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
  	    	
  	    	
  	    	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
			thisFFRate = 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 highStrik = 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 = (thisFFRate + ibkrRateAdj)/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;
    	}
 	}
}
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


				/*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);
	        		}
	        	}	*/
	
	
}
namespace QuantConnect.Algorithm.CSharp
{
    public partial class CollarAlgorithm : QCAlgorithm
    {
		// **********************   DividendRecord		 **************************************
	    // ***  This structure contains the dividend information necessary to calculate trading
	    // ***	decision.   It is used to build a List<DividendRecord> that can be searched to 
	    // ***	produce the nextExDivDate and dividend amount

	    public struct DividendRecord {
	    	public string ticker;
	    	public DateTime exDate;
	    	public decimal divAmt;
	    	public string frequency;
		}
    
    
    	// **********************   GetTickers()		 **************************************
	    // ***  This function downloads the tickers.csv file from Dropbox and loads it into 
	    // ***	a Dictionary<int, string> for randomly selecting stocks to put on 
	    // ***	this dictionary is used when making a trading decision 
		public void GetTickers()
		{
							// https://www.dropbox.com/s/hkpr3luefv3u141/StockTickers.csv?dl=1		// 2020-09-26
							// https://www.dropbox.com/sh/05qjk3o3y53fp4i/AAA6fEJg8J50xMQWm5nlg7M4a?dl=1 // prior
			var tickerFile = Download("https://www.dropbox.com/s/hkpr3luefv3u141/StockTickers.csv?dl=1");
			if (tickerFile == null)
			{
				Debug(" TTTTTTTTTTTTTTTTTTTTTTT     EMPTY TICKER FILE TTTTTTTTTTTTTTTTTTTTTTTTTTTTTT");
				return;
			}
			
			string[]  tickerLines = tickerFile.Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries);
			int h = 0;
			foreach (string tickerLine in tickerLines)
			{
				if(h==0)	// discard header row
				{
					h++;
					continue;
				}
				var vals = tickerLine.Split(',');
				tickers.Add(h, vals[0]);   
				h++;
			}
			
			
			// these next 2 lines are for debugging only -- 
			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
		public Dictionary<DateTime, decimal> GetFedFundsRates()
		{
			var ffFile = Download("https://www.dropbox.com/s/s25jzi5ng47wv4k/DFF.csv?dl=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
	    public List<DividendRecord> GetDividendDates()
	    {
	    	// 2020-9-25 9:24  https://www.dropbox.com/s/ap8s120gksb858h/DividendData.csv?dl=1
	    	// 2020-09-25 8:11 https://www.dropbox.com/s/ap8s120gksb858h/DividendData.csv?dl=1
	    	//  2020-09-25 8:09 https://www.dropbox.com/sh/05qjk3o3y53fp4i/AAA6fEJg8J50xMQWm5nlg7M4a?dl=1  -- zip file
	    	
	    	// 2021-01-14 8:33 var csvFile = Download("https://www.dropbox.com/s/ap8s120gksb858h/DividendData.csv?dl=1");
	    	var csvFile = Download("https://www.dropbox.com/s/jv0aaajwsw8auwo/FiveYearDividends.csv?dl=1");
	    	
	    	if (csvFile == null) return null;
			
			decimal lastDiv = 0;
	
	    	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
	    		
	    		if (!values[3].Equals("annual") ) {
	    			DividendRecord divRec = new DividendRecord();
	    		
	    			divRec.ticker = values[0];
	    		
		    		if (values[1] == "") {
		    				divRec.divAmt = 0;
		    		} else {
			    		divRec.divAmt = Convert.ToDecimal(values[1]);
		    		}
		    		
		    		divRec.exDate = DateTime.Parse(values[2]);
		    		divRec.frequency = values[3];
		    		dividendDates.Add(divRec);
	    		}
	    	}
	    	i++;
	    	return dividendDates;
	    }
    }
    	
}
using QuantConnect.Securities.Option;
using System.Collections.Generic;
using System.Drawing;
using Newtonsoft.Json;

namespace QuantConnect.Algorithm.CSharp
{
    public partial class CollarAlgorithm : QCAlgorithm
    {
		// Initialize trade control variables used to intercept automated options exercise.

		public bool badDtParameter;	// get this from the parameters for debugging
		public bool haltProcessing = false;	// use this to trap ERROR
		public bool doTracing = true;		// turn Debug() process tracing on/off

		int callLoopCntr = 0;				// for debugging weird looping
		int logged = 0;						// Switch to toggle log entry writing
		bool doItOnce = true;				// Switch to toggle options exercise interdiction
		bool didTheTrade = false;			// Flag that permits InterateOrderedSSQRMatrix only if a trade was done
		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
			
		bool iteratePortfolio = false;		// Switch to toggle Iterating and Logging portfolio
		
		public int addedSecCount = 0;		// used to figure out how options data is added.
		OptionChain testChain;				// used to figure out when options chains are available after adding them
		OptionContract textContract;		// used to figure out when options greeks are available

		decimal stockDollarValue;			// get the dollar value for the position
		decimal sharesToBuy;				// 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 callsToTrade;				// Get the initial call options to trade in a variable call coverage strategy
		decimal maxPutOTM = 0.5M;			// Instantiate and set maximum depth of PUT OTM -- percentage
		int MinNmbrDivs = 1;				// Instantiate and set minimum number of dividends acceptable in BestSSQRMatrix
		decimal wingFactor = 0;				// wing factor to multiply optionsToTrade to trade the wings
	
		List<optGrksRec> GrksList;			// List of options greeks records acquired in SSQR building
		
		List<Symbol> jajoStocks;			// list of stocks paying dividends in January, April, July, and October
		List<Symbol> fmanStocks;			// list of stocks paying dividends in February, May, August, and November
		List<Symbol> mjsdStocks;			// list of stocks paying dividends in March, June, September, and December
		
		bool doTheTrade = false;			// Used to allow trades the algorithm initiates
		bool doingARoll = false;			// Used to flag if a roll is in progress when usingDeltas and rolling to next minute
		bool useDeltas = false;				// used to turn use of deltas in trade determination on or off
		DateTime oldRollDate;				// used to make sure we don't do an ITM-FORCED ROLL on the same
		DateTime newRollDate;				// date as a DIVIDEND roll.
		public Symbol tradablePut;			// for roll determination
		public Symbol tradableCall;			// for roll determination
		public Symbol tradableWCall;		// for execution and rolling wings
		
		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;
		
		List<DividendRecord> exDividendDates = new List<DividendRecord>();
		Dictionary<DateTime, decimal> fedFundsRates = new Dictionary<DateTime, decimal>();
		public decimal workingDays = 365M;
		public decimal thisFFRate = 0M;
		public decimal ibkrRateAdj = .006M;		// IBKR adds 60bps to FFR (blended over $3,000,000)
		Dictionary<decimal, decimal> ibkrHairCuts = new Dictionary<decimal, decimal>();

		List<TradePerfRec> tradeRecs = new List<TradePerfRec>();	// used to track P&L of trades
		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;
		List<TradePerfRec> secTPRs = new List<TradePerfRec>();
		List<TradePerfRec> thetaTPRs = new List<TradePerfRec>();
		int curr2ndTPR = 0;											// Used to store index
		int curr1stTPR = 0;											// used to store index of 1st TPR
		

		// initialize class variables for ex-dividend dates, Active
		Dictionary<int, string> tickers = new Dictionary<int, string> ();

		//List<OptionContract> ActiveOptions = new List<OptionContract>();
		//List<Option> callOptions = new List<Option>();
		
		Symbol thisSymbol;							// Initialize Symbol T as class variable
		Symbol shortedCallSymbol;					// primary tradeRec call symbol
		Symbol longPutSymbol;						// primary tradeRec put symbol
		Symbol wingCallSymbol;						// primary tradeRec wing call symbol
		Symbol secondLongPutSymbol;					// secondary tradeRec put symbol
		Symbol thetaCallSymbol;						// theta call symbol
		IEnumerable crntTPRs;				// primary trade recs
		TradePerfRec crntTPR;				// current primary trade rec
		TradePerfRec currentSecondRec;				// current secondary trade rec
		TradePerfRec currThetaRec;					// current theta trade rec
		
		TimeSpan expireDateDeltaC;					// use vars for checking days before expiration
		TimeSpan expireDateDeltaP; 
		TimeSpan expireDateDelta2P;
		TimeSpan expireDateDeltaTC;
		
		decimal incrPrice = 0;						// check for underlying price appreciation
		decimal curr1stTPRPPrice = 0;				// for calculating potential roll P&L
		decimal curr1stTPRCPrice = 0;				// for calculating potential roll P&L
		decimal curr1stTPRPAsk = 0;	
		decimal curr1stTPRCAsk = 0;
		decimal curr2ndTPRPBid = 0;					// for calculating PnL on before rolling
		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<SSQRColumn> potentialCollars = new List<SSQRColumn>();

		decimal stockDividendAmount = 0M;
		string divFrequency = "Quarterly";
		decimal divPlotValue = 0M;
		DateTime fmrNextExDate;
		
		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

		// Instatiate and set plotting information
		//Stochastic sto;								//  Stochastic	
        AccumulationDistribution ad;				// Accumulation / Distribution
        AccumulationDistributionOscillator adOsc;	// Accumulation / Distribution Oscillator
        AverageDirectionalIndex adx;				// Average Directional Index
        AverageDirectionalMovementIndexRating adxr;	// Average Directional Index Rating
        OnBalanceVolume obv;						// On Balance Volumne indicator
        Variance variance;							// Variance of this stock		
        
        //decimal lastSto;							// store values from night before
        decimal lastAd;
        decimal lastAdOsc;
        decimal lastAdx;
        decimal lastAdxr;
        decimal lastObv;
        decimal lastVariance;
      
		Chart stockPlot;							// initialize Series Variables to reference during order processing and endofday plotting
		Series buyOrders;
		Series sellOrders;
		Series rollOrders;
		Series ptsOrders;
		Series assetPrice;
		Series varianceS;
		//Series stochastics;
		Series dividendsS;
		
		public override void Initialize()
        {
    		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(10000000);           //Set Strategy Cash

			SetWarmup(TimeSpan.FromDays(31), Resolution.Daily);
            
            //ABBV	ADM	BA BBY	BMY	CVS	DOW	GIS	GM	IBM	IRM	KO	LVS	M OHI OXY PM PG	PSX	QCOM SO	T VZ WFC XOM 
    		// Get RunTime control parameters for Minimum # of Dividends and Maximum OTM Put Depth
			
			badDtParameter = GetParameter("CheckBadDate")  == "true" ? true : false; // get this from parameters      
			doTracing = GetParameter("LogTrace") == "true" ? true : false;				// get this from paramters to turn Debug() tracing on/off
			stockDollarValue = Convert.ToDecimal(GetParameter("StockDollarValue"));
			maxPutOTM = Convert.ToDecimal(GetParameter("MaxOTMPutDepth"));			// get and set the Maximum OTM Put Depth
			MinNmbrDivs = Convert.ToInt16(GetParameter("MinNmbrDivs"));			// get and set minimum number of dividends acceptable in BestSSQRMatrix
			useDeltas = GetParameter("UseDeltas")  == "true" ? true : false;	// get this from parameters
			wingFactor = Convert.ToDecimal(GetParameter("wingFactor"));			// get wing factor for multiplying optionsToTrade when putting on wings
			

            thisSymbol = AddEquity(GetParameter("stockTicker"), Resolution.Minute).Symbol;
            Securities[thisSymbol].SetDataNormalizationMode(DataNormalizationMode.Raw);  
            Securities[thisSymbol].VolatilityModel = new StandardDeviationOfReturnsVolatilityModel(31);
    		
			//sto = STO(thisSymbol, 14, Resolution.Daily);			//  Stochastic 
            ad = AD(thisSymbol, Resolution.Daily);					// Accumulation / Distribution
            adOsc = ADOSC(thisSymbol, 3, 14, Resolution.Daily);		// Accumulation / Distribution Oscillator
            adx = ADX(thisSymbol, 7, Resolution.Daily);					// Average Directional Index
            adxr = ADXR(thisSymbol, 7, Resolution.Daily);				// Average Directional Index Rating
            obv = OBV(thisSymbol, Resolution.Daily);					// On Balance Volume
            variance = VAR(thisSymbol, 14, Resolution.Daily);		// Variance of this stock
			
 
			// Chart - Master Container for the Chart:
			stockPlot = new Chart("Stock Chart");

			// On the Trade Plotter Chart we want 3 series: trades and price:
			buyOrders = new Series("Buys", SeriesType.Scatter, "$", Color.Green, ScatterMarkerSymbol.Triangle);
			rollOrders = new Series("Rolls", SeriesType.Scatter, "$", Color.Blue, ScatterMarkerSymbol.Square);
			ptsOrders = new Series("PTSs", SeriesType.Scatter, "$", Color.Crimson, ScatterMarkerSymbol.Square);
			sellOrders = new Series("Sells", SeriesType.Scatter, "$", Color.Red, ScatterMarkerSymbol.TriangleDown);
			dividendsS = new Series("Divs", SeriesType.Scatter, "$", Color.Pink, ScatterMarkerSymbol.Diamond);
			assetPrice = new Series("EOD Price", SeriesType.Line, "$", Color.Purple);
			varianceS = new Series("Variance", SeriesType.Line, "$", Color.Magenta);
			//stochastics = new Series("Stochastics", SeriesType.Line, "%", Color.DarkBlue);
			
			assetPrice.Index = 0;
			buyOrders.Index = 0;
			rollOrders.Index = 0;
			ptsOrders.Index = 0;
			sellOrders.Index = 0;
			dividendsS.Index = 0;
		
			//varianceS.Index = 1;
			
			//stochastics.Index = 1;
			//stochastics.Index = 2;
			
			stockPlot.AddSeries(buyOrders);
			stockPlot.AddSeries(rollOrders);
			stockPlot.AddSeries(ptsOrders);
			stockPlot.AddSeries(sellOrders);
			stockPlot.AddSeries(dividendsS);
			stockPlot.AddSeries(assetPrice);
			//stockPlot.AddSeries(varianceS);
			//stockPlot.AddSeries(stochastics);
			AddChart(stockPlot);
			
			GetTickers();
		
			exDividendDates = GetDividendDates();
			if  (exDividendDates == null)  if (doTracing) Debug("|||||||||||||||||| MISSING DIV DATES |||||||||||||||");
		
			fedFundsRates = GetFedFundsRates();
			if  (fedFundsRates == null)  if (doTracing) Debug("|||||||||||||||||| MISSING FED FUNDS |||||||||||||||");
			
			ibkrHairCuts = InitializeHaircuts();
			
			// per Rahul, set the Security Initializer to a custom proc
			// so the Options we add will be "warmed up" to provide prices
			SetSecurityInitializer(HistoricalSecurityInitializer);  
			
			
        }  // Initialize()

        /// OnData event is the primary entry point for the algorithm. Each new data point will be pumped in here.
        /// Slice object keyed by symbol containing the stock and options data
        
        public void OnData(TradeBars tbData)
        {
        	// Does this update the indicators?
        }
        
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        
        public void OnData(Dividends dData)
        {
	        try{
	        	if (Portfolio.Invested) 
	        	{
		    		// DIVIDENDS DIVIDENDS DIVIDENDS DIVIDENDS DIVIDENDS DIVIDENDS DIVIDENDS DIVIDENDS 
		    		// If there's a dividend in this Slice, log the payment amount in tradeRecs.
		    		if(dData.ContainsKey(thisSymbol))
				    {
		        	  int k = 0;												// counter for updates
		        	  var paymentAmount = dData[thisSymbol].Distribution;
		        	  if (doTracing) Debug(" DDDDDDDDDD DDDDDDDDDDD DIVIDENDS FOR " + thisSymbol + " ARE " + paymentAmount);
		        	  
		        	  if (tradeRecs.Any(tpr=> tpr!=null && tpr.isOpen && tpr.uSymbol.Equals(thisSymbol))) {
		        		foreach(var tprec in tradeRecs.Where(tpr=> tpr.isOpen && tpr.uSymbol.Equals(thisSymbol))) {
		        	  		tprec.numDividends = tprec.numDividends + 1;
		        			tprec.divIncome = tprec.divIncome + paymentAmount;
		        			k = k + 1;
		        		}
		        	  }
		        	  
		        	  if (doTracing) Debug(" DDDDDDDDDD DDDDDDDDDDD UPDATED " + k.ToString() + " TRADE PERF RECORDS. ");
		        	  if (doTracing) Debug("-");

		        	  Plot("Stock Chart", "Divs", divPlotValue);
			 		}
					// DIVIDENDS DIVIDENDS DIVIDENDS DIVIDENDS DIVIDENDS DIVIDENDS DIVIDENDS DIVIDENDS 
	        	}       	
	        } catch (Exception errMsg)
	        {
	        	if (doTracing) Debug(" DIV ERROR DIV ERROR DIV ERROR " + errMsg);
	        	return;
	        }
        }
        
        public override void OnData(Slice data)
        {
			if (!IsMarketOpen(thisSymbol))
				return;

			//if (!sto.IsReady | !variance.IsReady) return;

    	  	//check if bar data exists in current Slice before attempting to access
        	if(!data.Bars.ContainsKey(thisSymbol)) 
        		return;

			// only process every quarter hour (and the subsequent minute for useDeltas)
			//if (data.Time.Minute != 0  & data.Time.Minute != 1 & data.Time.Minute != 15 & data.Time.Minute != 16 & data.Time.Minute != 30 & data.Time.Minute != 31 & data.Time.Minute != 45 & data.Time.Minute !=46) return;			/// restrict processing to each quarter hour because options resolution is 1 minute
			if (data.Time.Minute != 0 & data.Time.Minute != 15 & data.Time.Minute != 30 & data.Time.Minute != 45 & data.Time.Minute !=46) return;			/// restrict processing to each quarter hour because options resolution is 1 minute
			goodThresh = false;									// set the threshold switch to false;
			hasPrimaryRec = hasSecondaryRec = false;			// reset processing branch flags


		  	if (CheckBadDate(data.Time))
		  	{ 
		  		////////////////   USE THIS DIAGNOSTICALLY  ///////////////////////////////
		  		haltProcessing = true;
		  		Debug(" @@@@@@   BAD DATE   @@@@@@@@@@ The price of " + thisSymbol + " is " + data[thisSymbol].Price);
		  		foreach(var kvp in Securities)
				{
					var security = kvp.Value;
					if (security.Invested)
					{
						Debug($" |-|-|-|-  HOLDINGS: {security.Symbol} : {security.Holdings.Quantity} @ {security.BidPrice} by {security.AskPrice}");
					}
				}
		  	} else haltProcessing = false;

        	if (haltProcessing) {
        		Debug("				HALTED IN ONDATA()");
        	}
        	
			if (data.Time.Minute == 0 | data.Time.Minute == 15 | data.Time.Minute == 30 | data.Time.Minute == 45) {
				try {

					if (oLOs != null && oLOs.Count > 0) {
						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);
							string oloMsg = "";

							if (doTracing) Debug(" |()|()|() Found a " + olo.oTicket.Status + " limit order.");
							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);
							}
							
							if (olo.oTicket.Status == OrderStatus.Filled) {
								TradePerfRec updateTPR = olo.tpr;
								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 Short Call -- initializing a collar
											updateTPR.cStartPrice = olo.oTicket.AverageFillPrice;
											if (doTracing) Debug(" |()|()|()   Found a filled collar initiating short call limit order and updated start price to " + olo.oTicket.AverageFillPrice);
										} else {
											updateTPR.cEndPrice = olo.oTicket.AverageFillPrice;	
											if (doTracing) Debug(" |()|()|()   Found a filled collar ending short call limit order and updated end price to " + olo.oTicket.AverageFillPrice);
										}
									}
									
								} else if (olo.oRight == OptionRight.Put) {
									updateTPR.pEndPrice = olo.oTicket.AverageFillPrice;
								}
								if (doTracing) Debug(" |()|()|()   marking olo at " + currentLO + " for removal ");
								RemovableLOs.Add(currentLO);

								// oLOs.RemoveAt(currentLO);
							} else if (olo.oTicket.Status == OrderStatus.Submitted & (int)data.Time.Subtract(olo.oTicket.Time).TotalMinutes > 15) {
								TradePerfRec updateTPR = olo.tpr;
								if (olo.oRight == OptionRight.Call) {
									var option = (Option)Securities[olo.oTicket.Symbol];
									var orderUnderlyingPrice = option.Underlying.Price;
									//var orderUnderlyingPrice = Securities[olo.oTicket.Symbol.ID.Underlying.Symbol].Price;    // this error out on {T} b/c Underlying.Symbol gives a "Ticker" per Alex
									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 buyback limit order.   This contition will exist if the underlying price has moved up
										//Debug(" |()|()|()        with " + olo.oTicket.Symbol.ID.Underlying.Symbol + " trading at " + orderUnderlyingPrice + ", updating " + olo.oTicket.Symbol + "limit order to new limit price: " + lPrice );
										//Ticket.Update(new UpdateOrderFields{LimitPrice = orderUnderlyingPrice - orderStrikePrice + 0.10M});
										olo.oTicket.Cancel();
										Debug(" |()|()|()        With " + olo.oTicket.Symbol.ID.Underlying.Symbol + " trading at " + orderUnderlyingPrice + " and limit price set to " + orderLimitPrice);
										Debug(" |()|()|()        , updating " + olo.oTicket.Quantity + " of " + olo.oTicket.Symbol + " limit order to market order");
										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 initiating sell short call order
													updateTPR.cStartPrice = callTkt.AverageFillPrice;
													if (doTracing) Debug(" |()|()|()   Converted and filled short call sell limit to market order and updated end price to " + callTkt.AverageFillPrice);
												} else {
													updateTPR.cEndPrice = callTkt.AverageFillPrice;
													if (doTracing) Debug(" |()|()|()   Converted and filled short call sell limit to market order and updated end price to " + callTkt.AverageFillPrice);
												}
											}
											// oLOs.RemoveAt(currentLO);
											if (doTracing) Debug(" |()|()|()   marking converted olo at " + currentLO + " for removal ");
											RemovableLOs.Add(currentLO);
										}
									} else {
										Debug(" |()|()|()        With " + olo.oTicket.Symbol.ID.Underlying.Symbol + " trading at " + orderUnderlyingPrice + " and limit price set to " + orderLimitPrice);
										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 orderUnderlyingPrice = Securities[olo.oTicket.Symbol.ID.Underlying.Symbol].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
										//Debug(" |()|()|()        with " + olo.oTicket.Symbol.ID.Underlying.Symbol + " trading at " + orderUnderlyingPrice + ", updating " + olo.oTicket.Symbol + "limit order to new limit price: " + lPrice );
										//Ticket.Update(new UpdateOrderFields{LimitPrice = orderUnderlyingPrice - orderStrikePrice + 0.10M});
										olo.oTicket.Cancel();
										Debug(" |()|()|()       With " + olo.oTicket.Symbol.ID.Underlying.Symbol + " trading at " + orderUnderlyingPrice + " and limit price set to " + orderLimitPrice);
										Debug(" |()|()|()       , updating " + olo.oTicket.Quantity + " of " + olo.oTicket.Symbol + " limit order to market order");
										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 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 {
										Debug(" |()|()|()        With " + olo.oTicket.Symbol.ID.Underlying.Symbol + " trading at " + orderUnderlyingPrice + " and limit price set to " + orderLimitPrice);
										Debug(" |()|()|()		, waiting on " + olo.oTicket.Quantity + " of " + olo.oTicket.Symbol + "limit order fulfillment.");
									}
								}		// OptionRight == call or put
							}			// ticket = submitted			
						}				// 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);
							}
						}
					}					// if oLOs.Count > 0
				} 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);
	        		}
	        	}				
			} /// if 0,15,30, 45

			//if (haltProcessing) 
			if (didTheTrade)
			{
				
				Debug($" |||| |||| ||||  DID A TRADE ");
				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;
						Debug($" ||||  HOLDINGS: {security.Symbol} : {security.Holdings.Quantity} @ {security.BidPrice} by {security.AskPrice}");
					}
				
				}
				//Debug("           HALTED IN ONDATA() ");
				didTheTrade = false;
			}

			if (newRollDate.GetHashCode() == 0) newRollDate = data.Time.Date;
    		if (oldRollDate.GetHashCode() == 0) oldRollDate = data.Time.Date;
    
        	// get the next ex-dividend date for initializing positions or managing them
        	string tickerString = thisSymbol.Value;
        	DateTime nextExDate = getNextExDate(tickerString, data.Time);
        	if (nextExDate.Equals(DateTime.MinValue)) {
        		if (doTracing) Debug("-----   NULL nextExDate -----   NULL nextExDate -----");
        		hasDividends = false;
        		return;
        	} else hasDividends = true;
			
			stockPrice = data[thisSymbol].Price;
			if (divPlotValue == 0) { divPlotValue = stockPrice - 5; }
			
			thisROC = 0;
			thisROR = 0;
			
			//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			//
			//								IF NOT INVESTED AT ALL OR IF LESS THAN
			//								QUARTER'S ALLOCATION, CREATE AND TEST
			//								POTENTIAL OPTIONS COLLARS AND IF
			//								GOOD, ESTABLISH A POSITION
			//
			//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        	heldValue = Portfolio[thisSymbol].HoldingsValue;				// get the 
        	if (heldValue < stockDollarValue) {
        		sharesToBuy = Math.Round((stockDollarValue-heldValue)/stockPrice/100, 0) * 100;
        		
        		if ( sharesToBuy >= 1000M ) {
					buyMoreShares = true;
        		} else buyMoreShares = false;
        	}

        	if (haltProcessing) {
        		Debug("     --- ");
        	} 
        	
        	hasPrimaryRec = tradeRecs.Any(t => t!=null && t.isOpen && !t.isSecondary && t.uSymbol.Equals(thisSymbol));
			hasSecondaryRec = false;
			secndRecCount = 0;				// reset the 2ndTPR processing loop counter
			//hasSecondaryRec = tradeRecs.Any(t => t!=null && t.isOpen && t.isSecondary && t.uSymbol.Equals(thisSymbol));
			hasThetaRec = tradeRecs.Any(t => t!=null && t.isOpen && t.isTheta && t.uSymbol.Equals(thisSymbol));
        	try {
	       		
	       		if (!hasPrimaryRec && buyMoreShares & hasDividends)
				{
		        	// get the underlying stock price in this Slice
	    	    	
		        	if (nextExDate.Month == data.Time.Month) 
		        	{
		        		/////////////////////////////			ONLY TEST EVERY 15 QUARTER HOUR AND EACH SUBSEQUENT MINUTE
		        		if (data.Time.Minute != 0  & data.Time.Minute != 1 & data.Time.Minute != 15 & data.Time.Minute != 16 & data.Time.Minute != 30 & data.Time.Minute != 31 & data.Time.Minute != 45 & data.Time.Minute !=46) return;
		        		
		        		if (doTracing) Debug($" -----   PROCESSING COLLAR INITIAZATION: {thisSymbol}");
		        		if (doTracing) Debug(" -----   ");
		        		if (useDeltas)																			// if Greeks.Delta will be used to calculate most optimal trade
		        		{
							if(data.Time.Minute == 0  | data.Time.Minute == 15  | data.Time.Minute == 30  | data.Time.Minute == 45)
							{
								potentialCollars = GetPotentialCollars(data, thisSymbol, nextExDate);			// get a list of potential collars and evaluate deltas in next minute
								return;																			// leave thisSlice and evaluate potentialCollars in the next minute when options greeks are available
							} else if ((potentialCollars.Count != 0) && (data.Time.Minute == 1  | data.Time.Minute == 16  | data.Time.Minute == 31  | data.Time.Minute == 46) )
							{
								bestSSQRColumn = GetBestSSQRFromPotentialCollars(data, thisSymbol, nextExDate, potentialCollars);
								potentialCollars.Clear();														// Clear the PotentialCollars list.   NOTE:  this will obviate subsequent "next minute" evaluations
								callLoopCntr = 0;
								if (bestSSQRColumn == null) return;												// if there's no bestSSQRColumn, then loop around and try again
							} else return;																		// on next minute in useDeltas, potentialCollars.Count == 0, return and try again


		        		} else {																				// not using deltas -- original execution path
		        			if(data.Time.Minute != 0  & data.Time.Minute != 15  & data.Time.Minute != 30  & data.Time.Minute != 45)	return;
		        			
							potentialCollars = GetPotentialCollars(data, thisSymbol, nextExDate);				// get a list of potential collars
							if (potentialCollars.Count == 0) {
								if (doTracing) Debug($"-----   NO COLLARS RETURNED IN PROCESSING INITIAZATION: {thisSymbol}");
								return;											// if there's no collars, return and process again.rollOrders
							}
							bestSSQRColumn = GetBestSSQRFromPotentialCollars(data, thisSymbol, nextExDate, potentialCollars);		// separate GetBestSSQRColum here to Log only the traded SSQRMatrices (reducing log size)
							
							if (bestSSQRColumn == null) {
								if (doTracing) Debug($"-----   NULL bestSSQR RETURNED IN PROCESSING INITIAZATION:  {thisSymbol}");
								return;													// if there's no bestSSQRColumn, then loop around and try again
							}
		        		}		

	        			if (bestSSQRColumn == null || bestSSQRColumn.IsEmpty())				// just in case somehow we got here with a null bestSSQRColumn 
		        		{
		                    potentialCollars.Clear();
							callLoopCntr = 0;

		                    if (doTracing) Debug($" **************  null OR EMPTY bestSSQR in Trade Initializing  {thisSymbol} *************");
		        			return;
	    	    		} else {
	    	    			
							if (haltProcessing) {
								Debug($" *******************    ITERATING UNORDERED POTENTIAL COLLARS -- DIAGNOSING IN HALT PROCESSING:  {thisSymbol}");
								IterateSSQRMatrix(potentialCollars);
							}
							
							if (!bestSSQRColumn.IsEmpty()) {
								Debug($" *******************    EXECUTING BESTSSQRCOLUMN --  {thisSymbol} --- ");
								ExecuteTrade(data, bestSSQRColumn);
		    	    			if (didTheTrade) {
									oldRollDate = data.Time.Date;
									
									var orderedSSQRMatrix = potentialCollars.OrderByDescending(p => p.CCOR);
									IterateOrderedSSQRMatrix(orderedSSQRMatrix);
		    	    				//didTheTrade = false;
		    	    			}
							}
	    	    			potentialCollars.Clear();
	    	    			bestSSQRColumn = new SSQRColumn();
	    	    			return;
	    	    		}

		        	}				// if data.Time.Month == nextExDividendDate.Month
				}					// if !Portfolio.Invested
				
				else 	// POSITION MANAGEMENT  --- Options Expiry and Ex-Dividend Approachment
				{
					if (!useDeltas && (data.Time.Minute != 0  & data.Time.Minute != 15  & data.Time.Minute != 30  & data.Time.Minute != 45))	return;		// don't process follow-on minutes
					
					if (hasPrimaryRec) {
						//if (doTracing) Debug(" CCCCCCCCCCCCCC   HAS CURRENT PRIMARY RECORD." );
						crntTPR = tradeRecs.Where(t => t.isOpen && !t.isSecondary && t.uSymbol.Equals(thisSymbol)).FirstOrDefault();
						if (data.Time.Subtract(crntTPR.startDate).Days == 0) return;				// don't evaluate the TPR for trade progress on the first day
						
						if (crntTPR.cSymbol == null) return;										// don't evaluate the TPR for trade progress until cSymbol is placed
						
						shortedCallSymbol = crntTPR.cSymbol;
						longPutSymbol = crntTPR.pSymbol;
						wingCallSymbol = crntTPR.wcSymbol;
						expireDateDeltaC = shortedCallSymbol.ID.Date.Subtract(data.Time);
						expireDateDeltaP = longPutSymbol.ID.Date.Subtract(data.Time);
						callStrike = shortedCallSymbol.ID.StrikePrice;
						putStrike = longPutSymbol.ID.StrikePrice;
						wcStrike = wingCallSymbol.ID.StrikePrice;
						curr1stTPRPPrice = Securities[longPutSymbol].BidPrice;
						curr1stTPRPAsk = Securities[longPutSymbol].AskPrice;
						curr1stTPRCPrice = Securities[shortedCallSymbol].AskPrice;
						//curr1stTPRCBid = Securities[shortedCallSymbol].BidPrice;
						currSellPnL = (crntTPR.uQty*(stockPrice-crntTPR.uStartPrice)) + (100*crntTPR.pQty*(curr1stTPRPPrice - crntTPR.pStartPrice)) + (-100*crntTPR.cQty*(crntTPR.cStartPrice - curr1stTPRCPrice)) + (100*crntTPR.wcQty*(crntTPR.wcEndPrice - crntTPR.wcStartPrice));
						currExrcsPutPnL = (crntTPR.uQty*(crntTPR.pStrike-crntTPR.uStartPrice)) + (100*crntTPR.pQty*(0 - crntTPR.pStartPrice)) + (-100*crntTPR.cQty*(crntTPR.cStartPrice - curr1stTPRCPrice))  + (100*crntTPR.wcQty*(crntTPR.wcEndPrice - crntTPR.wcStartPrice)); 
						currExrcsCallPnL = (crntTPR.uQty*(crntTPR.cStrike-crntTPR.uStartPrice)) + (100*crntTPR.pQty*(curr1stTPRPPrice - crntTPR.pStartPrice)) + (-100*crntTPR.cQty*(crntTPR.cStartPrice - 0))  + (100*crntTPR.wcQty*(crntTPR.wcEndPrice - crntTPR.wcStartPrice));
					}
					
					if (hasThetaRec){
						var currThetaRecs = tradeRecs.Where(t => t.isOpen && t.isTheta && t.uSymbol.Equals(thisSymbol));
						foreach (TradePerfRec tTPR in currThetaRecs) {
							thetaCallSymbol = tTPR.cSymbol;
							expireDateDeltaTC = thetaCallSymbol.ID.Date.Subtract(data.Time);
							if (expireDateDeltaTC.Days <= 10){
								var tCallPrice = (Securities[thetaCallSymbol].AskPrice - Securities[thetaCallSymbol].BidPrice)/2;
								if (tCallPrice + tTPR.cStrike  < stockPrice) {
									
								}
							}
						}
					}

					if (haltProcessing) {
						Debug("							HALTED IN INVESTED OnData()       ||");
					}
					
					if (hasPrimaryRec){
						newRollDate = data.Time.Date;										// 2021-02-06 -- added this newRollDate here to make sure it's set
						//if (!newRollDate.Equals(oldRollDate)) return;						// 2021-01-08 -- added this validation to prevent evaluations of conditions that would result in abrogation 
						int dayDelta = nextExDate.Date.Subtract(data.Time).Days;

						// DIVIDEND APPROACHMENT DIVIDEND APPROACHMENT DIVIDEND APPROACHMENT DIVIDEND APPROACHMENT DIVIDEND APPROACHMENT  
						if (dayDelta < 4 && dayDelta > 0)	// check every time slice for call assignment ??--ask John
						{
							// About differentiating calls shorted for Theta Harvesting, ask John if those are weekly options?? 
							// Should we, and if so, how do we differentiate those in the context of call assignment during 
							// ex-dividend approachment
							// --------------------------------------------------------------------------------------
							// // /// /// return true in CheckDividendRoll if corresponding put premium is less than dividend amount.  Otherwise, return false and continue processing
							if (CheckDividendRoll (data, nextExDate, crntTPR, shortedCallSymbol, longPutSymbol, stockPrice, dayDelta) ) {
								return;
							} 
						}
						// DIVIDEND APPROACHMENT DIVIDEND APPROACHMENT DIVIDEND APPROACHMENT DIVIDEND APPROACHMENT DIVIDEND APPROACHMENT  

						// WING CALL EVALUATION WING CALL EVALUATION WING CALL EVALUATION WING CALL EVALUATION WING CALL EVALUATION WING CALL EVALUATION WING CALL EVALUATION WING CALL EVALUATION 
						if (crntTPR.wcSymbol != null && crntTPR.wcQty != 0 && crntTPR.wcEndPrice == 0) {
							if ( (stockPrice - wcStrike)/stockPrice >= .05M && expireDateDeltaP.Days <= 5){
								
								if (doTracing) Debug(" ** ** ** SELLING ITM WING CALL  SELLING ITM WING CALL SELLING ITM WING CALL");
								var wcSellTkt = MarketOrder(crntTPR.wcSymbol, -crntTPR.wcQty);
								if (wcSellTkt.Status == OrderStatus.Filled) {
									crntTPR.wcEndPrice = wcSellTkt.AverageFillPrice;
								}
							}
						}
						// WING CALL EVALUATION WING CALL EVALUATION WING CALL EVALUATION WING CALL EVALUATION WING CALL EVALUATION WING CALL EVALUATION WING CALL EVALUATION WING CALL EVALUATION 
						
						
						// CALL EXERCISE CALL EXERCISE CALL EXERCISE CALL EXERCISE CALL EXERCISE CALL EXERCISE CALL EXERCISE CALL EXERCISE CALL EXERCISE CALL EXERCISE CALL EXERCISE  
						if (((stockPrice - callStrike)/stockPrice >= .05M && expireDateDeltaC.Days <= 10 && expireDateDeltaC.Days > 1) || ((stockPrice - callStrike) > 0 && expireDateDeltaC.Days <= 1))
						//} else if ((callStrike + curr1stTPRCPrice) < stockPrice && expireDateDeltaC.Days <= 10 ) 
						{
							if (CheckCallRoll(data, nextExDate, crntTPR, shortedCallSymbol, longPutSymbol, stockPrice, expireDateDeltaC.Days)) {
								return;
							}
						}	
						// CALL EXERCISE CALL EXERCISE CALL EXERCISE CALL EXERCISE CALL EXERCISE CALL EXERCISE CALL EXERCISE CALL EXERCISE CALL EXERCISE CALL EXERCISE CALL EXERCISE 
						
						// PUT EXERCISE PUT EXERCISE PUT EXERCISE PUT EXERCISE PUT EXERCISE PUT EXERCISE PUT EXERCISE PUT EXERCISE 
						else if (( (putStrike - stockPrice )/stockPrice >= .05M && expireDateDeltaP.Days <= 10 && expireDateDeltaP.Days > 1) || ( (putStrike > stockPrice) && expireDateDeltaP.Days <= 1) )
						//else if ( (putStrike + curr1stTPRPAsk) > stockPrice  && expireDateDeltaP.Days <= 10 ) 
						{
							if (CheckPutRoll(data, nextExDate, crntTPR, shortedCallSymbol, longPutSymbol, stockPrice, expireDateDeltaC.Days)) {
								return;
							}	
						} // PUT if 5% over stock price -- what should we do?
						
						// PUT EXERCISE PUT EXERCISE PUT EXERCISE PUT EXERCISE PUT EXERCISE PUT EXERCISE PUT EXERCISE PUT EXERCISE 
	
						// NORMAL EXPIRATION NORMAL EXPIRATION NORMAL EXPIRATION NORMAL EXPIRATION NORMAL EXPIRATION NORMAL EXPIRATION
						// PRESUMABLY ALL POTENTIAL ASSIGNMENTS ARE TRAPPED BY THIS POINT IN THE EXECUTION																// if both the put and call are OTM 
						else if ((expireDateDeltaC.Days <= 1 && stockPrice <= callStrike) | (expireDateDeltaP.Days <= 1 && stockPrice >= putStrike))					// this is the put expiration by design.  the puts always control the collar and the risk
						{
							if (CheckOTMRoll(data, nextExDate, crntTPR, shortedCallSymbol, longPutSymbol, stockPrice, expireDateDeltaC.Days, expireDateDeltaP.Days)) {
								return;
							}	
	        			} // normal expiration
					}	// hasPrimaryRecd
					
				} // PortfolioInvested -- trade management
        	
        	} 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);
        		}
 
        		return;
        	}
        	
        }	// OnData()
        
        
        /// Rahul said this is beta and possibly inactive
        public void OnAssignmentOrderEvent(OrderEvent assignmentEvent)
        {
			//if (doTracing) Debug("ASSIGNMENT ==== " + assignmentEvent.Symbol.Value);
			Debug("AAAAAAAAAAAAAAA   ASSIGNMENT ==== " + assignmentEvent.Symbol.Value);
        }
        
        
       // **********************   OnOrderEvent		***********************************************
	    // ***  	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 override void OnOrderEvent(OrderEvent orderEvent) {
        	var order = Transactions.GetOrderById(orderEvent.OrderId);
			var oeSymb = orderEvent.Symbol;
			
			if (haltProcessing) { 
				Debug("			HALTED IN ONORDER()");
			}
			
			Debug(" OO+++   " + order.Type + " order for " + oeSymb + ", Order Status: " + orderEvent.Status);
    
   		try {
   			if (orderEvent.Status == OrderStatus.Filled) 
    		{
	    		//var order = Transactions.GetOrderById(orderEvent.OrderId);
				//var oeSymb = orderEvent.Symbol;

				if (order.Type == OrderType.OptionExercise)
				{
					Debug(" 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 
    					Debug(" OO " + orderEvent.UtcTime + " LEAN LEAN LEAN    ASSIGNMENT ORDER EVENT    LEAN LEAN LEAN  OOOOOO");
    					Debug(" OO ASSIGNMENT SYMBOL: " + oeSymb );
    				
   						if (oeSymb.HasUnderlying && oeSymb.ID.OptionRight == OptionRight.Call) {
   							sellThePut = true;
   						}
   					}

	        		Debug(" OO Quantity: " + orderEvent.FillQuantity + ", price: " + orderEvent.FillPrice);
	
					if (oeSymb.HasUnderlying) {
						didTheTrade = true;
						var thisOption = (Option)Securities[oeSymb];
						var stkSymbol = thisOption.Underlying;
    					Debug(" OO OPTIONS ORDER FOR : " + oeSymb + " IS A " + (oeSymb.ID.OptionRight == OptionRight.Put ? "PUT. " : "CALL.") + "for underlying: " + stkSymbol);
    					//  Get the open tradePerfRecord (if any still exists)  ???  what is the order of exercise events ???
    					//  tradePerfRec Call termination handled in code prior to PUT EXERCISE   
    					//	Execute TradePerfRec Underlying termination in OnOrder() upon Stock Assignment

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

    						if (tradeRecs.Any(t => t!=null && t.pSymbol.Equals(oeSymb) & t.pQty == -order.Quantity)) {
    							var pTPR = tradeRecs.Where(t => t!=null && t.pSymbol.Equals(oeSymb) & t.pQty == -order.Quantity).FirstOrDefault();
    							pTPR.pEndPrice = orderEvent.FillPrice;
    							Debug(" OO UPDATED PUT END PRICE TO : " + orderEvent.FillPrice);
    							
    							if (pTPR.cSymbol != null) {
    								var shrtCall = (Option)Securities[pTPR.cSymbol];
									TimeSpan daysToCallExpiry = shrtCall.Expiry.Subtract(orderEvent.UtcTime);
    								/*if (daysToCallExpiry.Days > 10 ) {
										Debug(" 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 = pTPR.uSymbol;
										newThTPR.index = pTPR.index;
										newThTPR.isOpen = pTPR.isOpen;
										newThTPR.isInitializer = true;
										newThTPR.isSecondary = true;
										newThTPR.isTheta = true;
										newThTPR.startDate = pTPR.startDate;
										newThTPR.strtngCndtn = "SPINNING OFF THETA CALLS";
										newThTPR.expDate = shrtCall.Expiry;
										newThTPR.cSymbol = pTPR.cSymbol;
										newThTPR.cQty = pTPR.cQty;
										newThTPR.cStartPrice = pTPR.cStartPrice;
										newThTPR.tradeCriteria = pTPR.tradeCriteria;
										
										tradeRecs.Add(newThTPR);
										
										pTPR.cSymbol = null;			// eliminate the call from the existint TPR
										pTPR.cStartPrice = 0;
										pTPR.cQty = 0;
    								} else { */
    								
									Debug(" OO SELLING THE CALL IF IT EXISTS");
									var closeCTkt = MarketOrder(pTPR.cSymbol, -pTPR.cQty);
									if (closeCTkt.Status == OrderStatus.Filled) {
										pTPR.cEndPrice = closeCTkt.AverageFillPrice;
    								}
    								//}
    							}
    							Debug(" OO SELLING THE WING CALL IF IT EXISTS");
    							if (pTPR.wcSymbol != null) {
    								//var wingCall = (Option)Securities[pTPR.wcSymbol];
    								var closeWingTkt = MarketOrder(pTPR.wcSymbol, -pTPR.wcQty);
    								if (closeWingTkt.Status == OrderStatus.Filled) {
    									pTPR.wcEndPrice = closeWingTkt.AverageFillPrice;
    								}
    							}	
								
							} else {											// 1st TPR in PUT EXERCISE
								Debug(" oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo ");
    							Debug(" oo PUT OPTION ORDER FOR : " + oeSymb);
    							Debug(" oo NOT SURE HOW THIS WAS ACCESSED - NO 1st TPR FOUND ");
    							Debug(" oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo ");
	
								//string jsonString = ConvertTradePerfRec(tradeRecs);
								/* if (tradeRecs.Any(t => t!=null &&  t.cSymbol.Equals(oeSymb) & t.cQty == -order.Quantity)) {
    								var cTPR = tradeRecs.Where(t => t!=null && t.cSymbol.Equals(oeSymb) & t.cQty == -order.Quantity).FirstOrDefault();
    								cTPR.cEndPrice = orderEvent.FillPrice;
    								Debug(" OO UPDATED CALL END PRICE TO : " + orderEvent.FillPrice);
    								Debug(" OO SELLING THE PUT IF IT EXISTS");
    								if (cTPR.cSymbol != null) {
   										var closePTkt = MarketOrder(cTPR.pSymbol, -cTPR.pQty);
   										if (closePTkt.Status == OrderStatus.Filled) {
   											cTPR.pEndPrice = closePTkt.AverageFillPrice;
   										}
    								}
    							} */
							}
    					} else if (oeSymb.ID.OptionRight == OptionRight.Call){
    						Debug(" oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo ");
    						Debug(" oo CALL OPTION EXERCISE ORDER FOR : " + oeSymb);
    						Debug(" oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo ");
   
    						if (tradeRecs.Any(t => t!=null && t.cSymbol.Equals(oeSymb) & t.cQty == -order.Quantity)) {
    							Debug(" oo oo FOUND SHORT CALL 1ST TPR oo ");
    							var cTPR = tradeRecs.Where(t => t!=null && t.cSymbol.Equals(oeSymb) & t.cQty == -order.Quantity).FirstOrDefault();
    							cTPR.cEndPrice = orderEvent.FillPrice;
    							Debug(" oo oo UPDATED 1ST TPR SHORT CALL END PRICE TO : " + orderEvent.FillPrice);
    							if (cTPR.pSymbol != null) {
    								var longPut = (Option)Securities[cTPR.pSymbol];
   									Debug(" 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];
   									Debug(" oo oo oo SELLING THE WING CALL IF IT EXISTS OR HASN'T BEEN BOUGHT");
   									if (cTPR.wcEndPrice != 0) {
   										Debug(" 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 Debug(" oo oo oo oo THE WING CALL WAS ALREADY SOLD");
    							}												////  THE FOLLOWING WOULD EXECUTE IF ALGO EXERCISED THE WING CALL -- NOT CONTEMPLATED
    						} else if (tradeRecs.Any(t => t!=null && t.wcSymbol.Equals(oeSymb) & t.wcQty == order.Quantity)) {
    							var wcTPR = tradeRecs.Where(t => t!=null && t.wcSymbol.Equals(oeSymb) & t.wcQty == order.Quantity).FirstOrDefault();
    							Debug(" oo FOUND SHORT CALL 1ST TPR oo ");
    							Debug(" oo UPDATED WING CALL END PRICE TO : " + orderEvent.FillPrice);
    							wcTPR.wcEndPrice = orderEvent.FillPrice;
    							
    						}
    					}
    					

					} else {				/// !.HasUnderlying -- this is stock being assigned
						Debug(" oo ASSIGNMENT OF UNDERLYING ORDER FOR : " + oeSymb);
						Debug(" oo STOCK EXERCISE ORDER EVENT FOR: " + order.Quantity + " shares." );
						
						if (haltProcessing) {
							Debug(" oo oo oo oo => HALTED IN OnOrder()  ");
						}
						didTheTrade = true;
						if(tradeRecs.Any(t => t!=null &&  t.isOpen & t.uSymbol.Equals(oeSymb) & t.uQty == -order.Quantity * 100M)) {
							Debug(" oo UPDATING 2ND TRADE RECORD");
							var uTPR = tradeRecs.Where(t => t!=null && t.isOpen & t.uSymbol.Equals(oeSymb) & t.uQty == -order.Quantity * 100M).FirstOrDefault();
							
							Plot("Stock Chart", "Sells", orderEvent.FillPrice);
							tradeRecCount = 0;									// reset tradeRec Counter ??? may be obviated
							uTPR.isOpen = false;
							uTPR.uEndPrice = orderEvent.FillPrice;
							uTPR.endDate = orderEvent.UtcTime;
							if (uTPR.reasonForClose !=null || uTPR.reasonForClose != "") {
								uTPR.reasonForClose = uTPR.reasonForClose +  "OPTIONS ASSIGNMENT -- UNDERLYING CLOSED";
							} else uTPR.reasonForClose = "OPTIONS ASSIGNMENT -- UNDERLYING CLOSED";
							
							if (Portfolio[uTPR.pSymbol].Invested && uTPR.pSymbol != null) {
								var sellPutTicket = MarketOrder(uTPR.pSymbol, -uTPR.pQty);
								if (doTracing) Debug(" oo oo oo oo ooo Selling the PUT and setting the TPR.EndPrice");
								if (sellPutTicket.Status == OrderStatus.Filled) {
									uTPR.pEndPrice = sellPutTicket.AverageFillPrice;
								}
								
							}
							if (Portfolio[uTPR.wcSymbol].Invested && uTPR.wcSymbol != null) {
								var sellWCallTicket = MarketOrder(uTPR.wcSymbol, -uTPR.wcQty);
								if (doTracing) Debug("  oo oo oo oo ooo Selling the Wing Call and setting the TPR.wcEndPrice");
								if (sellWCallTicket.Status == OrderStatus.Filled) {
									uTPR.wcEndPrice = sellWCallTicket.AverageFillPrice;
								}
								
							}
							/// NOTE:	OPTIONS WILL EXPIRE OR EXERCISE AT ENDPRICE = 0.  THEREFORE THESE VALUES ARE NOT SET HERE
							/// 		BECAUSE THE END PRICES MAY BE SET OTHERWISE ELSEWHERE
							
						} else {        				
  							Debug(" oo oo oo oo => FAILED TO LOCATE 2ND TPR");
						}
					}
    				Debug(" ---------------------------------------------------------------------------");
					
				} // Order.Type = OrderType.OptionExercise  
				else 
				{
					Debug(" OO ** ** ** ** ** ** ** ** ** NON EXERCISE OPTION ORDER -- " + oeSymb);
					Debug(" OO ** " + order.Type + ": " + orderEvent.UtcTime + ": "  + orderEvent.Direction + "  ** OO ");	
					Debug(" OO ** " + orderEvent.Status + ": " + orderEvent.Direction + " " + order.Quantity + " @ " + orderEvent.FillPrice );	
					
					if (oeSymb.HasUnderlying && order.Type == OrderType.Limit ) {							/// Option
						if (oeSymb.ID.OptionRight == OptionRight.Put)
						{
							Debug(" OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO ");
    						Debug(" OO PUT OPTION LIMIT ORDER FOR : " + oeSymb);
    						Debug(" OO PROCESSING TPR IN NEXT ON DATA   oo oo oo oo ");
    						Debug(" OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO ");

							/*if (tradeRecs.Any(t => t.pSymbol.Equals(oeSymb) & t.pQty == -order.Quantity)) { 
								var transRec = tradeRecs.Where(t => t.pSymbol.Equals(oeSymb) & t.pQty == -order.Quantity).FirstOrDefault();
								transRec.pEndPrice = orderEvent.FillPrice;
								Debug(" OO ** Setting pEndPrice.");
							} */
							
							//Debug(" OO NOTE     PUT EXPIRATION execute a market order to sell underlying");
							
						} else if (oeSymb.ID.OptionRight == OptionRight.Call) {
							Debug(" OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO ");
    						Debug(" OO CALL OPTION LIMIT ORDER FOR : " + oeSymb);
    						Debug(" OO PROCESSING TPR IN NEXT ON DATA   oo oo oo oo ");
    						Debug(" OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO ");
							/*if (tradeRecs.Any(t => t.cSymbol.Equals(oeSymb) & t.cQty == -order.Quantity)) { 
								var transRec = tradeRecs.Where(t => t.cSymbol.Equals(oeSymb) & t.cQty == -order.Quantity).FirstOrDefault();
								transRec.cEndPrice = orderEvent.FillPrice;
								Debug(" OO ** Setting cEndPrice.");
							}*/
							
							//Debug(" 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)
						{
							Debug(" OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO ");
    						Debug(" OO PUT OPTION MARKET ORDER FOR : " + oeSymb);
    						Debug(" OO PROCESSING TPR SYNCHRONOUSLY IN LINE oo oo oo ");
    						Debug(" OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO ");
							/*if (tradeRecs.Any(t => t.pSymbol.Equals(oeSymb) & t.pQty == -order.Quantity)) { 
								var transRec = tradeRecs.Where(t => t.pSymbol.Equals(oeSymb) & t.pQty == -order.Quantity).FirstOrDefault();
								transRec.pEndPrice = orderEvent.FillPrice;
								Debug(" OO ** Setting pEndPrice.");
							} */
							
							//Debug(" OO NOTE     ALGO-DRIVEN PUT market order");
							
						} else if (oeSymb.ID.OptionRight == OptionRight.Call) {
							
							Debug(" OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO ");
    						Debug(" OO CALL OPTION MARKET ORDER FOR : " + oeSymb);
    						Debug(" OO PROCESSING TPR SYNCHRONOUSLY IN LINE oo oo oo ");
    						Debug(" OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO ");

							/*if (tradeRecs.Any(t => t.cSymbol.Equals(oeSymb) & t.cQty == -order.Quantity)) { 
								var transRec = tradeRecs.Where(t => t.cSymbol.Equals(oeSymb) & t.cQty == -order.Quantity).FirstOrDefault();
								transRec.cEndPrice = orderEvent.FillPrice;
								Debug(" OO ** Setting cEndPrice.");
							}*/
							
							//Debug(" OO NOTE     ALGO-DRIVEN CALL MARKET ORDER");
						}
						
					} else if (!oeSymb.HasUnderlying)	 {
					
						Debug(" OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO ");
    					Debug(" OO UNDERLYING ORDER FOR : " + oeSymb);
    					Debug(" OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO ");
						
					} else {					// NON EXERCISE ORDER HAS UNDERLYING

    					Debug(" OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO ");
    					Debug(" OO UNKNOWN ALGO ORDER ORDER FOR : " + oeSymb);
    					Debug(" OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO ");
    						
						/*if (tradeRecs.Any(tpr => tpr.uSymbol.Equals(oeSymb) & tpr.uQty == -order.Quantity)) { 
							var transRec = tradeRecs.Where(tpr => tpr.uSymbol.Equals(oeSymb) & tpr.uQty == -order.Quantity).FirstOrDefault();
							Debug (" OO ** THERE IS A TPR THAT IS " + (transRec.isOpen ? " OPEN" : " CLOSED"));
							transRec.isOpen = false;
							transRec.uEndPrice = orderEvent.FillPrice;
							transRec.endDate = orderEvent.UtcTime;
							transRec.reasonForClose = "Options Expiration";
							Plot("Stock Chart", "Sells", orderEvent.FillPrice);
						}*/
					}
					Debug(" ---------------------------------------------------------------------------");
				}
     		}		// orderStatus = Filled
   		} 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);
    		}

    		return;
       	}

		}

		//private void CheckExpiration() {
		//    foreach(var callOption in callOptions) {
        //		if(callOption.Symbol.ID.Date == Time.Date) {
        //			Debug($"{callOption.Symbol}"); 
        //		}
      	//}
	//	}

		public override void OnEndOfDay(Symbol symbol)
		{
			//lastSto = sto.Current.Value;							// store values from night before
    		lastAd = ad.Current.Value;
        	lastAdOsc = adOsc.Current.Value;
        	lastAdx = adx.Current.Value;
        	lastAdxr = adxr.Current.Value;
        	lastObv = obv.Current.Value;
        	lastVariance = variance.Current.Value;
     
			
			Plot("Stock Chart", "EOD Price", Securities[thisSymbol].Price);
			//Plot("Stock Chart", "Variance", variance.Current.Value);
			//Plot("Stock Chart", "Stochastics", sto.Current.Value);
			Plot("Momentums" , "AD", ad.Current.Value);
			Plot("Momentums", "ADOSC", adOsc.Current.Value);
			Plot("Momentums", "OBV", obv.Current.Value);
			Plot("Trends", "ADX", adx.Current.Value);
			Plot("Trends", "ADXR", adxr.Current.Value);
			
			//string saveString = "";
			foreach(var kvp in Securities)
			{
				var security = kvp.Value;
				if (security.Invested)
				{
					//saveString = "," + security.Symbol + ", " + security.Holdings.Quantity + Environment.NewLine;
					//Debug($"{security.Symbol} : {security.Holdings.Quantity}");
				}
			}
		}

		public override void OnEndOfAlgorithm()
        {
        	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.cSymbol != null) {
						tpr.uEndPrice = Securities[tpr.uSymbol].Price;
					}
					
					if (tpr.pEndPrice == 0 && tpr.pSymbol != null) {
						tpr.pEndPrice = Securities[tpr.pSymbol].Price;
					}

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

        public override void OnSecuritiesChanged(SecurityChanges changes)
        {
        	/*
        	addedSecCount = addedSecCount + 1;
        	foreach(var security in changes.AddedSecurities)
        	{
        		if (security.Type == SecurityType.Equity) return;
	
	    		Debug(" -- " + addedSecCount + " ADDED SECURTITIES CHANGES :" + security.Symbol);
        		var history = History(security.Symbol, 60, Resolution.Minute);
        		foreach (var histSymb in history)
        		{
                	Debug("History: @" +  histSymb.EndTime + " " + histSymb);
                	
        		}
        	}
        	

        	foreach(var security in changes.RemovedSecurities)
        	{
				Debug($"{security.Symbol} removed at {Time}");
        	}
        	// changes.AddedSecurities
        	
        	*/
        	
        }
        
		private void HistoricalSecurityInitializer(Security security)
		{ 
			var bar = GetLastKnownPrice(security);
    		security.SetMarketPrice(bar);
			
			if(security.Type == SecurityType.Option){
    			var openInterest = History<OpenInterest>(security.Symbol, TimeSpan.FromDays(1));
    			var tradeBar = History<TradeBar>(security.Symbol, TimeSpan.FromDays(1));

    			////////////////////////////			WARNING THIS LINE OF CODE PREVENTS OPTIONS FROM BEING PRICED   //////////////////
    			//var optionChain = History<OptionChain>(security.Symbol, TimeSpan.FromDays(1));
				
			}
    		
		}
        
        private bool CheckBadDate(DateTime checkDate)
        {
        	DateTime badDate1 = Convert.ToDateTime(GetParameter("BadDate"));
        	DateTime badDate2 = badDate1.AddMinutes(1);
        	//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))
		  	{
		  		return badDtParameter;
		  	} else {
		  		return false;
		  		
		  	}
        }
    }  // class
} // namespace
using QuantConnect.Securities.Option;
namespace QuantConnect.Algorithm.CSharp {
	public partial class CollarAlgorithm : QCAlgorithm
	{
		public class SSQRColumn
		{
			public decimal stockPrice;
			public DateTime exDate;
			public DateTime optExpiry;
			public DateTime callExpiry;
			public int daysInPosition;
			public decimal interestCost;
			public Symbol putSymbol;
			public Symbol callSymbol;
			public Symbol wCallSymbol;
			public decimal putPremium;			// paid for buying the body
			public decimal callPremium;			// received for selling back call
			public decimal wCallPremium;		// paid for buying the wings
			public decimal putStrike;
			public decimal callStrike;
			public decimal wCallStrike;
			public decimal putOpenInterest;
			public decimal callOpenInterest;
			public decimal putDelta;
			public decimal callDelta;
			public decimal wcDelta;
			public decimal wingFactor;
			public decimal putGamma;
			public decimal callGamma;
			public decimal putVega;
			public decimal callVega;
			public decimal putRho;
			public decimal callRho;
			public decimal putTheta;
			public decimal callTheta;
			public decimal putImpliedVol;
			public decimal callImpliedVol;
			public decimal divAmt;
			public int divCount;
			public decimal downsideRisk;
			public decimal upsidePotential;
			public decimal netIncome;
			public decimal netOptions;
			public decimal divDollars;
			public decimal haircut;				// committed capital in a portfolio margin account
			public decimal ROC;					// Return on Capital
			public decimal ROR;					// Return on Risk
			public decimal CCOR;				// Call Coverage over downside Risk
			public string description1;
			public string description2;
			//public string description3;

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

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



		}
		
	}
}
/////////////////////////////			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 sub routine takes in the options expiries and all the symbols
	    // ***				available in the Slice.Data and builds the puts chains and calls symbols lists
	    // ***				The puts and calls symbols lists are used to build the contracts lists
	    // **				The contracts lists are used to build the SSQR Matrix
	    // ***********************************************************************************
    	public List<SSQRColumn> assembleSSQRMatrix(Dictionary<int, DateTime> expiries, IEnumerable <Symbol> allUndrOptSymbs, decimal stockPrice, decimal amtDividend, DateTime tradeDate, DateTime thisExDivDate)
    	{
			if (doTracing) Debug($" -- AA AA ASSEMBLE SSQR MATRIX FOR {thisSymbol}");
			if (haltProcessing)
			{
				Debug(" @@@@@  Halted assembleSSQR processing");
			}
			
			OptionChain putChain;						// chain object to get put contracts
			OptionChain callChain;						// chain object to get call contracts
			OptionChain wcChain;						// chain object to get wc contracts
			
			Greeks putGreeks;					// chain object to collect put greeks
			Greeks callGreeks;					// chain object to collect call greeks
			Greeks wcGreeks;					// chain object to collect wing call greeks

			List<SSQRColumn> SSQRMatrix = new List<SSQRColumn>();
			List<Symbol> ntmSymbs = new List<Symbol>();

        	var callSymbolsForThisExpiry = allUndrOptSymbs;   
        	var putSymbolsForThisExpiry = allUndrOptSymbs;	   
	        // Get the ATM call contract 
    		var atmCall = allUndrOptSymbs.Where(s => s.ID.OptionRight == OptionRight.Call)
    											.OrderBy(s => Math.Abs(s.ID.StrikePrice - stockPrice))
    											.FirstOrDefault();
    											
			var atmPut =  allUndrOptSymbs.Where(s => s.ID.OptionRight == OptionRight.Put)
    											.OrderBy(s => Math.Abs(s.ID.StrikePrice - stockPrice))
    											.FirstOrDefault();
    		
    		Option atmCallOpt = AddOptionContract(atmCall);
			Option atmPutOpt = AddOptionContract(atmPut);
			
			var atmStrike = atmCall.ID.StrikePrice;
    		var lowStrike = (1 - (maxPutOTM / (decimal)100)) * atmStrike;   // ~~ eventually need a mechanism to determine strike steps
    		var highStrike = (decimal)1.1 * atmStrike;  // ~~ and use strike steps to set upper and lower bounds
			
			// decimal highWCStrike = 0;						// for evaluating the range of wing call strikes
			// decimal lowWCStrike = 0;						// for evaluating the range of wing call strikes
			// decimal wcInterval = 0;							// for calculating wing calls

			// examine options expiries to get proper dividend-correlated time-frames
			
			
			if (badDtParameter) debugSymbol = atmPut;				// if debugging, set the debugSymbol to the atmPut for diagnostics
			
			
			// Alex informed me that the openInterest data has to be warmed up
			// Alex further informed me that .OpenInterest does not work, rather, use .Value
			var callOpenInterest = History<OpenInterest>(atmCall, TimeSpan.FromDays(5)).FirstOrDefault();
			var putOpenInterest = History<OpenInterest>(atmPut, TimeSpan.FromDays(5)).FirstOrDefault();
			
			var totOpenInterest = callOpenInterest.Value + putOpenInterest.Value;     											
    		
    		//if (doTracing) Debug("0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O");
    		//if (doTracing) Debug("0O0O0O0O0O0O0O0O0O0O0O0O		OPEN INTEREST		0O0O0O0O0O0O0O0O0O0O0O0O0O0O0");
    		//if (doTracing) Debug(" The total Put + Call Open Interest for " + atmCall.ID.Underlying + " is " + atmCallOpt.OpenInterest + ".");
	   		//if (doTracing) Debug("0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O");
    		
	        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>();
    		
    		decimal haircut;			// for buliding SSQR -- used when trading decision is based upon return on committed capital
    		int daysInTrade;			// for building SSQR -- calculating interest cost
    		Option thisPutOpt;			// for building SSQR
    		Option thisCallOpt;			// for building SSQR
    		Option thisWCCallOpt;
    		Symbol thisPutSymb;			// for building SSQR
    		Symbol thisCallSymb;		// for building SSQR
    		Symbol thisWCCallSymb;		// 
    		decimal thisPutPrem;		// for building SSQR
    		decimal thisCallPrem;		// for building SSQR
    		decimal thisWCCallPrem;	// for building SSQR
    		decimal thisPutStrike;		// for building SSQR
    		decimal thisCallStrike;		// for building SSQR
    		decimal thisWCCallStrike;	// for building SSQR
    		DateTime thisPutExpiry;		// for building SSQR
    		DateTime thisCallExpiry;	// for building SSQR
    		//DateTime thisExpiry;		// for building SSQR
			//decimal putOpenInterest;
			//decimal callOpenInterest;
			//decimal putDelta;
			//decimal callDelta;
			//decimal putGamma;
			//decimal callGamma;
			//decimal putVega;
			//decimal callVega;
			//decimal putRho;
			//decimal callRho;
			//decimal putTheta;
			//decimal callTheta;
			//decimal putImpliedVol;
			//decimal callImpliedVol;
			int numDividends;			// for building SSQR
    		//decimal amtDividend;		// for building SSQR
    		decimal interestCost;		// for building SSQR
    		decimal amtSpread;			// for building SSQR
    		decimal amtTotCost;			// for building SSQR
    		
    		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
			thisFFRate = fedFundsRates[justDate];							//	fedFundsRates is a Dictionary where DateTime index are all 12:00:00am
			//thisFFRate = thisFFRate;										// fedFundsRates entries are expressed as percentages and were converted upon reading them into dictionary
			//interestCost = (thisFFRate + ibkrRateAdj)/workingDays * (decimal) daysInTrade * stockPrice;  
																		// create a range of expiration dates from the prior expiration to the whichExpiry date.
			//callSymbolsForThisExpiry = allUnderlyingOptionsSymbols.Where( o=> DateTime.Compare(o.ID.Date, pastExpiry) > 0 && DateTime.Compare(o.ID.Date, whichExpiry)<=0 &&
			
			if (haltProcessing) {
				Debug("----------------------   Expiries   ----------------------------");
				Debug("-----" + stockPrice.ToString() + ", " + lowStrike.ToString() + ", " + highStrike.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"));
				//IterateChain(allUnderlyingOptionsSymbols, "allUnderlyingOptionsSymbols");
				
			}
			
			callLoopCntr = callLoopCntr + 1;
			
			callSymbolsForThisExpiry = allUndrOptSymbs.Where( o=> ( DateTime.Compare(o.ID.Date, expiries[2])==0 | 
																				DateTime.Compare(o.ID.Date, expiries[3])==0 |
																				DateTime.Compare(o.ID.Date, expiries[4])==0 |
																				DateTime.Compare(o.ID.Date, expiries[5])==0 ) &&
																	o.ID.OptionRight == OptionRight.Call &&
     																o.ID.StrikePrice >= lowStrike &&
     																o.ID.StrikePrice < highStrike)
     																.OrderByDescending(o => o.ID.StrikePrice);
		
																	// ****************		Check if any calls are returned.   If not, increment expiries by 1 month and try again
			if (callSymbolsForThisExpiry == null | callSymbolsForThisExpiry.Count() == 0) 
			{
				if (doTracing) Debug("--  get callSymbolsForThisExpiry failed 1st Pass ");
				expiries[2] = FindNextOptionsExpiry(expiries[1], 4);
				expiries[3] = FindNextOptionsExpiry(expiries[1], 7);
				expiries[4] = FindNextOptionsExpiry(expiries[1], 10);
				expiries[5] = FindNextOptionsExpiry(expiries[1], 13);
			
				callSymbolsForThisExpiry = allUndrOptSymbs.Where( o=> ( DateTime.Compare(o.ID.Date, expiries[2])==0 | 
																				DateTime.Compare(o.ID.Date, expiries[3])==0 |
																				DateTime.Compare(o.ID.Date, expiries[4])==0 |
																				DateTime.Compare(o.ID.Date, expiries[5])==0 ) &&
																	o.ID.OptionRight == OptionRight.Call &&
     																o.ID.StrikePrice >= lowStrike &&
     																o.ID.StrikePrice < highStrike)
     																.OrderByDescending(o => o.ID.StrikePrice);
				if (haltProcessing) {
					if (doTracing) Debug("----------------------   Expiries 2nd Pass  ----------------------------");
					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"));
					//if (doTracing);(callSymbolsForThisExpiry, "callSymbols");
				}
																	// *****************		If no calls are returned in 2nd pass, exit with null SSQRMatrix
				if (callSymbolsForThisExpiry == null || callSymbolsForThisExpiry.Count() == 0)
				{
					if (doTracing) Debug("--  get callSymbolsForThisExpiry failed 2nd Pass ");
					return SSQRMatrix;
				} else if (doTracing) Debug("-- get callSymbolsForTheseExpiries succeeded 2nd Pass ");	
			} else {
				if (doTracing) Debug(" -- getCallSymbolsForThisExpiry succeded 1st pass  ");
				
			} 
			
			
			var enumerator = callSymbolsForThisExpiry.GetEnumerator();	
    		//callOptionsList.Clear();
    			
    		while (enumerator.MoveNext()) 
    		{
				optSymbol = enumerator.Current;
				tempOption = AddOptionContract(optSymbol, Resolution.Minute, true);
				tempOption.PriceModel = OptionPriceModels.BinomialTian();		/// necessary for Greeks?
				callOptionsList.Add(tempOption);
			}
			
			
			// if the first attempt at obtaining calls fails, then expiries[2-5] are incremented by 1 month.  If that fails
			// this subroutine was exited before here.   In any case, if calls can be obtained, puts can be as well
			//putSymbolsForThisExpiry = allUnderlyingOptionsSymbols.Where( o=> DateTime.Compare(o.ID.Date, pastExpiry) > 0 && DateTime.Compare(o.ID.Date, whichExpiry)<=0 &&
			putSymbolsForThisExpiry =  allUndrOptSymbs.Where( o=> ( DateTime.Compare(o.ID.Date, expiries[2])==0 | 
																				DateTime.Compare(o.ID.Date, expiries[3])==0 |
																				DateTime.Compare(o.ID.Date, expiries[4])==0 |
																				DateTime.Compare(o.ID.Date, expiries[5])==0 ) &&
																	o.ID.OptionRight == OptionRight.Put &&
     																o.ID.StrikePrice >= lowStrike &&
     																o.ID.StrikePrice < atmStrike)
     																.OrderByDescending(o => o.ID.StrikePrice);
		
			if (haltProcessing) {
					if (doTracing) Debug("----------------------   Expiries PUTS Pass  ----------------------------");
					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"));
					//if (doTracing) IterateChain(putSymbolsForThisExpiry, "putSymbols");
			}
			
			
			if (putSymbolsForThisExpiry == null | putSymbolsForThisExpiry.Count()== 0)
			{ 
				if (doTracing) Debug("--  get putSymbolsForThisExpiry failed 2nd Pass (after call succeeded)");
				return SSQRMatrix;        	// return null SSQRMatrix and pass control back to OnData()
			
			} else {
    			if (doTracing) Debug("-- get putSymbolsForTheseExpiries succeeded.");	
    			var penumerator = putSymbolsForThisExpiry.GetEnumerator();	
    			//putOptionsList.Clear();
    			
    			while (penumerator.MoveNext()) 
    			{
					optSymbol = penumerator.Current;
					tempOption = AddOptionContract(optSymbol, Resolution.Minute, true);
					tempOption.PriceModel = OptionPriceModels.BinomialTian();			/// necessary for Greeks?

					putOptionsList.Add(tempOption);
				}
			
				IterateContracts(putOptionsList);
				IterateContracts(callOptionsList);
			}

			// Now iterate through the puts and sub-iterate through the calls to assemble the SSQRMatrix
			// for pricing, puts are bought at the offer and calls are sold at the bid prices.
			// Each price should be the midpoint between the open and close.

			if (putOptionsList == null | callOptionsList == null | putOptionsList.Count() ==0 | callOptionsList.Count() == 0)
			{
				Debug("-- get call and put options failed both passes ");	
				return SSQRMatrix;				// return empty SSQRMatrix
			} else {
				var putEnum = putOptionsList.GetEnumerator();
				Debug("HELLooooo");
    			while (putEnum.MoveNext())
    			{
    				thisPutOpt = putEnum.Current;
    				thisPutPrem = thisPutOpt.AskPrice;
    				thisPutStrike = thisPutOpt.StrikePrice;
    				thisPutExpiry = thisPutOpt.Expiry;
    				//putChain = History<OptionChain>(thisPutOpt.Symbol, TimeSpan.FromDays(5)).FirstOrDefault();
	    			//putGreeks = putChain.Contracts[thisPutOpt.Symbol];
	    			
	    			var underlying = thisPutOpt.Underlying.Symbol;
	    			var chains = CurrentSlice.OptionChains;
	    			Debug("Here 1");
	    			foreach(var chainSymbol in chains.Keys){
	    				Debug("Here 2");	
	    				var chain = chains[chainSymbol];
	    				
	    				foreach(var contract in chain){
	    					Debug("Here 3");
	    					if(contract.Symbol == thisPutOpt.Symbol){
	    						Debug("Contract Found in Chain! " + contract.Symbol.Value);
	    						putGreeks = contract.Greeks;
	    						Debug(putGreeks.Delta);
	    					}else{
	    						Debug(contract.Symbol + "!=" + thisPutOpt.Symbol);
	    					}
	    				}
	    				
	    			}
	    			// var optionChainSymbol = AddOption(underlying).Symbol;
	    			
	    			// thisPutGreeks = CurrentSlice.OptionChains[]

    				
    				
    				atmCall = allUndrOptSymbs.Where(s => s.ID.OptionRight == OptionRight.Call & DateTime.Compare(s.ID.Date, thisPutExpiry)==0)			/// get atmCall for this Put Option Expiration
    											.OrderBy(s => Math.Abs(s.ID.StrikePrice - stockPrice))
    											.FirstOrDefault();
    				atmCallOpt = AddOptionContract(atmCall);
    				
    				wcCallsList.Clear();
    				ntmSymbs = allUndrOptSymbs.Where( o=> ( DateTime.Compare(o.ID.Date, thisPutExpiry)==0) &
																	o.ID.OptionRight == OptionRight.Call &
     																o.ID.StrikePrice >= atmStrike & 
     																o.ID.StrikePrice <= (decimal)1.1 * atmStrike).Distinct().ToList();
     																
     				if (haltProcessing & doTracing) Debug($" -- Put Option {putEnum.Current} ");
     				foreach (var optSymb in ntmSymbs)												
     				{
     					atmCallOpt = AddOptionContract(optSymb);
     					wcCallsList.Add(atmCallOpt);
     					if (haltProcessing & doTracing) Debug(" -- -- NTM Wing Call " + atmCallOpt);
     				}
     																
					wcCallsList.Sort((x,y) => x.StrikePrice.CompareTo(y.StrikePrice));
					
    				var wcEnum = wcCallsList.GetEnumerator();
    				
    				var callEnum = callOptionsList.GetEnumerator();	
    				while (callEnum.MoveNext())
    				{
    					thisCallOpt = callEnum.Current;
    					thisCallPrem = thisCallOpt.BidPrice;
    					thisCallStrike = thisCallOpt.StrikePrice;
    					thisCallExpiry = thisCallOpt.Expiry;
						//callChain = History<OptionChain>(thisCallOpt.Symbol, TimeSpan.FromDays(30)).FirstOrDefault();
						//callGreeks = callChain.Contracts[thisCallOpt.Symbol];
						
    					///*dividends,  daysInTrade, interestCost
    					//if (thisCallStrike > thisPutStrike & DateTime.Compare(thisCallExpiry,thisPutExpiry)>=0 )		// only add put/call combinations where call strike is above put strike and call expiry is equal to or later than put
    					if ((thisCallStrike > thisPutStrike & DateTime.Compare(thisCallExpiry,thisPutExpiry)>=0) | (thisCallStrike >= thisPutStrike & DateTime.Compare(thisCallExpiry,thisPutExpiry)>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)
    					{
    						foreach (var wcCallOpt in wcCallsList) {
    							thisWCCallPrem = wcCallOpt.AskPrice;
    							if (wcCallOpt.StrikePrice > thisCallStrike ) {
	    							SSQRColumn thisSSQRColumn = buildSSQRColumn(thisPutOpt, thisCallOpt, wcCallOpt, tradeDate, thisExDivDate, amtDividend, stockPrice, thisFFRate);
    								SSQRMatrix.Add(thisSSQRColumn);
    							}
    						}
    					}				//  if thisCallStrike == thisPutStrike
    				}					//  while callEnum
    			}						//  while putEnum	
    			
    			if (doTracing) Debug($" -- AA AA RETURNED {SSQRMatrix.Count()} SSQR MATRICES FOR {thisSymbol}" );
    			
   			}							//  !null

    		return SSQRMatrix;
    		
    	} // AssembleSSQRMatrix
  

    	// **********************   buildSSQRColumn **************************************
	    // ***  			This sub routine takes in the variables for the iterated put and call Options Lists
	    // ***				as well as the dividends count, dividend amount, and stock price
	    // ***				and returns an SSQRColumt to be added to the SSQRMatrix list
	    // ***********************************************************************************

	    public SSQRColumn buildSSQRColumn(Option thisPutOpt, Option thisCallOpt, Option wcCall, DateTime tradeDate, DateTime firstExDate, decimal amtDividend, decimal stockPrice, decimal FFRate)
        //public SSQRColumn buildSSQRColumn(Option thisPutOpt, 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
	    	decimal wingPremium = 0;											// added premium to do the wings
	    	int monthsInTrade = 0;
	    	int daysInTrade = 0;
	    	int dividends = 0;
	    	
	    	decimal thisPutPrem = thisPutOpt.AskPrice;
	    	decimal thisCallPrem = thisCallOpt.BidPrice;
	    	decimal wcCallPrem = wcCall.AskPrice;								// ATM premium to buy the calls
	    	
	    	wingPremium = (thisPutPrem + wcCallPrem);				// premium (proportionate) for the wings
			
			SSQRColumn thisColumn = new SSQRColumn();							// get a new SSQRColumn
	    	if (thisPutPrem == 0 | thisCallPrem == 0) return thisColumn;		// don't build SSQRColumns with missing premium values
	
	    	decimal thisPutStrike = thisPutOpt.StrikePrice;
	    	decimal thisCallStrike = thisCallOpt.StrikePrice;
	    	decimal wcCallStrike = wcCall.StrikePrice;
	    	DateTime thisPutExpiry = thisPutOpt.Expiry;
	    	DateTime thisCallExpiry = thisCallOpt.Expiry;
	    	//DateTime atmCallExpiry = atmCall.Expiry;
	    
	    	daysInTrade = (thisPutExpiry - tradeDate).Days;
	    	decimal intCost = (FFRate + ibkrRateAdj)/workingDays * (decimal) daysInTrade * stockPrice;  

	    	if (haltProcessing) {
	    		Debug("				HALTED IN buildSSQRColumn processing")	;
	    	}

	    	monthsInTrade = thisPutExpiry.Month - firstExDate.Month;
	    	
	    	if( thisPutExpiry.Year != firstExDate.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.putSymbol = thisPutOpt.Symbol;
	    	thisColumn.callSymbol = thisCallOpt.Symbol;
	    	thisColumn.wCallSymbol = wcCall.Symbol;		// atm call for this column (based upon put)
	    	thisColumn.putPremium = thisPutPrem;
	    	thisColumn.callPremium = thisCallPrem;
	    	thisColumn.wCallPremium = wcCall.AskPrice;	// 
	    	thisColumn.putStrike = thisPutStrike;
	    	thisColumn.callStrike = thisCallStrike;
	    	thisColumn.wCallStrike = wcCall.StrikePrice;
	    	thisColumn.exDate = firstExDate;
	    	thisColumn.optExpiry = thisPutExpiry;
	    	thisColumn.callExpiry = thisCallExpiry;

	    	/* if (useDeltas) {
	    		thisColumn.putDelta = pGrks.Greeks.Delta;
	    		thisColumn.callDelta = cGrks.Greeks.Delta;
	    		thisColumn.putGamma = pGrks.Greeks.Gamma;
	    		thisColumn.callGamma = cGrks.Greeks.Gamma;
	    		thisColumn.putVega = pGrks.Greeks.Vega;
	    		thisColumn.callVega = cGrks.Greeks.Vega;
	    		thisColumn.putRho = pGrks.Greeks.Rho;
	    		thisColumn.callRho = cGrks.Greeks.Rho;
	    		thisColumn.putTheta = pGrks.Greeks.Theta;
	    		thisColumn.callTheta = cGrks.Greeks.Theta;
	    		thisColumn.putImpliedVol = pGrks.ImpliedVolatility;
	    		thisColumn.callImpliedVol = cGrks.ImpliedVolatility;
	    	} */
	    	thisColumn.divAmt = amtDividend;
	    	thisColumn.divCount = dividends;
	    	thisColumn.stockPrice = stockPrice;
	    	thisColumn.daysInPosition = daysInTrade;
	    	thisColumn.interestCost = intCost;
	    	
	    	thisSpread = thisCallStrike - thisPutStrike;
	    	if (!ibkrHairCuts.ContainsKey( (thisCallStrike - thisPutStrike)) )
	    	{
	    		//Debug("*^*^*^*^*^*^*^*^*^*^**^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*");
	    		//Debug("Make a haircut entry for " + (thisCallStrike - thisPutStrike).ToString());
	    		//Debug("*^*^*^*^*^*^*^*^*^*^**^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*");
	    		if (thisSpread < 5M)
				{
					thisColumn.haircut = .5M;
				} else thisColumn.haircut = thisSpread;
	    		
	    	}else
	    	{
	    		thisColumn.haircut = ibkrHairCuts[thisSpread];
	    	}
        	
	    	decimal divDollars = amtDividend * dividends;
	    	thisColumn.divDollars = divDollars;
	    	
	    	decimal stockLossIfCalled = (thisCallStrike>stockPrice) ? 0 : (thisPutPrem>thisCallPrem) ? (thisCallStrike -stockPrice) : 0; // loss=0 if cStrike>stkPrice, otherwise negative	***Loss (negative value) if ITM calls are assigned (0 if #calls<#puts)
	    	
	    	decimal netOptions = -thisPutPrem + thisCallPrem;		/// netOptions equals negative putPrem (expense) plus positive call 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
	    	
			wingFactor  = (netOptions + divDollars - intCost) / wingPremium;
			
			if (wingFactor < 0)  wingFactor = 0;
			if (wingFactor > 0.2M ) wingFactor = 0.2M;
	    	thisColumn.wingFactor = wingFactor;
	    	
	    	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 - netOptions) > thisPutStrike) ? stockPrice - netOptions - thisPutStrike + intCost: intCost;
	    	thisColumn.downsideRisk = downsideRisk;														// subtracts dividends collected and net options premiums and intCost
	    	
	    	decimal upsidePotential = thisCallStrike - stockPrice + divDollars + netOptions - intCost;	// When writing OTM calls, there is a potential 
	    	thisColumn.upsidePotential = upsidePotential;												// upside appreciation from net collar cost to the call strike.
	    	
	    	thisColumn.ROR = upsidePotential/-downsideRisk;												// store ROR 
	    	
	    	/*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 " + thisPutOpt.Symbol.Underlying  + " @ " + stockPrice + " is the " + thisPutStrike + "/" + thisCallStrike + " collar  ";
	    	
	    	thisColumn.description2 = "," + thisPutOpt.Symbol.Underlying + "," + String.Format("{0:0.00}", stockPrice) + "," + firstExDate.ToString("MM/dd/yy") + "," 
	    		+ dividends + "," + String.Format("{0:0.00}", amtDividend) + "," + String.Format("{0:0.00}",divDollars) + "," + daysInTrade + ", " 
	    		+ String.Format("{0:0.00}", intCost) + ", " + thisPutExpiry.ToString("MM/dd/yy") + ", " + thisCallExpiry.ToString("MM/dd/yy") + ", " 
	    		+ String.Format("{0:0.00}",thisPutStrike) + ", " + String.Format("{0:0.00}",thisPutPrem) + ", " 
	    		+ String.Format("{0:0.00}",thisCallStrike) + ", " + String.Format("{0:0.00}", thisCallPrem) + ", " 
	    		+ String.Format("{0:0.00}", thisColumn.wCallStrike) + ", " + String.Format("{0:0.00}", thisColumn.wCallPremium) + ", "
				+ String.Format("{0:0.00}",thisColumn.putDelta) + ", " + String.Format("{0:0.00}", thisColumn.callDelta) + ", "
	    		+ String.Format("{0:0.00}",netOptions) + ", " + String.Format("{0:0.00}", thisColumn.netIncome) + ", " + String.Format("{0:0.00}", thisColumn.haircut) + ", " 
	    		+ String.Format("{0:0.00}",thisColumn.ROC) + "," + String.Format("{0:0.00}", upsidePotential) + "," 
	    		+ String.Format("{0:0.00}", downsideRisk) + "," + String.Format("{0:0.00}",thisColumn.ROR) + "," + String.Format("{0:0.00}", thisColumn.CCOR ) + "," 
	    		+ String.Format("{0:0.00}", thisColumn.wingFactor) + "," + thisColumn.putSymbol  + "," + thisColumn.callSymbol;
	    	
	    	return thisColumn;
	    }
  
    	
	    // **********************   AddCorrespondingPut	  *******************************************
	    // ***  			This code will add the put option which corresponds to the call shorted
	    // ***				for purposes of evaluating it in ex-dividend approachment.
	    // ***				Option must be constructed with correct parameters before it can be added
	    // ******************************************************************************************
        
    	public Option AddCorrespondingPut(Symbol tradableCall)
	    {    		
	        int indexOfC = tradableCall.ToString().LastIndexOf("C");
			char[] charArrayC = tradableCall.ToString().ToCharArray();
			char[] charArrayP = charArrayC;
			charArrayP[indexOfC] = 'P';
			string putString = new string(charArrayP);

			var putSymbol = QuantConnect.Symbol.CreateOption(
	    		tradableCall.Underlying,
				Market.USA,
				OptionStyle.American,
				OptionRight.Put,
				tradableCall.ID.StrikePrice,
				tradableCall.ID.Date);
			
			Option correspondingPut = AddOptionContract(putSymbol);
			return correspondingPut;
	    }
        
        
        // **********************   GetCorrespondingPut	  *******************************************
	    // ***  			This code will get the put option which corresponds to the call shorted
	    // ***				for purposes of evaluating it in ex-dividend approachment -- 
	    // ***				??? return Symbol or string?
	    // ******************************************************************************************
        
    	public string GetCorrespondingPut(Symbol tradableCall)
	    {    		
	        int indexOfC = tradableCall.ToString().LastIndexOf("C");
			char[] charArrayC = tradableCall.ToString().ToCharArray();
			char[] charArrayP = charArrayC;
			charArrayP[indexOfC] = 'P';
			string putString = new string(charArrayP);
			return putString;
	    }
	    
	    
	    // **********************   getNextExDate		**************************************
	    // ***  			Use this to find and return the next ex-dividend date from 
	    // ***				the list exDividendDates given a Slice.DateTime
	    // ***********************************************************************************
        
        public DateTime getNextExDate(string tickStr, DateTime sliceTime)     
		{
			// // /// /// 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) {
				Debug("       HALTED IN getNextExDate");
			}
			DividendRecord nextExDateRec = exDividendDates.Where(d => DateTime.Compare(sliceTime.Date, d.exDate.Date)<=0 &&
											 d.ticker == tickStr)
											.OrderBy(d => d.exDate)
											.FirstOrDefault();
			
			DateTime nextExDate = nextExDateRec.exDate;
			stockDividendAmount = nextExDateRec.divAmt;
			divFrequency = nextExDateRec.frequency;
			
			if (DateTime.Compare(nextExDate, fmrNextExDate) > 0) logged = 0;
			
			//Console.WriteLine("This slice was on " + sliceTime + " and the last NextExDate was " + fmrNextExDate + ".");
			
			if (logged < 1)
			{
				fmrNextExDate = nextExDate;
				//Console.WriteLine("This slice was on " + sliceTime + " and we found the next Ex-Date was " + nextExDate + " ");
				logged++;
			}
			
			return nextExDate;
		}	
    	
    	
    	
    	public SSQRColumn fillSSQRColumn ()
	    {
	    	SSQRColumn anotherSSQRColumn = new SSQRColumn();
	    	return anotherSSQRColumn;
	    }
	    
	    // **********************   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
	    // ***********************************************************************************
	    
	    public Dictionary<int, DateTime> GetOptionExpiries(DateTime tradeD, DateTime nextExDate, DateTime thisMonthExpiry, bool isPrimary){
	    		// Initialize expiration date variables //
        		DateTime firstExpiry = new DateTime();
        		DateTime secondExpiry = new DateTime();
        		DateTime thirdExpiry = new DateTime();
        		DateTime fourthExpiry = new DateTime();
        		DateTime fifthExpiry = 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 (isPrimary) {
	        		if (DateTime.Compare(nextExDate, thisMonthExpiry) <= 0)
	        		{
	        			firstExpiry = FindNextOptionsExpiry(thisMonthExpiry, 0);
	        
	        			if (firstExpiry.Subtract(tradeD).Days <= 10) {
		        			firstExpiry = FindNextOptionsExpiry(thisMonthExpiry, 1);
		        			secondExpiry = FindNextOptionsExpiry(thisMonthExpiry, 4);
		        			thirdExpiry = FindNextOptionsExpiry(thisMonthExpiry, 7);
		        			fourthExpiry = FindNextOptionsExpiry(thisMonthExpiry, 10);
		        			fifthExpiry = FindNextOptionsExpiry(thisMonthExpiry, 13);
	        			}
	        			
	        			secondExpiry = FindNextOptionsExpiry(thisMonthExpiry, 3);	
	        			thirdExpiry = FindNextOptionsExpiry(thisMonthExpiry, 6);
	        			fourthExpiry = FindNextOptionsExpiry(thisMonthExpiry, 9);
	        			fifthExpiry = FindNextOptionsExpiry(thisMonthExpiry, 12);
	        			
	        		}else
	        		{
	        			firstExpiry = FindNextOptionsExpiry(thisMonthExpiry, 1);
	        			secondExpiry = FindNextOptionsExpiry(thisMonthExpiry, 4);
	        			thirdExpiry = FindNextOptionsExpiry(thisMonthExpiry, 7);
	        			fourthExpiry = FindNextOptionsExpiry(thisMonthExpiry, 10);
	        			fifthExpiry = FindNextOptionsExpiry(thisMonthExpiry, 13);
	        		}
        		} else {
	        		if (DateTime.Compare(nextExDate, thisMonthExpiry) <= 0)
	        		{
	        			firstExpiry = FindNextOptionsExpiry(thisMonthExpiry, 0);
	        			secondExpiry = FindNextOptionsExpiry(thisMonthExpiry, 1);	
	        			thirdExpiry = FindNextOptionsExpiry(thisMonthExpiry, 2);
	        			fourthExpiry = FindNextOptionsExpiry(thisMonthExpiry, 3);
	        			fifthExpiry = FindNextOptionsExpiry(thisMonthExpiry, 4);
	        			
	        		}else
	        		{
	        			firstExpiry = FindNextOptionsExpiry(thisMonthExpiry, 1);
	        			secondExpiry = FindNextOptionsExpiry(thisMonthExpiry, 2);
	        			thirdExpiry = FindNextOptionsExpiry(thisMonthExpiry, 3);
	        			fourthExpiry = FindNextOptionsExpiry(thisMonthExpiry, 4);
	        			fifthExpiry = FindNextOptionsExpiry(thisMonthExpiry, 5);
	        		}
        			
        		}
    
        		
        		expiries.Add(1, firstExpiry);
        		expiries.Add(2, secondExpiry);
        		expiries.Add(3, thirdExpiry);
        		expiries.Add(4, fourthExpiry);
        		expiries.Add(5, fifthExpiry);
        		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, 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;
	    	}
	    	
	    	month = month + addedMonths;
	    	
	    	// Adjust if bigger than 12
	    	if(month > 12){
	    		month = month % 12; 
	    		year = year + 1;
	    	}

			if (haltProcessing) {
				Debug(" HALTED IN 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
       		while (USHoliday.Dates.Contains(findDate)) findDate = findDate.AddDays(-1);
   	    	
	    	return findDate;
	    }

		// **********************   FindDay		*******************************************************
	    // ***  	Generalized function to find and return a DateTime for a given year, month, DayOfWeek 
	    // ***		and occurrence in the month.
	    // ***		
	    // ********************************************************************************************
	
	    public DateTime FindDay(int year, int month, DayOfWeek Day, int occurrence)
	    {
	
			if (haltProcessing) {
				Debug(" HALTED IN 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}");
				
			}
	    }
	    	    
		// **********************   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 " + 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(" ---------------------------------------------------------------------------------------------");
    
        }
        
        
        // **********************   IterateContracts		*******************************************************
	    // ***  	Generalized function to iterate through and print members of an IEnumerable of Contracts
	    // ***		This is used for debugging only
	    // ********************************************************************************************
	    public void IterateContracts(List<Option> thisOptionsList)
        {	
        	
        	int k = 1;
       		Option thisOption;
        	var enumerator = thisOptionsList.GetEnumerator();	
        	
        	Debug("  ||||||||||||||||||||||||||||||||   NEW OPTION CONTRACTS LIST  |||||||||||||||||||||||||||||||");
        	Debug("There are " + thisOptionsList.Count() + " contracts in this options list.");
        	
        	while (enumerator.MoveNext()) 
        	{
				thisOption = enumerator.Current;
				
				//Debug("Iterated  " + k + " times");
				//Debug("Option Chain: " + thisOption.ToString());
				
				//Debug(thisOption.StrikePrice + " " + thisOption.Expiry + " " + thisOption.Right + " " + thisOption.GetLastData());
				Debug(thisOption.StrikePrice + " " + thisOption.Expiry + " " + thisOption.Right + " BID: " + thisOption.BidPrice + " ASK: " + thisOption.AskPrice);
				k++;
        	}
            //Debug(" ---------------------------------------------------------------------------------------------");

        }
    
    
    	// **********************   Iterate Matrix		*******************************************************
	    // ***  	Generalized function to iterate through and print members of an IEnumerable of Contracts
	    // ***		This is used for debugging only
	    // ********************************************************************************************
	    public void IterateSSQRMatrix(List<SSQRColumn> thisMatrix)
        {	
        	
        	int k = 1;
    		SSQRColumn thisColumn;   		
        	var matrixEnum = thisMatrix.GetEnumerator();	
        	
        	Debug("  ||||||||||||||||||||||||||||||||   NEW OPTION SSQRMatrix  |||||||||||||||||||||||||||||||");
			Debug("There are " + thisMatrix.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
        	Debug(",Ticker,Stock Price,Ex-Date,# Dividends,Dividend,Dollars,Days In,Interest,PExpiry, CExpiry, PutStrike,PutASK,CallStrike,CallBid, atmStrike, atmCallAsk, PutDelta, CallDelta, NetOptions,Net Income,Haircut,ROC,Upside,Downside,ROR,CCOR, wingFactor, PutSymb, CallSymb");
	
        	while (matrixEnum.MoveNext()) 
        	{
				thisColumn = matrixEnum.Current;
				
				//Debug("Iterated  " + k + " times");
				Debug(thisColumn.description2);
				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
        	Debug(",Ticker,Stock Price,Ex-Date,# Dividends,Dividend,Dollars,Days In,Interest,PExpiry, CExpiry, PutStrike,PutASK,CallStrike,CallBid, atmStrike, atmCallAsk, PutDelta, CallDelta, NetOptions,Net Income,Haircut,ROC,Upside,Downside,ROR,CCOR, wingFactor, PutSymb, CallSymb");

        	foreach (SSQRColumn thisColumn in thisOrdMatrix) 
        	{
				//Debug("Iterated  " + k + " times");
				Debug(thisColumn.description2);
				//Debug("  ");
				k++;
				if (k == 21) 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;
        	}

        }
 
     }
    	
}
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;

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

		public void ExecuteTrade(Slice data, SSQRColumn bestSSQRColumn)
		{
	        thisCCOR = bestSSQRColumn.CCOR;
	        decimal maxWingFactor = 0;
	        decimal thisWingFactor = 0;
			decimal wingPremium = 0;
			decimal thisNetOptions = bestSSQRColumn.netOptions;
			
			if (haltProcessing) 
			{
				Debug("           HALTED IN ExecuteTheTrade() ");
			}

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

			if (goodThresh)
			{
				//sharesToBuy = Math.Round(stockDollarValue/stockPrice/100, 0) * 100;			// set in top of OnData()
				optionsToTrade = sharesToBuy/100;
				//callsToTrade = Decimal.Round(optionsToTrade * bestSSQRColumn.putPremium / bestSSQRColumn.callPremium);					/// legacy VCCPTS code

				//Debug(tradableColumn.ToString());
				tradablePut = bestSSQRColumn.putSymbol;
				tradableCall = bestSSQRColumn.callSymbol;
				tradableWCall = bestSSQRColumn.wCallSymbol;
				
				if (Securities[tradableCall].AskPrice + bestSSQRColumn.callStrike < stockPrice) // make sure that no one can buy the option for less than the stock
        		{
        			Debug($" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  EXERCISE PREVENTION FADE  FOR  {thisSymbol} @@@@@@@@@@@@");
        			Debug(" @@@@@@@@@@@@@@@@@@@  CALL ASK: " + Securities[tradableCall].AskPrice  + " Strike: " + bestSSQRColumn.callStrike + " Stock Price: " + stockPrice +" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 
	    			Debug(" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
    				return;   
    			}
    		
    			tradeRecCount = tradeRecCount + 1;				//  increment trade record count
    			collarIndex = collarIndex + 1;
    			doTheTrade = true;
				var stockTicket = MarketOrder(thisSymbol, sharesToBuy);	
				if (stockTicket.Status == OrderStatus.Filled)
        		{
            		didTheTrade = true;
            		Plot("Stock Chart", "Buys", stockTicket.AverageFillPrice + 5);
            		// make a new TradePerfRec
            		TradePerfRec thisNewCollar = new TradePerfRec();

            		thisNewCollar.strtngCndtn = "INITIAL COLLAR";
            		thisNewCollar.isOpen = true;
            		thisNewCollar.isInitializer = true;
            		thisNewCollar.tradeRecCount = tradeRecCount;
            		thisNewCollar.index = collarIndex;
					thisNewCollar.startDate = data.Time;
					thisNewCollar.expDate = bestSSQRColumn.optExpiry;
					thisNewCollar.thetaExpiration = bestSSQRColumn.callExpiry;
            		thisNewCollar.uSymbol = thisSymbol;
            		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 = thisROR;
            		thisNewCollar.ROC = thisROC;
            		thisNewCollar.CCOR = thisCCOR;
            		thisNewCollar.RORThresh = RORThresh;
            		thisNewCollar.ROCThresh = ROCThresh;
            		thisNewCollar.CCORThresh = CCORThresh;
            		//thisNewCollar.tradeCriteria = switchROC ? "ROC" : "ROR";
            		thisNewCollar.tradeCriteria = "Wing";
            		thisNewCollar.stockADX  = lastAdx;
            		thisNewCollar.stockADXR = lastAdxr;
            		thisNewCollar.stockOBV = lastObv;
            		//thisNewCollar.stockAD = lastAd;
            		//thisNewCollar.stockADOSC = lastAdOsc;
            		//thisNewCollar.stockSTO = lastSto;
            		thisNewCollar.stockVariance = lastVariance;
            		thisNewCollar.SSQRnetProfit = stockTicket.QuantityFilled * bestSSQRColumn.netIncome;

		    		Option logCorrespondingPut = AddCorrespondingPut(tradableCall); 			// Add the corresponding put here so system tracks its price for Ex-dividend approachment
		    		logCorrespondingPut = AddCorrespondingPut(bestSSQRColumn.wCallSymbol);	// Add the wing call corresponding put here so system tracks its price for Ex-Dividend approachment
	
					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 {
						var callTicket = MarketOrder(tradableCall, -optionsToTrade);
	    				if (callTicket.Status == OrderStatus.Filled)
	    				{
		    				thisNewCollar.cStartPrice = callTicket.AverageFillPrice;
		    				thisNewCollar.cQty = (int)callTicket.QuantityFilled;
		    			}
					}

					thisWingFactor = bestSSQRColumn.wingFactor;

	    			var putTicket = MarketOrder(tradablePut, (1 + thisWingFactor) * 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);
	    			Debug("-");
        		}		// marketOrder(thisSymbol) == filled
			}			// goodThresh is TRUE
			return;
		}

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

			if (doTracing) Debug(" C2 ** C2 ** STARTING CLOSE2ndTPR PROCESSING ** C2 ** C2 ");
			if (doTracing) Debug(" -- ");
			if (doTracing)	{
				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;
						Debug($" ||||  HOLDINGS: {security.Symbol} : {security.Holdings.Quantity} @ {security.BidPrice} by {security.AskPrice}");
					}
				}
			}

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

				//Plot("Stock Chart", "Sells", 40);				// plot in OnOrder() ??
			}
			
			doTheTrade = true;
			Debug(" C2 ** C2 ** C2 ** C2 ** KILLING 2nd TPR  ** C2 ** C2 ** C2 ** C2 ** C2 ** ");
			//Debug(" C2 ** Stock Price: " + stockPrice.ToString() + " Call Bid/Offer: " + closeRec.cSymbol.BidPrice.ToString() + "/" + closeRec.cSymbol.AskPrice.ToString());

			if  (closeRec.pStrike >= stockPrice)											/// ITM Put -- use limit order
			{
				limitPrice = closeRec.pStrike - stockPrice + 0.10M;
				closePutTicket = LimitOrder(closeRec.pSymbol, -closeRec.pQty, limitPrice); // sell the puts	

				OpenLimitOrder oLO = new OpenLimitOrder();
				oLO.oTicket = closePutTicket;
				oLO.tpr = closeRec;
				oLO.oRight = OptionRight.Put;
				oLOs.Add(oLO);
				
				Debug(" C2 **  LIMIT ORDER TO SELL " + closeRec.pQty.ToString() + " contracts of " + closeRec.pSymbol + " at " + limitPrice.ToString());
				Debug("-");
			
			} else {
				closePutTicket = MarketOrder(closeRec.pSymbol, -closeRec.pQty); // sell the puts	
				Debug(" C2 **  MARKET ORDER TO SELL " + closeRec.pQty.ToString() + " contracts of " + closeRec.pSymbol + " at the market." );
				Debug("-");
			}
			
			if (closePutTicket.Status == OrderStatus.Filled)
			{
				closeRec.pEndPrice = closePutTicket.AverageFillPrice;
			}
			closeRec.reasonForClose = reason;
			closeRec.endDate = closeDate;			// set the end date of this collar
			Debug(" C2 ** C2 ** C2 ** C2 ** CLOSED 2nd TPR  ** C2 ** C2 ** C2 ** C2 ** C2 ** ");
			Debug("-");
		}
	
		///////////////////////////////////////////////////////////////////////////////////
		//								KillTheCollar
		////////////////////////////////////////////////////////////////////////////////////

		public void KillTheCollar(TradePerfRec killRec, DateTime killDate, string reason)
		{
			decimal limitPrice = 0;
			decimal currUPrice = Securities[killRec.uSymbol].Price;
			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 sellPnL = 0;
			//decimal exrcsPnL = 0;

			if (doTracing) Debug($" KK ** KK ** STARTING KILLTHECOLLAR PROCESSING  FOR  {thisSymbol} ** KK ** KK ");
			if (doTracing) Debug(" -- ");
			if (doTracing)	{
				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;
						Debug($" ||||  HOLDINGS: {security.Symbol} : {security.Holdings.Quantity} @ {security.BidPrice} by {security.AskPrice}");
					}
				}
				Debug($" ||||  SELL OPTS  P&L: " + String.Format("{0:0.00}", currSellPnL));
				Debug($" ||||  Exrcs PUT  P&L: " + String.Format("{0:0.00}", currExrcsPutPnL));
				Debug($" ||||  Exrcs CALL P&L: " + String.Format("{0:0.00}", currExrcsCallPnL));
				
			}

			if (haltProcessing) 
			{
				Debug("           HALTED IN KILLTHECOLLAR ");
			}

			doTheTrade = true;
			
			//   determine if this is an ITM call or ITM put and within 1 day of expiry
			
			if (killRec.pSymbol != null && stockPrice <= putStrike && expireDateDeltaP.Days <= 1) {
				//  determine if it's more expensive to sell or exercise ***** remember, killRec.cQty is negative for collars (sold calls)
	
				if (currExrcsPutPnL > currSellPnL) {					// for an ITM PUT, both costs should be negative 
					if (doTracing) Debug($" KK ** KK ** EXERCISING PUTS AND CALLS THETA IN KILLTHECOLLAR  FOR  {thisSymbol} ** KK ** KK ");
					if (killRec.cSymbol != null) {			// Exercise the PUTs.   Let longer expiry calls ride to attempt theta decay
						//var shrtCall = (Option)Securities[killRec.cSymbol];
						//TimeSpan daysToCallExpiry = shrtCall.Expiry.Subtract(killDate);
    					/*if (daysToCallExpiry.Days > 10 ) {
							Debug(" 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 (doTracing) Debug(" KK ** KK ** BUYING BACK SHORT CALLS IN KILLTHECOLLAR ** KK ** KK ");
						
						closeCallTicket = MarketOrder(killRec.cSymbol, -killRec.cQty);  // buy the calls	
						Debug(" KK **  MARKET ORDER TO BUY " + killRec.cQty.ToString() + " contracts of " + killRec.cSymbol + " at the market.");
						Debug("-");

						if (closeCallTicket.Status == OrderStatus.Filled)
						{
							killRec.cEndPrice = closeCallTicket.AverageFillPrice;
						}
    				}
					if (doTracing) Debug(" ------- ");
					if (doTracing) Debug(" KK ** KK ** EXERCISING PUTS IN KILLTHECOLLAR ** KK ** KK ");
						
					closePutTicket = ExerciseOption(killRec.pSymbol, killRec.pQty);					/// underlying and calls will be closed in onOrder() event
					killRec.grossPnL = currExrcsPutPnL;							// log the PnL used in runtime decision
					
					potentialCollars.Clear();
					bestSSQRColumn = new SSQRColumn();
					return;
				}

				if (doTracing) Debug(" KK ** KK ** CLOSE POSITIONS IN KILLTHECOLLAR ** KK ** TT ");
				goto noExercise;

			}
			
			if (killRec.cSymbol != null && stockPrice >= callStrike && expireDateDeltaC.Days <= 1) {
				if (doTracing) Debug(" KK ** KK ** EXERCISING CALLS AND SELLING BACK PUTS IN KILLTHECOLLAR ** KK ** TT ");
				if (currExrcsCallPnL > currSellPnL) {					// for an ITM CALL, both costs should be positive
					if (doTracing) Debug(" KK ** KK ** EXIT KILLTHECOLLAR AND AWAIT CALL EXERCISE** KK ** TT ");
					killRec.grossPnL = currExrcsCallPnL;							// log the PnL used in runtime decision
					return;													// get out of the OnData() and await LEAN-driven call exercise
				}
				if (doTracing) Debug(" KK ** KK ** CLOSING POSITIONS IN KILLTHECOLLAR ** KK ** TT ");
			}

noExercise:
			//if OTM or it's less costly to execute orders, then do so here.
			
			var stockTicket = MarketOrder(killRec.uSymbol, -killRec.uQty);	// sell the stock
			Debug(" KK **  MARKET ORDER TO SELL " + killRec.uQty.ToString() + " shares of " + killRec.uSymbol + " at the market.");
			
			if (stockTicket.Status == OrderStatus.Filled)
			{
				Debug(" KK ** KK ** UPDATING U END PRICE ** KK ** KK");
				killRec.isOpen = false;
				killRec.uEndPrice = stockTicket.AverageFillPrice;
				Plot("Stock Chart", "Sells", stockTicket.AverageFillPrice + 1);
				tradeRecCount = 0;				//  reset trade record count

				//Plot("Stock Chart", "Sells", 40);				// plot in OnOrder() ??
			}
			
			doTheTrade = true;
			Debug(" KK ** KK ** KK ** KK ** KILLING  COLLAR ** KK ** KK ** KK ** KK ** KK ** ");
			//Debug(" KK ** Stock Price: " + stockPrice.ToString() + " Call Bid/Offer: " + killRec.cSymbol.BidPrice.ToString() + "/" + killRec.cSymbol.AskPrice.ToString());

			
			if (killRec.cSymbol != null) {			// Exercise the PUTs.   Buy back any calls if possible
				var shrtCall = (Option)Securities[killRec.cSymbol];
				TimeSpan daysToCallExpiry = shrtCall.Expiry.Subtract(killDate);
    			/*if (daysToCallExpiry.Days > 10 ) {
					Debug(" 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.cStrike <= stockPrice) {											/// ITM Call -- use limit order
		
					limitPrice = stockPrice - killRec.cStrike + 0.10M;
					
					closeCallTicket = LimitOrder(killRec.cSymbol, -killRec.cQty, limitPrice);
					OpenLimitOrder oLO = new OpenLimitOrder();
					oLO.oTicket = closeCallTicket;
					oLO.tpr = killRec;
					oLO.oRight = OptionRight.Call;
					oLOs.Add(oLO);
					Debug(" KK **  LIMIT ORDER TO BUY TO CLOSE SHORT CALL " + killRec.cQty.ToString() + " contracts of " + killRec.cSymbol + " at " + limitPrice.ToString());
				} else {
					closeCallTicket = MarketOrder(killRec.cSymbol, -killRec.cQty);  // buy the calls	
					Debug(" KK **  MARKET ORDER TO BUY TO CLOSE SHORT CALL" + killRec.cQty.ToString() + " contracts of " + killRec.cSymbol + " at the market.");
					if (closeCallTicket.Status == OrderStatus.Filled)
					{
						killRec.cEndPrice = closeCallTicket.AverageFillPrice;
					}

				}
			}
			
			Debug("---------------------------------------");

			
			if  (killRec.pStrike >= stockPrice)											/// ITM Put -- use limit order
			{
				limitPrice = killRec.pStrike - stockPrice + 0.10M;
				closePutTicket = LimitOrder(killRec.pSymbol, -killRec.pQty, limitPrice); // sell the puts	

				OpenLimitOrder oLO = new OpenLimitOrder();
				oLO.oTicket = closePutTicket;
				oLO.tpr = killRec;
				oLO.oRight = OptionRight.Put;
				oLOs.Add(oLO);
				
				Debug(" KK **  LIMIT ORDER TO SELL TO CLOSE " + killRec.pQty.ToString() + " contracts of " + killRec.pSymbol + " at " + limitPrice.ToString());
				Debug("-");
			
			} else {
				closePutTicket = MarketOrder(killRec.pSymbol, -killRec.pQty); // sell the puts	
				Debug(" KK **  MARKET ORDER TO SELL TO CLOSE " + killRec.pQty.ToString() + " contracts of " + killRec.pSymbol + " at the market." );
				Debug("-");
			}
			
			if (closePutTicket.Status == OrderStatus.Filled)
			{
				killRec.pEndPrice = closePutTicket.AverageFillPrice;
				Debug(" KK ** UPDATING PUT PRICE TO " + killRec.pEndPrice + " ** KK ** KK");
			}
			
			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;
					Debug(" 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 puts	
	
					OpenLimitOrder oLO = new OpenLimitOrder();
					oLO.oTicket = closeWCallTicket;
					oLO.tpr = killRec;
					oLO.oRight = OptionRight.Call;
					oLO.isWingCall = true;
					oLOs.Add(oLO);
					
					Debug("-");
				
				} else {
					closeWCallTicket = MarketOrder(killRec.wcSymbol, -killRec.wcQty); // sell the puts	
					Debug(" KK **  LIMIT ORDER TO SELL TO CLOSE WING " + killRec.wcQty.ToString() + " contracts of " + killRec.wcSymbol + " at " + limitPrice.ToString());
					Debug("-");
				}
				
				if (closeWCallTicket.Status == OrderStatus.Filled)
				{
					killRec.wcEndPrice = closePutTicket.AverageFillPrice;
					Debug(" KK ** UPDATING WING END PRICE TO " + killRec.wcEndPrice + " ** KK ** KK");
				}
			}
			
			
			killRec.reasonForClose = reason;
			killRec.endDate = killDate;			// set the end date of this collar
			killRec.grossPnL = currSellPnL;		// for logging and analysis of runtime conditions
			
			Debug("-");
		}

		///////////////////////////////////////////////////////////////////////////////////
		//								RollPut
		////////////////////////////////////////////////////////////////////////////////////
		
		public void RollPut(Slice slcData, DateTime nExDvDt, TradePerfRec oldTPR, IEnumerable <Symbol> undrOptsSymbols, decimal sPrice, decimal incrAmt, string reason, bool forceAction){
			int rollQty = 0;						// change in qty, difference between total stock and covered stock = uncovered stock == amount to roll up.
			int findYear = slcData.Time.Year;
			int findMonth = slcData.Time.Month;
			PutSpread bestPutSpread;

			OrderTicket closePutTicket;					// used to close the open puts
			OrderTicket openPutTicket;					// used to open (roll up) new puts
			
			if (haltProcessing) {
				Debug(" RP ** RP ** RP **	HALTED IN ROLLPUT RR ** RR ** RR **");
			}
			
			if (doTracing) Debug(" RP ** RP ** STARTING ROLL2NDPUT PROCESSING ** RP ** RP ");
			if (doTracing) Debug(" -- ");
			if (doTracing)	{
				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;
						Debug($" ||||  HOLDINGS: {security.Symbol} : {security.Holdings.Quantity} @ {security.BidPrice} by {security.AskPrice}");
					}
				
				}
				Debug($" ||||  SELL OPTS  P&L: " + String.Format("{0:0.00}", currSellPnL));
				Debug($" ||||  Exrcs PUT  P&L: " + String.Format("{0:0.00}", currExrcsPutPnL));
				Debug($" ||||  Exrcs CALL P&L: " + String.Format("{0:0.00}", currExrcsCallPnL));

			}
			// 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
			Dictionary<int, DateTime> expiries = GetOptionExpiries(slcData.Time, nExDvDt, thisMonthExpiry, false);
	
			List<PutSpread> pSpreads = AssemblePutSpreads(slcData, expiries, oldTPR, undrOptsSymbols, sPrice, incrAmt);

			
			if (pSpreads == null | pSpreads.Count() == 0) {
				if (doTracing) Debug(" RP RP RP RP FAILED ROLLPUT -- NO PSPREADS");
				if (doTracing) Debug(" - ");
				return;				// loop around and try again
			}
			
			bestPutSpread = GetBestPutSpread(pSpreads);
			
			if (bestPutSpread == null) {

				// if (haltProcessing) {					/// 2021-02-17   removed this
				//if (doTracing) Debug("			HALTED IN ROLLPUT --NULL BESTPUTSPREAD -- ITERATE");
				
				if (doTracing) Debug(" RP 2nd TPR PROCESSING CANNOT ROLL " + thisSymbol.ToString() + " CANNOT GET bestPutSpread -- FORCE PUT EXERCISE OOOOOOOOOOO");					// EXERCISE THE PUT removing PUTs and STOCK. Buy back calls in OnOrder()
				if (doTracing) Debug(" RP FORCE PUT EXERCISE OR CLOSE ON PUT: " + secondLongPutSymbol + " -- OOOOOOOOOOO OOOOOOOO OOOOOOOOOOO");					// EXERCISE THE PUT removing PUTs and STOCK. Buy back calls in OnOrder()
		
				if (doTracing){		
					var orderedPSpreads = pSpreads.Where(s => s.netIncome + s.netOptions > 0 ).OrderByDescending( s => (s.netIncome + s.netOptions)/Math.Abs(s.stockPrice - s.newPutStrike));
					//var orderedPSpreads = pSpreads.OrderByDescending(s=>s.netIncome + s.netOptions);
					IterateOrderedPutSpreadList(orderedPSpreads);
				}

				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 (doTracing) Debug(" **************  END 2nd TPR ITM PUT CALC ****************");
					if (doTracing) Debug("-");
				}
				Plot("Stock Chart", "PTSs", divPlotValue);
				return;									// loop around and try again
			}
			
			if (doTracing){		
				var orderedPSpreads = pSpreads.OrderByDescending(s=>s.netIncome + s.netOptions);
				IterateOrderedPutSpreadList(orderedPSpreads);
			}
			/// Roll up the collar
			if (!oldTPR.isSecondary && oldTPR.pQty > oldTPR.cQty) {
			
				rollQty = oldTPR.pQty + oldTPR.cQty;								// call Qty is stored as negative value.  this is equivalent: abs(p)-abs(c)
				
				Debug(" RP ** RP ** RP ** RP ** ROLLING UP PUTS  ** RP ** RP ** RP ** RP ** RP ** ");
				doTheTrade = true;
				
				Debug(" RP **  MARKET ORDER TO SELL " + rollQty + " contracts of " + oldTPR.pSymbol + " at market");
				closePutTicket = MarketOrder(oldTPR.pSymbol, -rollQty); 			// sell the puts	
		
				Debug(" RP **  MARKET ORDER TO BUY " + rollQty + " contracts of " + bestPutSpread.newPutSymb + " at market");
				openPutTicket = MarketOrder(bestPutSpread.newPutSymb, rollQty);	// buy the higher puts
				
				// first adjust the old tradePerfRec to decrement pQty and uQty.  It remains open to be processed for the remaining covered, collared stock.
				
				oldTPR.pQty = oldTPR.pQty - rollQty;								// decrement oldTPR to fully covered collar.  Leave it open for future roll processing
				oldTPR.uQty = oldTPR.uQty - (100 * rollQty);
				
				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)
				
				newTPR1.uSymbol = newTPR2.uSymbol = oldTPR.uSymbol;					// newTPR1 for the uncovered synthetic call (put + stock) portion of the original collar
				newTPR1.index = newTPR2.index = oldTPR.index;						// maintain collarIndex throughout the entire sequence of collars and synthCalls
				newTPR1.uQty = rollQty * 100;										// log the starting and ending values and close the TradePerfRec
				newTPR1.uStartPrice = oldTPR.uStartPrice;
				newTPR1.uEndPrice = sPrice;
				newTPR1.pSymbol = oldTPR.pSymbol;
				newTPR1.pStrike = oldTPR.pStrike;
				newTPR1.expDate = oldTPR.expDate;
				newTPR1.pQty = rollQty;
				newTPR1.pStartPrice = oldTPR.pStartPrice;
				newTPR1.startDate = oldTPR.startDate;
				newTPR1.endDate = slcData.Time;
				newTPR1.isOpen = false;
				newTPR1.isInitializer = false;
				newTPR1.isSecondary = true;
				newTPR1.numDividends = oldTPR.numDividends;
				newTPR1.divIncome = oldTPR.divIncome;
				newTPR1.tradeRecCount = oldTPR.tradeRecCount;
				newTPR1.ROR = oldTPR.ROR;
				newTPR1.ROC = oldTPR.ROC;
				newTPR1.CCOR = oldTPR.CCOR;
				newTPR1.tradeCriteria = oldTPR.tradeCriteria;
				newTPR1.strtngCndtn = "OLD SYNTH CALL --  MAIN COLLAR SPLIT ON APPRECIATION";
				newTPR1.reasonForClose = "P ROLL OLD FRAGMENT SPLIT-SOLD PUTS SPrice @ " + String.Format("{0:0.00}",sPrice) + " up " + String.Format("{0:0.00}", incrAmt) ;
				
				if (closePutTicket.Status == OrderStatus.Filled)
				{
					newTPR1.pEndPrice = closePutTicket.AverageFillPrice;
				}
				
				newTPR2.uQty = rollQty * 100;									//	newTPR2 for the new uncovered synthetic call (put + stock)
				newTPR2.uStartPrice = sPrice;									//  log the starting values for underlying and puts.
				newTPR2.pSymbol = bestPutSpread.newPutSymb;
				newTPR2.pStrike = bestPutSpread.newPutStrike;
				newTPR2.pQty = rollQty;
				newTPR2.expDate = bestPutSpread.putExpiry;
				newTPR2.startDate = slcData.Time;
				newTPR2.isOpen = true;
				newTPR2.isInitializer = true;									// mark the first synthCall TPR as initializer
				newTPR2.isSecondary = true;
				//newTPR2.				
				newTPR2.tradeRecCount = 1;
				newTPR2.ROR = oldTPR.ROR;
				newTPR2.ROC = oldTPR.ROC;
				newTPR2.CCOR = oldTPR.CCOR;
				newTPR2.tradeCriteria = oldTPR.tradeCriteria;
				newTPR2.strtngCndtn = "NEW SYNTH CALL -- COLLAR SPLIT ON APPRECIATION";
				
				if (openPutTicket.Status == OrderStatus.Filled)
				{
					newTPR2.pStartPrice = openPutTicket.AverageFillPrice;
				}
				
				tradeRecs.Add(newTPR1);
				tradeRecs.Add(newTPR2);
				Plot("Stock Chart", "PTSs", divPlotValue);
				if (doTracing) Debug(" RP ** RP ** END OF ROLL PUT FROM PRIMARY TPR ** RP ** RP ** ");
			} else if (oldTPR.isSecondary) {
				// 
				rollQty = oldTPR.pQty;											// call Qty is stored as negative value.  this is equivalent: abs(p)-abs(c)
				
				Debug(" RP ** RP ** RP ** RP ** ROLLING UP PUTS  ** RP ** RP ** RP ** RP ** RP ** ");
				doTheTrade = true;
				
				Debug(" RP **  MARKET ORDER TO SELL " + rollQty + " contracts of " + oldTPR.pSymbol + " at market");
				closePutTicket = MarketOrder(oldTPR.pSymbol, -rollQty); 			// sell the puts	
		
				Debug(" RP **  MARKET ORDER TO BUY " + rollQty + " contracts of " + bestPutSpread.newPutSymb + " at market");
				openPutTicket = MarketOrder(bestPutSpread.newPutSymb, rollQty);	// buy the higher puts
				
				// first adjust the old tradePerfRec to decrement pQty and uQty.  It remains open to be processed for the remaining covered, collared stock.
				//oldTPR.pQty = oldTPR.pQty - rollQty;	// whether this is the first secondary TPR, or a subsequent, 
				//oldTPR.uQty = 100M * rollQty;
				
				TradePerfRec newTPR1 = new TradePerfRec();							// create a tradePerfRec #1 for the puts sold, solely to log their P/L.
				//TradePerfRec newTPR2 = new TradePerfRec();							// create a TradePerfRec #2 for the new Synthetic Call (stock-covered puts)

				foreach (var field in typeof(TradePerfRec).GetFields())				// copy oldTPR to newTPR1
				{
					field.SetValue(newTPR1, field.GetValue(oldTPR));
				}
				
				oldTPR.uEndPrice = sPrice;
				if (closePutTicket.Status == OrderStatus.Filled)
				{
					oldTPR.pEndPrice = closePutTicket.AverageFillPrice;
				}
				oldTPR.endDate = slcData.Time;
				oldTPR.isOpen = false;
				oldTPR.grossPnL = closePutTicket.AverageFillPrice - oldTPR.pStartPrice;
				
				oldTPR.reasonForClose = "P ROLLUP " + oldTPR.uSymbol + " IS " + String.Format("{0:0.00}",sPrice) + "WHICH STARTED @ " + String.Format("{0:0.00}", oldTPR.uStartPrice);
				

				newTPR1.uStartPrice = sPrice;									// set the newTPR.uPrice to 0-delta current sPrice
				if (openPutTicket.Status == OrderStatus.Filled)
				{
					newTPR1.pStartPrice = openPutTicket.AverageFillPrice;
				}
				newTPR1.pSymbol = bestPutSpread.newPutSymb;						// set the pSymbol to the bestPutSpread put symbol
				newTPR1.pStrike = bestPutSpread.newPutStrike;
				newTPR1.expDate = bestPutSpread.putExpiry;
				newTPR1.startDate = slcData.Time;
				newTPR1.numDividends = 0;
				newTPR1.divIncome = 0;
				
				newTPR1.isInitializer = false;									// tied to 1st synthCall TPR
				newTPR1.isSecondary = true;
				newTPR1.tradeRecCount = oldTPR.tradeRecCount + 1;				// increment this iteration of TPR
				newTPR1.strtngCndtn = reason;
				
				//newTPR1.reasonForClose = "";
				//newTPR1.ROR = oldTPR.ROR;
				//newTPR1.ROC = oldTPR.ROC;
				//newTPR1.CCOR = oldTPR.CCOR;
				//newTPR1.tradeCriteria = oldTPR.tradeCriteria;
				Plot("Stock Chart", "PTSs", divPlotValue);
				tradeRecs.Add(newTPR1);

				if (doTracing) Debug(" RP ** RP ** END OF ROLL PUT FROM SECONDARY TPR ** RP ** RP ** ");

			}			
		}


		///////////////////////////////////////////////////////////////////////////////////
		//
		//								RollTheCollar
		//
		////////////////////////////////////////////////////////////////////////////////////
		public void RollTheCollar(Slice data, Symbol thisSymbol, string reason)
		{

	        thisCCOR = bestSSQRColumn.CCOR;
			decimal thisNetOptions = bestSSQRColumn.netOptions;

			decimal limitPrice = 0;
			decimal maxWingFactor = 0;
			decimal thisWingFactor = 0;
			decimal wingPremium = 0;
			OrderTicket closeCallTicket;
			OrderTicket closePutTicket;
			OrderTicket closeWCallTicket;
			
			if (haltProcessing) 
			{
				Debug("                 HALTED IN ROLL ");
			}
			
			oldRollDate = data.Time.Date;
			decimal stockPrice = data[thisSymbol].Price;
			
			Plot("Stock Chart", "Rolls", stockPrice + 5);
			
			// find the trade performance record for this collar and save its index for updating
			var oldTradeRec = tradeRecs.Where(t => t.isOpen && !t.isSecondary &&  t.uSymbol.Equals(thisSymbol)).FirstOrDefault();
        	//int oldTradeRecIndx = tradeRecs.IndexOf(oldTradeRec);
        	
        	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
			Debug(" ROLLING ** STARTING ** ROLLING ** STARTING ** ROLLING  ** STARTING ** ROLLING ** STARTING ** ROLLING ** STARTING ** ");
			doTheTrade = true;
			
			if (doTracing) Debug(" -- ");
			if (doTracing)	{
				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;
						Debug($" ||||  HOLDINGS: {security.Symbol} : {security.Holdings.Quantity} @ {security.BidPrice} by {security.AskPrice}");
					}
				
				}
				Debug($" ||||  SELL OPTS  P&L: " + String.Format("{0:0.00}", currSellPnL));
				Debug($" ||||  Exrcs PUT  P&L: " + String.Format("{0:0.00}", currExrcsPutPnL));
				Debug($" ||||  Exrcs CALL P&L: " + String.Format("{0:0.00}", currExrcsCallPnL));
			}

			
			if  (oldTradeRec.pStrike >= stockPrice)											/// ITM Put -- use limit order to close
			{
				limitPrice = oldTradeRec.pStrike - stockPrice + 0.10M;
				Debug(" ROLLING **  LIMIT ORDER TO SELL " + oldTradeRec.pQty.ToString() + " contracts of " + oldTradeRec.pSymbol + " at " + limitPrice.ToString());
				closePutTicket = LimitOrder(oldTradeRec.pSymbol, -oldTradeRec.pQty, limitPrice); // sell the puts	

				OpenLimitOrder oLO = new OpenLimitOrder();
				oLO.oTicket = closePutTicket;
				oLO.tpr = oldTradeRec;
				oLO.oRight = OptionRight.Put;
				oLOs.Add(oLO);
				//if (closePutTicket.Status == OrderStatus.Submitted) oldTradeRec.pEndPrice = limitPrice;
			
			} else {
				Debug(" ROLLING **  MARKET ORDER TO SELL TO CLOSE " + oldTradeRec.pQty.ToString() + " contracts of " + oldTradeRec.pSymbol + " at market");
				closePutTicket = MarketOrder(oldTradeRec.pSymbol, -oldTradeRec.pQty); // sell the puts	
			}

			if (closePutTicket.Status == OrderStatus.Filled)
			{
				oldTradeRec.pEndPrice = closePutTicket.AverageFillPrice;
				Debug(" ROLLING **  UPDATING PUT " + oldTradeRec.pSymbol + " END PRICE @ " + oldTradeRec.pEndPrice );
			}

			Debug("-");

			// 2nd, buy back the long call
			doTheTrade = true;
			
			if  (oldTradeRec.cStrike <= stockPrice)											/// ITM Call -- use limit order
			{																				/// call QTY should be negative from the opening short trade
				limitPrice = stockPrice - oldTradeRec.cStrike + 0.10M;
				Debug(" ROLLING **  LIMIT ORDER TO BUY TO CLOSE " + oldTradeRec.cQty.ToString() + " contracts of " + oldTradeRec.cSymbol + " at " + limitPrice.ToString());
				closeCallTicket = LimitOrder(oldTradeRec.cSymbol, -oldTradeRec.cQty, limitPrice);
				//if (closeCallTicket.Status == OrderStatus.Submitted) oldTradeRec.cEndPrice = limitPrice;

				OpenLimitOrder oLO = new OpenLimitOrder();
				oLO.oTicket = closeCallTicket;
				oLO.tpr = oldTradeRec;
				oLO.oRight = OptionRight.Call;
				oLOs.Add(oLO);
	
			} else {
				Debug(" ROLLING **  MARKET ORDER TO BUY TO CLOSE " + oldTradeRec.cQty.ToString() + " contracts of " + oldTradeRec.cSymbol + " at market");
				closeCallTicket = MarketOrder(oldTradeRec.cSymbol, -oldTradeRec.cQty);  // buy the calls	
				Debug(" ROLLING **  UPDATING CALL " + oldTradeRec.cSymbol + "END PRICE @ " + oldTradeRec.cEndPrice );
			}

			Debug("-");

			if (closeCallTicket.Status == OrderStatus.Filled)
			{
				oldTradeRec.cEndPrice = closeCallTicket.AverageFillPrice;
				Debug(" ROLLING **  UPDATING CALL END PRICE @ " + oldTradeRec.cEndPrice );
			}
			
			// 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;
					Debug(" ROLLING **  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 {
					Debug(" ROLLING **  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	
					
				}
	
				Debug("-");
	
				if (closeCallTicket.Status == OrderStatus.Filled)
				{
					oldTradeRec.wcEndPrice = closeWCallTicket.AverageFillPrice;
					Debug(" ROLLING **  UPDATING WING CALL " + oldTradeRec.wcSymbol + "END PRICE @ " + oldTradeRec.wcEndPrice );
				}
			}
			//  Keep the stock, but close this trade performance record.
			
			
			Debug(" ROLLING ** ROLLING ** ROLLING ** ROLLING ** ROLLING ** SELL NEW COLLAR ** ROLLING ** ROLLING ** ROLLING ** ROLLING ** ROLLING ** ");
			
			
			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 = thisSymbol;					// 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 = "MAIN COLLAR ROLL / " + reason;
			thisNewTPRec.index = oldTradeRec.index;				// maintain the collarIndex through the entire sequence of collars
			thisNewTPRec.tradeRecCount = tradeRecCount;			// count the trades
			thisNewTPRec.startDate = data.Time;					// set the start date
			thisNewTPRec.pStrike = bestSSQRColumn.putStrike;
            thisNewTPRec.cStrike = bestSSQRColumn.callStrike;
            thisNewTPRec.wcStrike = bestSSQRColumn.wCallStrike;
			thisNewTPRec.expDate = bestSSQRColumn.optExpiry;			// 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 = "Wing";
    		thisNewTPRec.stockADX  = lastAdx;
    		thisNewTPRec.stockADXR = lastAdxr;
    		thisNewTPRec.stockOBV = lastObv;
    		//thisNewTPRec.stockAD = lastAd;
    		//thisNewTPRec.stockADOSC = lastAdOsc;
    		//thisNewTPRec.stockSTO = lastSto;
    		thisNewTPRec.stockVariance = lastVariance;

			//Debug(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
			
    		Option logCorrespondingPut = AddCorrespondingPut(tradableCall);   // Add the corresponding put here so system tracks its price for Ex-dividend approachment
			logCorrespondingPut = AddCorrespondingPut(bestSSQRColumn.wCallSymbol);	// Add the wing call corresponding put here so system tracks its price for Ex-Dividend approachment

			// 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;
			
			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

			doTheTrade = true;
			Debug(" ROLLING **  EXECUTING PUT BUY MARKET ORDER TO OPEN " + ((1 + thisWingFactor) * optionsToTrade) + " contracts of " + tradablePut );
			var putTicket = MarketOrder(tradablePut, (1 + thisWingFactor) * optionsToTrade);
			if (putTicket.Status == OrderStatus.Filled)
			{
				thisNewTPRec.pSymbol = tradablePut;
				thisNewTPRec.pStartPrice = putTicket.AverageFillPrice;
				thisNewTPRec.pQty = (int)putTicket.QuantityFilled;
				Debug(" ROLLING **  UPDATING PUT START PRICE TO " + thisNewTPRec.pStartPrice + " FOR " + thisNewTPRec.pQty + " CONTRACTS" );
			}
		
			doTheTrade = true;
			Debug(" ROLLING **  EXECUTING CALL SELL MARKET ORDER TO OPEN " + optionsToTrade + " contracts of " + tradableCall );
			var callTicket = MarketOrder(tradableCall, -optionsToTrade);
			//var callTicket = MarketOrder(tradableCall, -callsToTrade);
			if (callTicket.Status == OrderStatus.Filled)
			{
				thisNewTPRec.cSymbol = tradableCall;
				thisNewTPRec.cStartPrice = callTicket.AverageFillPrice;
				thisNewTPRec.cQty = (int)callTicket.QuantityFilled;
				Debug(" ROLLING **  UPDATING SHORT CALL START PRICE TO " + thisNewTPRec.cStartPrice + " FOR " + thisNewTPRec.cQty + " CONTRACTS" );
			}
	
			doTheTrade = true;
			if (thisWingFactor > 0) {
				Debug(" ROLLING **  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;
					Debug(" ROLLING **  UPDATING WING CALL START PRICE TO " + thisNewTPRec.wcStartPrice + " FOR " + thisNewTPRec.wcQty + " CONTRACTS" );
				} else {
					Debug(" ROLLING ** WING FACTOR IS 0 -- NO WINGS ADDED");
				}
			}	

			didTheTrade = true;
			/// Roll is done.    save the new trade performance record
			//	IterateTradeRecord(thisNewTPRec);
			tradeRecs.Add(thisNewTPRec);
		}

		///////////////////////////////////////////////////////////////////////////////////
		//
		//								GetPotentialCollars
		//
		////////////////////////////////////////////////////////////////////////////////////

		public List<SSQRColumn> GetPotentialCollars(Slice thisSlice, Symbol thisStock, DateTime nextExDivDate)
        {
			if (haltProcessing)
			{
				Debug("  @@@@@@   HALTED IN GetPotentialCollars");
			}
			// First get the underlying stock price in this Slice
			decimal stockPrice = thisSlice[thisStock].Price;

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

			if (allUnderlyingOptionsSymbols.Count() == 0)               // missing data at this time
			{
				Debug(" DDDDDDDDDDDDDDDDDDDDD   Missing Data at " + thisSlice.Time + " no options for " + thisStock);
				List<SSQRColumn> blankCollarsList = new List<SSQRColumn>();
				return blankCollarsList;
			}

			int findYear = Time.Year;
			int findMonth = 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
			Dictionary<int, DateTime> expiries = GetOptionExpiries(thisSlice.Time, nextExDivDate, thisMonthExpiry, true);

			// now assemble the SSQR matrix using the expiries dictionary and the contracts lists
			List<SSQRColumn> ssqrMatrix = assembleSSQRMatrix(expiries, allUnderlyingOptionsSymbols, stockPrice, stockDividendAmount, thisSlice.Time, nextExDivDate);

			return ssqrMatrix;

		}

		///////////////////////////////////////////////////////////////////////////////////
		//
		//								GetBestSSQRFromPotentialCollars
		//
		////////////////////////////////////////////////////////////////////////////////////

		public SSQRColumn GetBestSSQRFromPotentialCollars(Slice thisSlice, Symbol thisStock, DateTime nextExDivDate, List<SSQRColumn> passedMatrix)
		{
			OptionChain ssqrPutChain;       // instantiate an OptionChain var for updating SSQRMatrix with slice data
			OptionChain ssqrCallChain;      // 
			Symbol ssqrPutSymbol;           // instantiate a Symbol var for updating SSQRMatrix with slice Data
			Symbol ssqrCallSymbol;          //

	    	if (haltProcessing)
    	    {
		    	Debug("  @@@@@@   HALTED IN GetBestSSQRFromPotentialCollars");
	    	}

			/*foreach (SSQRColumn ssqrC in passedMatrix) /// loop through the SSQRMatris to update the deltas and open interest
	        {
	        	ssqrPutSymbol = ssqrC.putSymbol;
	        	ssqrCallSymbol = ssqrC.callSymbol;
	        	if (thisSlice.OptionChains.TryGetValue(ssqrPutSymbol, out ssqrPutChain))
	        	{
					Debug(" HEY THE ssqrPutChain count is " + ssqrPutChain + " AT " + thisSlice.Time + " in FROM COLLARS.");
	        	} else {
	        		Debug("  HEY NO OPTIONS IN THIS SLICE in FROM COLLARS" + thisSlice.Time);
	        	}
	        	
	        	if (thisSlice.OptionChains.TryGetValue(ssqrCallSymbol, out ssqrCallChain))
	        	{
					// Moved this code to Main.cs ***  ssqrCallChain will not be included in the same Slice 
					// where the options contracts are added.
	        	}
	        }
	        */
			
    		// Get the SSQRColumn with the best reward to risk
	    	SSQRColumn bestTradableColumn = new SSQRColumn();
	    	if (passedMatrix.Count == 0) return bestTradableColumn;		/// found it's possible to have no SSQRs, if so, pass the empty/null SSQRColumn to calling routine

			var qualifyingCollars = passedMatrix.Where(s=>s.putPremium!=0 & s.putPremium<=s.callPremium).Count();
			
			if (qualifyingCollars == 0) return bestTradableColumn;
			
			// bestTradableColumn = passedMatrix.OrderByDescending(p => p.CCOR).FirstOrDefault();
			bestTradableColumn = passedMatrix.OrderBy(bTC => bTC.CCOR).FirstOrDefault();   /// 2021-03-21 -- changed from OrderedByDescending ..... using downsideRisk/upsidePotential

			return bestTradableColumn;
		}



    }
}
using QuantConnect.Securities.Option;
namespace QuantConnect.Algorithm.CSharp {
	public partial class CollarAlgorithm : QCAlgorithm
	{
		public Dictionary<decimal, decimal> InitializeHaircuts()
		{
			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;
			
		}
	}
		
}
using QuantConnect.Securities.Option;
using Newtonsoft.Json;

namespace QuantConnect.Algorithm.CSharp {
	
	public partial class CollarAlgorithm : QCAlgorithm
	{
		public class optGrksRec {
			//Debug(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 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;						// 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 int uQty;						// 23 number of underlying shares
			public int pQty;						// 24 number of put contracts
			public int cQty;						// 25 number of call contracts
			public int wcQty;						// 26 number of wing call contracts
			public decimal uStartPrice;				// 27 Underlying Price when trade put on
			public decimal pStartPrice;				// 28 Put Price when trade put on
			public decimal cStartPrice;				// 29 Call Price when trade put on
			public decimal wcStartPrice;			// 30 ATM Call Price when trade put on
			public decimal uEndPrice;				// 31 Underlying Price when trade taken off
			public decimal pEndPrice;				// 32 Put Price when trade taken off
			public decimal cEndPrice;				// 33 Call Price when trade taken off
			public decimal wcEndPrice;				// 34 ATM Call Price when trade taken off
			public int	numDividends;				// 35 # of dividends collected during the trade
			public decimal divIncome;				// 36 $'s collected in Dividend income during the trade
			public decimal betaValue;				// 37 beta value of underlying when trade put on
			public decimal RORThresh;				// 38 Threshold for ROR
			public decimal ROCThresh;				// 39 Threshold for ROC
			public decimal CCORThresh;				// 40 Threshold for CCOR
			public string tradeCriteria;			// 41 ROR or ROC or CCOR
			public decimal ROR;						// 42 ROR calculation from SSQR Matrix
			public decimal ROC;						// 43 ROC calculation from SSQR Matrix
			public decimal CCOR;					// 44 CCOR calculation from SSQR Matrix
			public decimal stockADX;				// 45 Average Directional Index Value
			public decimal stockADXR;				// 46 Average Directional Index Rating
			public decimal stockOBV;				// 47 On Balance Volume
			public decimal stockAD;					// 48 Accumulation/Distribution
			public decimal stockADOSC;				// 49 Accumulation/Distribution Oscillator
			public decimal stockSTO;				// 50 Stochastic value
			public decimal stockVariance;			// 51 Variance of underlying stock
			public decimal grossPnL;				// 52 runtime calculation of PnL at close;
			public decimal SSQRnetProfit;			// 53 runtime calculation of replacement bestSSQR net Profit
		
			//   ****    put class methods here to use collection of TradePerfRecs as basis to examine positions for expirations and assignments
			
			
			
		}
	
		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 string ConvertTradePerfRec(List<TradePerfRec> tPR)
		{
			string tPRString = "";
			string jasonString = "";
			
			jasonString = "{";
			tPRString = ",^^^";
			foreach (var field in typeof(TradePerfRec).GetFields())	
			{
				tPRString = tPRString + ", " + field.Name;
			}
			Debug(tPRString);
				
			var tPREnum = tPR.GetEnumerator();
			
			///////  NOTE:  Have to get the JASON formatted correctly.   Need one long string.  CHECK THIS
			while (tPREnum.MoveNext())
			{
				TradePerfRec thisPerfRec = tPREnum.Current;
				jasonString = "{";
				tPRString = ",^^^";
				
				tPRString = tPRString + ", " + thisPerfRec.uSymbol;
				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 + ", " + String.Format("{0:MM/dd/yy H:mm:ss}", thisPerfRec.thetaExpiration);
				tPRString = tPRString + ", " + thisPerfRec.pSymbol.Value;
				tPRString = tPRString + ", " + thisPerfRec.cSymbol.Value;
				tPRString = tPRString + ", " + thisPerfRec.wcSymbol.Value;
				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 + ", " + 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.grossPnL);
				tPRString = tPRString + ", " + String.Format("{0:0.00}", thisPerfRec.SSQRnetProfit);
				
				/*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);
			}
			
			return jasonString;
		}

	}
}
using QuantConnect.Securities.Option;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace QuantConnect.Algorithm.CSharp
{ 
    public partial class CollarAlgorithm : QCAlgorithm
    {
		//*************************************************************************************************
		//**************     CheckDividendRoll      *******************************************************
		//*************************************************************************************************

		public bool CheckDividendRoll (Slice slD, DateTime nxtExDate, TradePerfRec currTPRec, Symbol sCall, Symbol lPut, decimal sPrice, int daysRemaining)
		//*** First look at potential call assigments for dividend approachment beginning 4 days before ex dividend
		//*** the stock may be called if the extrinsic value is less than the dividend ***//
		//*** in this case we have to roll the calls by buying them and selling new calls ***//
		//***  About differentiating calls shorted for Theta Harvesting, ask John if those are weekly options?? 
		//***  Should we, and if so, how do we differentiate those in the context of call assignment during 
		//***  ex-dividend approachment
		//***  --------------------------------------------------------------------------------------

		// 1st, get the price of the corresponding put.   Its premium equals the extrinsic value of the short call.
		// If the corresponding put premium is less than the dividend, the stock may be assigned.

		{
			if (haltProcessing) {
				Debug("               HALTED IN DIVIDEND APPROACHMENT     --");
			}

			string correspondingPutString = GetCorrespondingPut(sCall);
			Symbol putSymb = Securities[correspondingPutString].Symbol;
			//decimal cpPrice = (Securities[correspondingPutString].AskPrice + Securities[correspondingPutString].BidPrice) / 2M;
			decimal cpPrice = Securities[correspondingPutString].AskPrice;
			// If putPremium (price) is less than the dividend, the calls might be exercised, so figure out if we should roll the options
	        if (cpPrice < stockDividendAmount)
	        {
				if (doTracing) Debug(" **************  BEGIN APPROACHMENT CALC FOR " + thisSymbol + " priced @" + sPrice );
				if (doTracing) Debug(" **************  EX-Date: " + nxtExDate.ToString() );
				if (doTracing) Debug(" **************  DIVIDEND " + stockDividendAmount + " Extrinsic Value: " + cpPrice );

				if (useDeltas)																			// if Greeks.Delta will be used to calculate most optimal trade
		        {
					if(slD.Time.Minute == 0  | slD.Time.Minute == 15  | slD.Time.Minute == 30  | slD.Time.Minute == 45)
					{
						potentialCollars = GetPotentialCollars(slD, thisSymbol, nxtExDate);			// get a list of potential collars and evaluate deltas in next minute
						return true;																		// gert out of this slice and come back the next minute to evaluate
					} else if ((potentialCollars.Count != 0) && (slD.Time.Minute == 1  | slD.Time.Minute == 16  | slD.Time.Minute == 31  | slD.Time.Minute == 46) )
					{
						bestSSQRColumn = GetBestSSQRFromPotentialCollars(slD, thisSymbol, nxtExDate, potentialCollars);
						potentialCollars.Clear();														// Clear the PotentialCollars list.   NOTE:  this will obviate subsequent "next minute" evaluations
					} else return true;

        		} else {
        			if(slD.Time.Minute != 0  & slD.Time.Minute != 15  & slD.Time.Minute != 30  & slD.Time.Minute != 45)	return true;
        			//bestSSQRColumn = GetBestSSQR(data, thisSymbol, nextExDate);				// this is the normal route of non-delta execution
					potentialCollars = GetPotentialCollars(slD, thisSymbol, nxtExDate);			// get a list of potential collars 
					if (potentialCollars.Count == 0) {
	    				if (doTracing) Debug($" OOOOOOOOOOOO NO potentialCollars IN  {thisSymbol} POTENTIAL DIVIDEND ASSIGNMENT");
						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 (doTracing) Debug($" OOOOOOOOOOOO NO bestSSQR FOR  {thisSymbol} ON LAST DAY OF DIVIDEND-FORCED EXERCISE -- KILL THE COLLAR  OOOOOOOOOO");
							KillTheCollar(currTPRec, slD.Time, "NO bestSSQR ON LAST DAY OF DIVIDEND-APPROACHMENT -- KILL" );
    					}
						potentialCollars.Clear();
						bestSSQRColumn = new SSQRColumn();
						if (doTracing) Debug($"**************  END APPROACHMENT PROCESSING FOR  {thisSymbol} ******************");
						if (doTracing) Debug("----------");

						return true;					// Don't execute further processing in this slice if rolled due to dividend approachment
					}
					bestSSQRColumn = GetBestSSQRFromPotentialCollars(slD, thisSymbol, nxtExDate, potentialCollars);
        		}		
    		
    			//  Trap the synchronous processing when not evaluating deltas -- 
    			
    			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 (doTracing) Debug($" OOOOOOOOOOOO NO bestSSQR ON LAST DAY OF DIVIDEND-FORCED EXERCISE FOR  {thisSymbol} -- KILL THE COLLAR  OOOOOOOOOO");
						KillTheCollar(currTPRec, slD.Time, "NO bestSSQR ON LAST DAY OF DIVIDEND-APPROACHMENT -- KILL" );
						potentialCollars.Clear();
						bestSSQRColumn = new SSQRColumn();
						if (doTracing) Debug($"**************  END APPROACHMENT PROCESSING FOR  {thisSymbol} ******************");
						if (doTracing) Debug("-");

						return true;					// Don't execute further processing in this slice if rolled due to dividend approachment
	
    				} else {
    				
    					if (doTracing) Debug($"**************  END DIV APPROACHMENT PROCESSING FOR  {thisSymbol} -- NULL bestSSQR -- TRY AGAIN ******************");
						if (doTracing) Debug("----");	
						return true;								// Exit OnData if there's no SSQR Column to process
    				}
    			} 

				/// -----------------------------------------------------------------------------------------------------------------------------------------------
    			///  useDeltas  ////  useDeltas   ////  useDeltas   /////////////////	Check for empty bestSSQRColum in useDeltas /// useDeltas  /// useDeltas  /// useDeltas  /// useDeltas  /// useDeltas 
				if (useDeltas && bestSSQRColumn.IsEmpty() && (potentialCollars.Count != 0) && (slD.Time.Minute == 1  | slD.Time.Minute == 16  | slD.Time.Minute == 31  | slD.Time.Minute == 46) ) 				// found that OptionChainProvider.GetOptionContractList(thisStock, thisSlice.Time)
        		{											// sometimes returns nothing	
        			// THIS IS A useDeltas branch of code
        			
        			if (doTracing) Debug($" *************  DIV APPROACHMENT RETURNED NO bestSSQR Column FOR  {thisSymbol} *******");
        			if (doTracing) Debug($"**************  END DIV APPROACHMENT PROCESSING	FOR  {thisSymbol} ******************");
					if (doTracing) Debug(" ----");
        			return true;
	    		} 
    			///  useDeltas  ////  useDeltas   ////  useDeltas   /////////////////	Check for empty bestSSQRColum in useDeltas /// useDeltas  /// useDeltas  /// useDeltas  /// useDeltas  /// useDeltas 
				/// -----------------------------------------------------------------------------------------------------------------------------------------------

				if (!bestSSQRColumn.IsEmpty() )
				{
					TimeSpan expireDateDeltaSSQR = bestSSQRColumn.optExpiry.Subtract(slD.Time);
			
					//goodThresh = (bestSSQRColumn.CCOR >= CCORThresh);
					goodThresh = true;
					        			
					        			
					if (goodThresh)				// roll the position forward
					{
						newRollDate = slD.Time.Date;
						// don't do the roll if one has just been done -- 
						// sometimes ex-dividend dates are within 10 days of options expiration and a roll has already been done
												
						if (newRollDate.Equals(oldRollDate)) {   	/////// THIS SHOULD NOT HAPPEN IN v17 AND BEYOND BECAUSE LINQ WAS AMENDED TO PREVENT THESE OPTIONS
			
							if (doTracing) Debug($" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  DIVIDEND EXERCISE ROLL ABORT -- DUPLICATION  {thisSymbol} @@@@@@@@@@@@@@@@@@@@@@@");
						    if (doTracing) Debug(" @@@@@@@@@@@@@@@@@@@  OLD DATE: " + oldRollDate.ToString("MM/dd/yyyy HH:mm:ss") + "  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 
					    	if (doTracing) Debug(" @@@@@@@@@@@@@@@@@@@  NEW DATE: " + newRollDate.ToString("MM/dd/yyyy HH:mm:ss") + "  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 
					    	if (doTracing) Debug(" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
							if (doTracing) Debug("-");
							if (doTracing) Debug($" **************  END APPROACHMENT PROCESSING FOR  {thisSymbol} ******************");
							if (doTracing) Debug("------");
							potentialCollars.Clear();
							bestSSQRColumn = new SSQRColumn();
							return true;
												
						} else 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 (doTracing) Debug($" @@@@@@@@@@@@@@@@@@@@@@@  DIVIDEND EXERCISE ROLL ABORT -- CALL PREVENTION FOR  {thisSymbol} @@@@@@@@@@@");
				        	if (doTracing) Debug(" @@@@@@@@@@@@@@@@@@@  CALL ASK: " + Securities[tradableCall].AskPrice  + " Strike: " + bestSSQRColumn.callStrike + " Stock Price: " + stockPrice +" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 
			    	    	if (doTracing) Debug(" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@");
							if (doTracing) Debug("-");
			        		if (doTracing) Debug($" **************  END APPROACHMENT PROCESSING FOR  {thisSymbol} ******************");
							if (doTracing) Debug("-");
							potentialCollars.Clear();
							bestSSQRColumn = new SSQRColumn();
			        		// DO NOT KILL THE COLLAR HERE.   
			        						
				       		return true;   
				       	} 
						
						if (doTracing) Debug($" **************  BEGIN DIV APPROACHMENT ROLL FOR  {thisSymbol} ****************");
						
						//iterate potetialCollars here solely when executing a trade
											
						
						if (currSellPnL > 0 | currTPRec.uQty * bestSSQRColumn.netIncome > Math.Abs(currSellPnL)) {								// Roll solely if we can sell the current collar profitably
						//if (currSellPnL > 0 ) {								// Roll solely if we can sell the current collar profitably
							RollTheCollar(slD, thisSymbol, "DIVIDEND APPROACHMENT ROLL");
									
							if (didTheTrade) {
								oldRollDate = slD.Time.Date;						// set the oldRollDate to Date of Roll
								if (doTracing) Debug(" **************  SUCCESSFUL DIV APPROACHMENT ROLL WITH SSQR: ");
								var orderedSSQRMatrix = potentialCollars.OrderBy(p => p.CCOR);
		    	    			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
    							{
    								KillTheCollar(currTPRec, slD.Time, "KILL- FAILED ROLL 1ST TPR IN DIVIDEND-FORCED EXERCISE ON LAST DAY" );	// KillTheCollar may return to try again as well
	    						}
		
							potentialCollars.Clear();
							bestSSQRColumn = new SSQRColumn();
							return true;									
						} else { 
							Debug(" 00 Some code");
							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
    						{
    							KillTheCollar(currTPRec, slD.Time, "KILL- LOSS ON 1ST TPR IN DIVIDEND-FORCED EXERCISE ON LAST DAY" );	// KillTheCollar may return to try again as well
    						}
							potentialCollars.Clear();
							bestSSQRColumn = new SSQRColumn();
							if (doTracing) Debug($"**************  END APPROACHMENT PROCESSING FOR  {thisSymbol} ******************");
							if (doTracing) Debug("-------");
	
							return true;					// Don't execute further processing in this slice if rolled due to dividend approachment
						}

						if (doTracing) Debug($" **************  END DIV APPROACHMENT ROLL FOR  {thisSymbol} ****************");
						if (doTracing) Debug("----");
						if (doTracing) Debug($"**************  END DIV APPROACHMENT PROCESSING FOR  {thisSymbol} ******************");
						if (doTracing) Debug("-");
						return true; 				// 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 (doTracing) Debug($" OOOOOOOOOOOO BAD THRESH ON DIVIDEND-FORCED EXERCISE FOR  {thisSymbol}-- KILL THE COLLAR ON LAST DAY OOOOOOOOOO");
							KillTheCollar(currTPRec, slD.Time, "KILL- LAST DAY BAD THRESH ON DIVIDEND-FORCED EXERCISE" );							// KillTheCollar may return to try again as well
    					} else {
    						if (doTracing) Debug($" OOOOOOOOOOOO BAD THRESH ON DIVIDEND-FORCED EXERCISE FOR  {thisSymbol} -- RETURN AND TRY AGAIN");
    						
    					}
						potentialCollars.Clear();
						bestSSQRColumn = new SSQRColumn();
						if (doTracing) Debug($"**************  END APPROACHMENT PROCESSING FOR  {thisSymbol} ******************");
						if (doTracing) Debug("-----");

						return true;					// Don't execute further processing in this slice if rolled due to dividend approachment
					} // not goodThresh
					
					return true;
					
				}  // !bestSSQRColumn /// there was no bestSSQRColumn	
			} // correspondingPutPremium < Dividend			/// no danger of dividend-call assignment
			return false;
		} // end of CheckDividendRoll () funtion  

		//*************************************************************************************************
		//**************     CheckCallRoll          *******************************************************
		//*************************************************************************************************

		public bool CheckCallRoll (Slice slD, DateTime nxtExDate, TradePerfRec currTPRec, Symbol sCall, Symbol lPut, decimal sPrice, int daysRemaining)
		{
		// Determine if it should be rolled forward.
	
			if (doTracing) Debug($" **************  BEGIN ITM CALL CALC FOR  {thisSymbol} ****************");

    		//bestSSQRColumn = GetBestSSQR(data, thisSymbol, nextExDate);
    		potentialCollars = GetPotentialCollars(slD, thisSymbol, nxtExDate);		// get a list of potential collars here to iterate if there's a good threshold
			if (potentialCollars.Count == 0) {
    			if (daysRemaining <= 1) {											// if at the last day of call expiration and haven't yet rolled, kill the collar.
					if (doTracing) Debug($" *********  END ITM CALL FORCED ASSIGNMENT PROCESSING FOR  {thisSymbol} -- NO POTENTIAL COLLARS ON LAST DAY *************");

					KillTheCollar(currTPRec, slD.Time, "KILL ITM CALL ASSIGNMENT -- NO POTENTIAL COLLARS ON LAST DAY");
					potentialCollars.Clear();
					bestSSQRColumn = new SSQRColumn();
					if (doTracing) Debug($" OOOOOOOOO TT END CHECK IMPLICIT CALL ASSIGNMENT FOR  {thisSymbol} OOOOOOOOO");
					if (doTracing) Debug("-----");

    			}

    			Debug($" **************  END ITM CALL CALC PROCESSING FOR  {thisSymbol} -- NO POTCOLS ***");
    			return true;										// if no collars then return and loop around again
    		}

    		bestSSQRColumn = GetBestSSQRFromPotentialCollars(slD, thisSymbol, nxtExDate, potentialCollars);

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

						KillTheCollar(currTPRec, slD.Time, "KILL ITM CALL ASSIGNMENT -- NO POTENTIAL COLLARS ON LAST DAY");
						potentialCollars.Clear();
						bestSSQRColumn = new SSQRColumn();
						if (doTracing) Debug($" TT END CHECK IMPLICIT CALL ASSIGNMENT FOR  {thisSymbol} OOOOOOOOO");
						if (doTracing) Debug("-");

    				} else {

    				if (doTracing) Debug($" **************  END ITM CALL CALC FOR  {thisSymbol} -- null bestSSQRColumn *************");
    				potentialCollars.Clear();
    				bestSSQRColumn = new SSQRColumn();
    				}
    			return true;													// return and exit OnData()
    			
    		}		

			goodThresh = bestSSQRColumn.CCOR >= CCORThresh;
			
    		if (goodThresh)				// roll the position forward
			{
				if (doTracing) Debug($" **************  BEGIN ITM CALL ROLL FOR  {thisSymbol} ****************");
				newRollDate = slD.Time.Date;
			
				if (!newRollDate.Equals(oldRollDate)) {
						
																				// 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 (Securities[tradableCall].AskPrice + bestSSQRColumn.callStrike < stockPrice) // make sure that no one can buy the option for less than the stock
	        		{
		    			if (doTracing) Debug($" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  ABORT ITM CALL ROLL TO PREVENT EXERCISE FOR  {thisSymbol} @@@@@@@@@@@");
    					if (doTracing) Debug(" @@@@@@@@@@@@@@@@@@@  CALL ASK: " + Securities[tradableCall].AskPrice  + " Strike: " + bestSSQRColumn.callStrike + " Stock Price: " + stockPrice +" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 
    					if (doTracing) Debug(" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
						if (doTracing) Debug("-");
						
						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
    					{
    						KillTheCollar(currTPRec, slD.Time, "ABORT ITM CALL ROLL TO PREVENT EXERCISE ON LAST DAY" );	// KillTheCollar may return to try again as well
    					}

						potentialCollars.Clear();
						bestSSQRColumn = new SSQRColumn();
						return true;   
					}

					
					if (currSellPnL > 0 | currTPRec.uQty * bestSSQRColumn.netIncome > Math.Abs(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.
						
						RollTheCollar(slD, thisSymbol, "ROLL--ITM CALL EXPIRATION APPROACHMENT");
						if (didTheTrade) {
							oldRollDate = slD.Time.Date;						// set the oldRollDate to Date of Roll
							if (doTracing) Debug($" **************  ROLLED ITM CALLS COMPLETED FOR  {thisSymbol}*************");
							var orderedSSQRMatrix = potentialCollars.OrderBy(p => p.CCOR);
								
							IterateOrderedSSQRMatrix(orderedSSQRMatrix);
							//didTheTrade = false;
						}	else if (daysRemaining <= 1) {
							if (doTracing) Debug($"  ************** UNSUCCESSFUL ROLL FOR  {thisSymbol} -- KILL ITM PUT COLLAR ON LAST DAY  **************");
							KillTheCollar(currTPRec, slD.Time, "KILL- LOSS IN 1st TPR IN ITM CALL ROLL" );			// Goto KillTheCollar and determine whether to close or allow call assignment there
							potentialCollars.Clear();
							bestSSQRColumn = new SSQRColumn();
							if (doTracing) Debug("-");
							return true;
						}
					} else if (daysRemaining <= 1) {
						Debug($"  ************** UNPROFITABLE ROLL FOR  {thisSymbol} -- KILL ITM PUT COLLAR ON LAST DAY  **************");
						KillTheCollar(currTPRec, slD.Time, "KILL- LOSS IN 1st TPR IN ITM CALL ROLL" );			// Goto KillTheCollar and determine whether to close or allow call assignment there
						potentialCollars.Clear();
						bestSSQRColumn = new SSQRColumn();
						if (doTracing) Debug("-");
						return true;
					}
						Debug($"  ************** UNPROFITABLE ROLL FOR  {thisSymbol} -- LOOP AROUND AND TRY AGAIN  **************");
						
						potentialCollars.Clear();
						bestSSQRColumn = new SSQRColumn();
						if (doTracing) Debug("-");
						return true;
				} else {														///  -- newRollDate == oldRollDate -- >> exit OnData() and loop back again
					if (doTracing) Debug($" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  ABORT ITM CALL EXERCISE  --  {thisSymbol} DUPLICATION @@@@@@@@@@@@@@@@@@@@@@@");
    				if (doTracing) Debug(" @@@@@@@@@@@@@@@@@@@  OLD DATE: " + oldRollDate.ToString("MM/dd/yyyy HH:mm:ss") + "  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 
    				if (doTracing) Debug(" @@@@@@@@@@@@@@@@@@@  NEW DATE: " + newRollDate.ToString("MM/dd/yyyy HH:mm:ss") + "  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 
    				if (doTracing) Debug(" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");

					
					if (doTracing) Debug("-");   // goodThresh, but already rolled today. NOTE: still using prior variables in Slice go past goodThresh
					potentialCollars.Clear();
					bestSSQRColumn = new SSQRColumn();
					return true;
				}
			} 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 (doTracing) Debug($" **************  END ITM CALL FORCED ASSIGNMENT PROCESSING FOR  {thisSymbol}-- BAD THRESH ****************");
			if (daysRemaining <= 1) {
				if (doTracing) Debug($" **************  KILL COLLAR IN ITM CALL FORCED ASSIGNMENT PROCESSING FOR  {thisSymbol} -- BAD THRESH ON LAST DAY");
				KillTheCollar(currTPRec, slD.Time, "KILL ITM CALL -- PREVENT ASSIGNMENT");
			}
			potentialCollars.Clear();
			bestSSQRColumn = new SSQRColumn();
			if (doTracing) Debug($" TT END CHECK IMPLICIT CALL ASSIGNMENT FOR  {thisSymbol} OOOOOOOOO");
			if (doTracing) Debug($" ------------------------------------------------ ");
			return true;										// exit OnData() and loop around again until last day.   May get assigned!
		
			//ExerciseOption(shortedCallSymbol, Decimal.ToInt32(currTPRec.cQty));   // LEAN error, cannot exercise short options

		}
       	return false;	
		// endif ITM calls -10 -> Expiry.    Probably do an elseif {ITM puts here to definitively trap all assignments
		
			
		} // end CheckCallRoll function
	
		//*************************************************************************************************
		//**************     CheckPutRoll          *******************************************************
		//*************************************************************************************************

		public bool CheckPutRoll (Slice slD, DateTime nxtExDate, TradePerfRec currTPRec, Symbol sCall, Symbol lPut, decimal sPrice, int daysRemaining)
		{
			// Determine if it should be rolled forward.
			if (doTracing) Debug($" **************  BEGIN ITM PUT CALC FOR  {thisSymbol} ****************");
								
			//bestSSQRColumn = GetBestSSQR(data, thisSymbol, nextExDate);
			potentialCollars = GetPotentialCollars(slD, thisSymbol, nxtExDate);			// get a list of potential collars 
			if (potentialCollars.Count == 0) {
				if (daysRemaining <= 1) {
					if (doTracing) Debug($" *********  END ITM PUT FORCED ASSIGNMENT PROCESSING FOR  {thisSymbol} -- NO POTENTIAL COLLARS ON LAST DAY *************");

					KillTheCollar(currTPRec, slD.Time, "KILL ITM PUT ASSIGNMENT -- NO POTENTIAL COLLARS ON LAST DAY");
					potentialCollars.Clear();
					bestSSQRColumn = new SSQRColumn();
					if (doTracing) Debug($" TT END CHECK IMPLICIT PUT ASSIGNMENT FOR  {thisSymbol} OOOOOOOOO");
					if (doTracing) Debug("-----");
    			}

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

					KillTheCollar(currTPRec, slD.Time, "KILL ITM PUT ASSIGNMENT -- NO POTENTIAL COLLARS ON LAST DAY");
					potentialCollars.Clear();
				
					bestSSQRColumn = new SSQRColumn();
					if (doTracing) Debug($" TT END CHECK IMPLICIT PUT ASSIGNMENT FOR  {thisSymbol} OOOOOOOOO");
					if (doTracing) Debug("--------");

    			} else {
					if (doTracing) Debug($" *********  END ITM PUT FORCED ASSIGNMENT PROCESSING FOR  {thisSymbol} -- bestSSQR null or empty LOOPING TO TRY AGAIN");
					
					potentialCollars.Clear();
					bestSSQRColumn = new SSQRColumn();
					return true;					// loop around and try again
    			}
			}	
					
			tradablePut = bestSSQRColumn.putSymbol;
			tradableCall = bestSSQRColumn.callSymbol;
	
			fTPRPutPrice = 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 ((currTPRec.pStartPrice - fTPRPutPrice) > (currTPRec.uStartPrice + currTPRec.pStartPrice - currTPRec.cStartPrice)  )
											{
												if (doTracing) Debug(" 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, -currTPRec.cQty);
												
												if (closeCallTicket.Status == OrderStatus.Filled)
												{
													currTPRec.cEndPrice = closeCallTicket.AverageFillPrice;
												}
												
												var putExerciseTicket = ExerciseOption(longPutSymbol,  currTPRec.pQty);
												potentialCollars.Clear();
												bestSSQRColumn = new SSQRColumn();
												if (doTracing) Debug(" **************  END ITM PUT CALC -- EXERCISED PUTS    ******");
												
												return true;
												
											} */
			goodThresh = bestSSQRColumn.CCOR >= CCORThresh;
			
			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 (Securities[tradableCall].AskPrice + bestSSQRColumn.callStrike < stockPrice) // make sure that no one can buy the option for less than the stock
		        {
			    	if (doTracing) Debug($" @@@@@@@@@@@@@@@@@@@  ITM PUT ROLL ABORT FOR  {thisSymbol} -- IMMEDIATE CALL-EXERCISE PREVENTION FADE @@@@@@@@@@@@@@@@@@@@@@@");
	    			if (doTracing) Debug(" @@@@@@@@@@@@@@@@@@@  CALL ASK: " + Securities[tradableCall].AskPrice  + " Strike: " + bestSSQRColumn.callStrike + " Stock Price: " + stockPrice +" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 
	    			if (doTracing) Debug(" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
					if (doTracing) Debug("------");
					if (daysRemaining <= 1) {
						KillTheCollar(currTPRec,  slD.Time, "ABORT ITM PUT ROLL TO PREVENT SUBSEQUENT CALL ASSIGNMENT");
					}
					potentialCollars.Clear();
					bestSSQRColumn = new SSQRColumn();
					return true;   
				}
	
				if (doTracing) Debug($" **************  BEGIN ITM PUT ROLL FOR  {thisSymbol} ****************");
				newRollDate = slD.Time.Date;
				if (!newRollDate.Equals(oldRollDate)) {
					
					if (currSellPnL > 0 | currTPRec.uQty * bestSSQRColumn.netIncome > Math.Abs(currSellPnL)) {								// Roll solely if we can sell the current collar profitably
					//if (currSellPnL > 0 ) {				// Roll solely if we can sell the current collar profitably
						RollTheCollar(slD, thisSymbol, "ROLL -- ITM PUT NEAR EXPIRATION");
						if (didTheTrade) {
							oldRollDate = slD.Time.Date;						// set the oldRollDate to Date of Roll
							if (doTracing) Debug($" **************  ROLLED ITM PUTS COMPLETED FOR  {thisSymbol} ****************");
							var orderedSSQRMatrix = potentialCollars.OrderBy(p => p.CCOR);							// 2021-03-21 -- changed from OrderedByDescending
							IterateOrderedSSQRMatrix(orderedSSQRMatrix);
							didTheTrade = false;
						} else {
							if (daysRemaining <= 1) {
								KillTheCollar(currTPRec,  slD.Time, "ITM PUT ROLL FAILED");
							}
						}
						
						potentialCollars.Clear();
						bestSSQRColumn = new SSQRColumn();
					} else {													// un profitable roll
						if (daysRemaining <= 1) {
							if (doTracing) Debug($" ************** UNPROFITABLE ITM PUT ROLL FOR  {thisSymbol} ON LAST DAY -- ATTEMPT KILL");
							KillTheCollar(currTPRec, slD.Time, "KILL- LOSS IN 1st TPR IN ITM PUT ROLL" );					
						}
						potentialCollars.Clear();
						bestSSQRColumn = new SSQRColumn();
						if (doTracing) Debug($" TT END CHECK IMPLICIT PUT ASSIGNMENT FOR  {thisSymbol} OOOOOOOOO");
						if (doTracing) Debug("-----");
					}
					return true;																				// exit OnData and try again until last day
					
				} else { 
					if (doTracing) Debug("************  FADE ITM PUT ROLL DUE PRIOR DIVIDEND ROLL ****************");
					potentialCollars.Clear();	
					bestSSQRColumn = new SSQRColumn();
					if (doTracing) Debug($" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  ABORT ITM PUT PRE-EXERCISE ROLL FOR  {thisSymbol} --DUPLICATION @@@@@@@@@@@@@@@@");
	    			if (doTracing) Debug(" @@@@@@@@@@@@@@@@@@@  OLD DATE: " + oldRollDate.ToString("MM/dd/yyyy HH:mm:ss") + "  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 
	    			if (doTracing) Debug(" @@@@@@@@@@@@@@@@@@@  NEW DATE: " + newRollDate.ToString("MM/dd/yyyy HH:mm:ss") + "  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 
	    			if (doTracing) Debug(" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
	
					if (doTracing) Debug($" **************  END ITM PUT ROLL - FADED  FOR  {thisSymbol} ****************");
					if (doTracing) Debug($" **************  END ITM PUT CALC FOR  {thisSymbol} ****************");
					if (doTracing) Debug("------");
					return true;
				}
			} else {							// bad threshhold on ITM PUT ROLL -- EXERCISE IT
				
				if (daysRemaining <= 1) {
					if (doTracing) Debug($" ************** BAD SSQR THRESHOLD IN ITM PUT ROLL FOR  {thisSymbol} ON LAST DAY -- ATTEMPT KILL");
					KillTheCollar(currTPRec,  slD.Time, "KILL ON LAST DAY OF ITM PUT ");
				}
				potentialCollars.Clear();
				bestSSQRColumn = new SSQRColumn();

				if (doTracing) Debug($" **************  END ITM PUT CALC FOR  {thisSymbol} ****************");
				if (doTracing) Debug("---------");
				return true;					// roll around and try again
			}

		return true;
	
	
		}	// end CheckPutRoll

		//*************************************************************************************************
		//**************     CheckOTMRoll           *******************************************************
		//*************************************************************************************************

		public bool CheckOTMRoll(Slice slD, DateTime nxtExDate, TradePerfRec currTPRec, Symbol sCall, Symbol lPut, decimal sPrice, int daysRemainingC, int daysRemainingP) {
			// risk of options expiration WITHOUT EXERCISE
			if (doTracing) Debug($" **************  BEGIN OTM OPTIONS CALC FOR  {thisSymbol} ****************");

			if (haltProcessing) 
			{
				Debug("                 HALTED OTM OPTIONS CALC ");
			}
    		potentialCollars = GetPotentialCollars(slD, thisSymbol, nxtExDate);			// get a list of potential collars
    		if (potentialCollars.Count == 0) {
    			if (daysRemainingC <= 1 | daysRemainingP <= 1) {
    				KillTheCollar(currTPRec,  slD.Time, "ABORT OTM ROLL -- NO POT COLLARS FOR " + thisSymbol );
 					potentialCollars.Clear();
					bestSSQRColumn = new SSQRColumn();
					
					if (doTracing) Debug($" **************  END OTM OPTIONS KILL FOR  {thisSymbol} ****************");
					if (doTracing) Debug("-");
					return true;
   				} else {
	    			potentialCollars.Clear();
	    			bestSSQRColumn = new SSQRColumn();
	
	    			Debug($" **************  END OTM OPTIONS CALC -- NO POTCOLS FOR  {thisSymbol} -- LOOP AND TRY AGAIN LATER  ***");
	    			return true;										// if no collars then return and loop around again
	    		}
    		}
    		
    		bestSSQRColumn = GetBestSSQRFromPotentialCollars(slD, thisSymbol, nxtExDate, potentialCollars);
    		
    		if (bestSSQRColumn == null || bestSSQRColumn.IsEmpty()) {
    			if (doTracing) Debug($" **************  null bestSSQRColumn in OTM Expiry Approachment FOR  {thisSymbol} *************");
    			if (doTracing) Debug($" **************  END  OTM OPTIONS CALC FOR  {thisSymbol} ****************");
    			
    			if (daysRemainingC <= 1 | daysRemainingP <= 1) {
    				KillTheCollar(currTPRec,  slD.Time, "END OTM PROCESSING -- NO VIABLE SSQRS FOR " + thisSymbol );
					potentialCollars.Clear();
					bestSSQRColumn = new SSQRColumn();
							
					if (doTracing) Debug($" **************  END OTM OPTIONS KILL FOR  {thisSymbol} LOOP AND TRY AGAIN LATER ****************");
					if (doTracing) Debug("-----");
					return true;
    			} else {

	    			potentialCollars.Clear();
	    			bestSSQRColumn = new SSQRColumn();
	    			Debug($" **************  END OTM OPTIONS CALC  FOR  {thisSymbol} -- bestSSQRColumn NULL or EMPTY ***");
	    			return true;													//  exit OnData() and loop around and try again
    			}	
    		} 	// no bestSSQRColumn	
    		
			// IS IT NECESSARY TO SET THESE HERE
			tradablePut = bestSSQRColumn.putSymbol;
			tradableCall = bestSSQRColumn.callSymbol;

			//goodThresh = bestSSQRColumn.CCOR >= CCORThresh;
			goodThresh = true;
    		
    		if (goodThresh)				// roll the position forward
			{
				if (doTracing) Debug($" **************  BEGIN OTM OPTIONS ROLL FOR  {thisSymbol}  ****************");
				newRollDate = slD.Time.Date;
				if (!newRollDate.Equals(oldRollDate)) {
					if (currSellPnL > 0 | currTPRec.uQty * bestSSQRColumn.netIncome > Math.Abs(currSellPnL)){										// only roll the collar if the current record may be closed profitably-- otherwise seek exercise in kill
					//if (currSellPnL > 0) {
						RollTheCollar(slD, thisSymbol, "OTM OPTIONS EXPIRATION ROLL");
						if (didTheTrade) {
							oldRollDate = slD.Time.Date;						// set the oldRollDate to Date of Roll
							if (doTracing) Debug($" **************  ROLLED OTM OPTIONS  FOR  {thisSymbol} COMPLETED WITH SSQR: ****************");
							if (doTracing) Debug("-");
							var orderedSSQRMatrix = potentialCollars.OrderBy(p => p.CCOR);
	
	   						IterateOrderedSSQRMatrix(orderedSSQRMatrix);
	   						//didTheTrade = false;
							potentialCollars.Clear();
							bestSSQRColumn = new SSQRColumn();
							
							if (doTracing) Debug($" **************  END SUCCESSFUL OTM OPTIONS ROLL FOR  {thisSymbol}  ****************");
							if (doTracing) Debug("-");
							return true;
	   					} else {
			    			if (daysRemainingC <= 1 | daysRemainingP <= 1) {
	    						if (doTracing) Debug($" **************  KILLING OTM OPTIONS COLLAR  FOR  {thisSymbol}  ON LAST DAY - FAILED ROLL ****************");
	    						KillTheCollar(currTPRec,  slD.Time, "END OTM PROCESSING -- FAILED ROLL FOR " + thisSymbol );
								potentialCollars.Clear();
								bestSSQRColumn = new SSQRColumn();
								
								if (doTracing) Debug($" **************  END OTM OPTIONS KILL  FOR  {thisSymbol}  ON LAST DAY - FAILED ROLL ****************");
								if (doTracing) Debug("-");
								return true;
	    					}
							potentialCollars.Clear();
							bestSSQRColumn = new SSQRColumn();
							
							if (doTracing) Debug($" **************  END OTM OPTIONS ROLL  FOR  {thisSymbol}  -- FAILED ROLL ****************");
							if (doTracing) Debug("-");
							return true;
	   					}
					} else if (daysRemainingC <= 1 | daysRemainingP <= 1) {			//   CANNOT EXECUTE ROLL PROFITABLY SO KILL THE COLLAR IF ON LAST DAY
						KillTheCollar(currTPRec,  slD.Time, "END OTM PROCESSING -- UNPROFITABLE ROLL FOR " + thisSymbol + " ON THE LAST DAY" );
						potentialCollars.Clear();
						bestSSQRColumn = new SSQRColumn();
						
						if (doTracing) Debug($" **************  END OTM OPTIONS ROLL  FOR  {thisSymbol} WITH KILL ****************");
						if (doTracing) Debug("-");
						return true;
					}
				}			// new roll date != old roll date
				if (doTracing) Debug($" **************  END OTM OPTIONS ROLL PROCSSING FOR  {thisSymbol}  ****************");
				if (doTracing) Debug("------------------------------------");
				potentialCollars.Clear();
				bestSSQRColumn = new SSQRColumn();
				return true;

			} else if (daysRemainingC <= 1 | daysRemainingP <= 1) {				// IF BADTHRESH
			
				if (doTracing) Debug($" **************  BEGIN OTM OPTIONS COLLAR KILL FOR  {thisSymbol}  ****************");
		
				// kill the collar
				KillTheCollar(currTPRec, slD.Time, "BAD THRESH ON OTM OPTIONS ROLL");
				potentialCollars.Clear();
				bestSSQRColumn = new SSQRColumn();
				
				if (doTracing) Debug($" **************  END OTM OPTIONS ROLL WITH KILL ON BAD THRESH FOR  {thisSymbol}  ****************");
				if (doTracing) Debug("-------");
				return true;
			} // goodThresh on rolling OTM Options
			potentialCollars.Clear();
			bestSSQRColumn = new SSQRColumn();
						
			if (doTracing) Debug($" **************  END OTM OPTIONS ROLL PROCESSING  FOR  {thisSymbol}  ****************");
			if (doTracing) Debug("-");
			return true;
		}
    }
}