Overall Statistics |
Total Trades 0 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Net Profit 0% Sharpe Ratio 0 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio 0 Tracking Error 0 Treynor Ratio 0 Total Fees $0.00 |
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) { 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 //var csvFile = Download("https://www.dropbox.com/s/ap8s120gksb858h/DividendData.csv?dl=1"); --- worked then it failed var csvFile = Download("https://www.dropbox.com/s/ap8s120gksb858h/DividendData.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 } DividendRecord divRec = new DividendRecord(); var values = csvLine.Split(','); // this file is comma delimited 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.Drawing; 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 int logged = 0; // Switch to toggle log entry writing bool doItOnce = true; // Switch to toggle options exercise interdiction 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 decimal optionsToTrade; // Get the initial Options to trade (1/10th the sharesToBuy) 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 bool doTheTrade = false; // Used to allow trades the algorithm initiates 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 decimal ROCThresh; public decimal RORThresh; 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>(); decimal workingDays = 365M; decimal thisFFRate = 0M; 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 // 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 debugSymbol; // general purpose debugging variable OptionChain debugChain; // special purpose debugging variable SSQRColumn bestSSQRColumn = new SSQRColumn(); decimal stockDividendAmount = 0M; decimal divPlotValue = 0M; DateTime fmrNextExDate; // 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; public override void Initialize() { SetStartDate(2019, 1, 1); //Set Start Date SetEndDate(2019, 1, 5); SetCash(10000000); //Set Strategy Cash SetWarmup(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 //var qBook = new QuantBook(); // Get RunTime control parameters for Minimum # of Dividends and Maximum OTM Put Depth badDtParameter = true; // GetParameter("CheckBadDate") == "true" ? true : false; // get this from parameters stockDollarValue = 10m; // Convert.ToDecimal(GetParameter("StockDollarValue")); maxPutOTM = 2; //Convert.ToDecimal(GetParameter("MaxOTMPutDepth")); // get and set the Maximum OTM Put Depth MinNmbrDivs = 1; //Convert.ToInt16(GetParameter("MinNmbrDivs")); // get and set minimum number of dividends acceptable in BestSSQRMatrix useDeltas = true; // GetParameter("UseDeltas") == "true" ? true : false; // get this from parameters switchROC = true; // GetParameter("ROC") == "true" ? true : false ; // Switch to decide to trade based upon return on capital or return on risk thisSymbol = AddEquity("AAPL", Resolution.Minute).Symbol; Securities[thisSymbol].SetDataNormalizationMode(DataNormalizationMode.Raw); Securities[thisSymbol].VolatilityModel = new StandardDeviationOfReturnsVolatilityModel(30); //SetBenchmark(thisSymbol); sto = STO(thisSymbol, 14, Resolution.Daily); // Stochastic 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 GetTickers(); ibkrHairCuts = InitializeHaircuts(); SetSecurityInitializer(HistoricalSecurityInitializer); } // Initialize() public override void OnData(Slice data) { if (!IsMarketOpen(thisSymbol) | !sto.IsReady | !variance.IsReady | !data.Bars.ContainsKey(thisSymbol)) return; if (useDeltas) { Plot("Chains", "Vale", data.OptionChains.Count); // 0 foreach (var newChain in data.OptionChains) { var testVol = Securities[newChain.Key.Underlying].VolatilityModel.Volatility; Debug("At " + data.Time + ", Symbol, Bid, Ask, Last, Open Interest, Volatility, Theoretical Price, Delta, Implied Vol"); foreach (var thisContract in newChain.Value) { Debug("At " + data.Time + ", " + thisContract.Symbol.Value + ", " + thisContract.BidPrice + ", " + thisContract.AskPrice + ", " + thisContract.LastPrice + ", " + thisContract.OpenInterest + ", "+ testVol + ", " + thisContract.TheoreticalPrice + ", " + thisContract.Greeks.Delta + ", " + thisContract.ImpliedVolatility); } } if (data.OptionChains.Count > 0) Quit(); } GetBestSSQR(data, thisSymbol, getNextExDate(thisSymbol.Value, data.Time)); } // OnData() private void HistoricalSecurityInitializer(Security security) { var bar = GetLastKnownPrice(security); security.SetMarketPrice(bar); } } // 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 int daysInPosition; public decimal interestCost; public Symbol putSymbol; public Symbol callSymbol; public decimal putPremium; public decimal callPremium; public decimal putStrike; public decimal callStrike; public decimal putOpenInterest; public decimal callOpenInterest; public decimal putDelta; public decimal callDelta; 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 string description1; public string description2; //public string description3; public override string ToString() { return this.description1; } } } }
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> allUnderlyingOptionsSymbols, decimal stockPrice, decimal amtDividend, DateTime tradeDate, DateTime thisExDivDate) { if (haltProcessing) { Debug(" @@@@@ Halting assembleSSQR processing"); } OptionChain putChain; // chain object to get contracts OptionChain callChain; // chain object to get contracts OptionContract putGreeks; // chain object to collect greeks OptionContract callGreeks; // chain object to collect greeks List<SSQRColumn> SSQRMatrix = new List<SSQRColumn>(); var callSymbolsForThisExpiry = allUnderlyingOptionsSymbols; var putSymbolsForThisExpiry = allUnderlyingOptionsSymbols; // Get the ATM call contract var atmCall = allUnderlyingOptionsSymbols.Where(s => s.ID.OptionRight == OptionRight.Call) .OrderBy(s => Math.Abs(s.ID.StrikePrice - stockPrice)) .FirstOrDefault(); var atmPut = allUnderlyingOptionsSymbols.Where(s => s.ID.OptionRight == OptionRight.Put) .OrderBy(s => Math.Abs(s.ID.StrikePrice - stockPrice)) .FirstOrDefault(); Option atmCallOpt = AddOptionContract(atmCall); Option atmPutOpt = AddOptionContract(atmPut); atmCallOpt.PriceModel = OptionPriceModels.BinomialTian(); atmPutOpt.PriceModel = OptionPriceModels.BinomialTian(); return SSQRMatrix; 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 var callOpenInterest = History<OpenInterest>(atmCall, TimeSpan.FromDays(5)).FirstOrDefault(); var putOpenInterest = History<OpenInterest>(atmPut, TimeSpan.FromDays(5)).FirstOrDefault(); var totOpenInterest = atmCallOpt.OpenInterest + atmPutOpt.OpenInterest; //Debug("0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O"); // Debug("0O0O0O0O0O0O0O0O0O0O0O0O OPEN INTEREST 0O0O0O0O0O0O0O0O0O0O0O0O0O0O0"); // Debug(" The total Put + Call Open Interest for " + atmCall.ID.Underlying + " is " + atmCallOpt.OpenInterest + "."); // Debug("0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O0O"); 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 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>(); 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 Symbol thisPutSymb; // for building SSQR Symbol thisCallSymb; // for building SSQR decimal thisPutPrem; // for building SSQR decimal thisCallPrem; // for building SSQR decimal thisPutStrike; // for building SSQR decimal thisCallStrike; // 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(); // assemble the symbols lists for each of the next 4 dividends, for (int dividends = MinNmbrDivs; dividends < 5; dividends++) { switch (dividends) { case 1: whichExpiry = expiries[1]; break; case 2: whichExpiry = expiries[2]; break; case 3: whichExpiry = expiries[3]; break; case 4: whichExpiry = expiries[4]; break; } 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; callSymbolsForThisExpiry = allUnderlyingOptionsSymbols.Where(o => DateTime.Compare(o.ID.Date, whichExpiry) == 0 && o.ID.OptionRight == OptionRight.Call && o.ID.StrikePrice >= lowStrike && o.ID.StrikePrice < highStrike && !(daysInTrade <=10 & daysInTrade > 1 & o.ID.StrikePrice > (stockPrice - (0.5M * stockPrice)) | (daysInTrade <= 1 & o.ID.StrikePrice < stockPrice))) .OrderByDescending(o => o.ID.StrikePrice); if (callSymbolsForThisExpiry != null) { //Debug("This call chain is for AT&T whose price is " + stockPrice + ", and expiring on " + whichExpiry); //IterateChain(callSymbolsForThisExpiry); // Use this call to debug and examine members var enumerator = callSymbolsForThisExpiry.GetEnumerator(); callOptionsList.Clear(); while (enumerator.MoveNext()) { optSymbol = enumerator.Current; tempOption = AddOptionContract(optSymbol); tempOption.PriceModel = OptionPriceModels.BinomialTian(); /// necessary for Greeks? callOptionsList.Add(tempOption); } // IterateContracts(callOptionsList); } putSymbolsForThisExpiry = allUnderlyingOptionsSymbols.Where(o => DateTime.Compare(o.ID.Date, whichExpiry) == 0 && o.ID.OptionRight == OptionRight.Put && o.ID.StrikePrice >= lowStrike && o.ID.StrikePrice <= atmStrike && !(daysInTrade <=10 & daysInTrade > 1 & o.ID.StrikePrice > (stockPrice + (0.5M * stockPrice)) | (daysInTrade <= 1 & o.ID.StrikePrice > stockPrice))) .OrderByDescending(o => o.ID.StrikePrice); if (putSymbolsForThisExpiry != null) { //Debug("This put chain is for AT&T whose price is " + stockPrice + ", and expiring on " + whichExpiry); //IterateChain(putSymbolsForThisExpiry); // Use this call to debug and examine members var enumerator = putSymbolsForThisExpiry.GetEnumerator(); putOptionsList.Clear(); while (enumerator.MoveNext()) { optSymbol = enumerator.Current; tempOption = AddOptionContract(optSymbol); tempOption.PriceModel = OptionPriceModels.BinomialTian(); /// necessary for Greeks? putOptionsList.Add(tempOption); } // IterateContracts(putOptionsList); } // 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) { var putEnum = putOptionsList.GetEnumerator(); while (putEnum.MoveNext()) { thisPutOpt = putEnum.Current; thisPutPrem = thisPutOpt.AskPrice; thisPutStrike = thisPutOpt.StrikePrice; //putChain = History<OptionChain>(thisPutOpt.Symbol, TimeSpan.FromDays(30)).FirstOrDefault(); //putGreeks = putChain.Contracts[thisPutOpt.Symbol]; var callEnum = callOptionsList.GetEnumerator(); // cannot reset this enumerator, have to re-create while (callEnum.MoveNext()) { thisCallOpt = callEnum.Current; thisCallPrem = thisCallOpt.BidPrice; thisCallStrike = thisCallOpt.StrikePrice; //callChain = History<OptionChain>(thisCallOpt.Symbol, TimeSpan.FromDays(30)).FirstOrDefault(); //callGreeks = callChain.Contracts[thisCallOpt.Symbol]; if (thisCallStrike >= thisPutStrike) // only add put/call combinations where call strike is above put strike { //SSQRColumn thisSSQRColumn = buildSSQRColumn(thisPutOpt, thisCallOpt, putGreeks, callGreeks, whichExpiry, tradeDate, thisExDivDate, dividends, amtDividend, stockPrice, daysInTrade, interestCost); SSQRColumn thisSSQRColumn = buildSSQRColumn(thisPutOpt, thisCallOpt, whichExpiry, tradeDate, thisExDivDate, dividends, amtDividend, stockPrice, daysInTrade, interestCost); SSQRMatrix.Add(thisSSQRColumn); /* if (thisCallStrike == thisPutStrike) { Debug(" --------------------------------PUT/CALL EQUIVALENCE WARNING -------------------------------------------------------------"); Debug("the PUT AND CALL CONTRACTS HAVE THE SAME STRIKE " + thisPutStrike + "/" + thisCallStrike + " and the downside risk is " + thisSSQRColumn.downsideRisk + " with a potential profit of " + thisSSQRColumn.netIncome ); Debug(" The description reads thus: " + thisSSQRColumn.description); Debug(" ----------------------------------------------------------------------------------------------------------------------------"); } */ }// if thisCallStrike == thisPutStrike } // while callEnum } //while putEnum } // !null // reset the List<Option> vars for next dividend iteration (redundant? -- see above) //putOptionsList.Clear(); //callOptionsList.Clear(); } // for dividends 1 through 4 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, DateTime whichExpiry, DateTime tradeDate, DateTime exDate, int dividends, decimal amtDividend, decimal stockPrice, int daysInTrade, decimal intCost) //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 thisPutPrem = thisPutOpt.AskPrice; decimal thisCallPrem = thisCallOpt.BidPrice; decimal thisPutStrike = thisPutOpt.StrikePrice; decimal thisCallStrike = thisCallOpt.StrikePrice; SSQRColumn thisColumn = new SSQRColumn(); // get a new SSQRColumn thisColumn.putSymbol = thisPutOpt.Symbol; thisColumn.callSymbol = thisCallOpt.Symbol; thisColumn.putPremium = thisPutPrem; thisColumn.callPremium = thisCallPrem; thisColumn.putStrike = thisPutStrike; thisColumn.callStrike = thisCallStrike; thisColumn.exDate = exDate; //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.optExpiry = whichExpiry; 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: thisCallStrike -stockPrice; // loss=0 if cStrike>stkPrice, otherwise negative ***Loss (negative value) if ITM calls are assigned decimal netOptions = -thisPutPrem + thisCallPrem; /// netOptions equals negative putPrem (expense) plus positive call premium (income) thisColumn.netOptions = netOptions; thisColumn.netIncome = divDollars + netOptions + stockLossIfCalled - intCost; // Net Income in SSQR.xls subtracts interest cost but does not allow for appreciation to OTM call strike thisColumn.ROC = (divDollars + netOptions + stockLossIfCalled - intCost) / thisColumn.haircut; // store ROC for statistical analysis decimal downsideRisk = thisPutStrike - stockPrice + divDollars + netOptions - intCost; // downside risk is defined as the potential loss due to stock price depreciation _ 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 thisColumn.description1 = "Combination in " + thisPutOpt.Symbol.Underlying + " @ " + stockPrice + " is the " + thisPutStrike + "/" + thisCallStrike + " collar "; thisColumn.description2 = "," + thisPutOpt.Symbol.Underlying + "," + String.Format("{0:0.00}", stockPrice) + "," + exDate.ToString("MM/dd/yy") + "," + dividends + "," + String.Format("{0:0.00}", amtDividend) + "," + String.Format("{0:0.00}",divDollars) + "," + daysInTrade + ", " + String.Format("{0:0.00}", intCost) + ", " + whichExpiry.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.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.netIncome/thisColumn.haircut) + "," + String.Format("{0:0.00}", upsidePotential) + "," + String.Format("{0:0.00}", downsideRisk) + "," + String.Format("{0:0.00}",upsidePotential/-downsideRisk); 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) { DividendRecord nextExDateRec = exDividendDates.Where(d => DateTime.Compare(sliceTime, d.exDate)<0 && d.ticker == tickStr) .OrderBy(d => d.exDate) .FirstOrDefault(); DateTime nextExDate = nextExDateRec.exDate; stockDividendAmount = nextExDateRec.divAmt; 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 nextExDate, DateTime thisMonthExpiry){ // Initialize expiration date variables // DateTime firstExpiry = new DateTime(); DateTime secondExpiry = new DateTime(); DateTime thirdExpiry = new DateTime(); DateTime fourthExpiry = 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 (DateTime.Compare(nextExDate, thisMonthExpiry) <= 0) { firstExpiry = FindNextOptionsExpiry(thisMonthExpiry, 0); secondExpiry = FindNextOptionsExpiry(thisMonthExpiry, 3); thirdExpiry = FindNextOptionsExpiry(thisMonthExpiry, 6); fourthExpiry = FindNextOptionsExpiry(thisMonthExpiry, 9); }else { firstExpiry = FindNextOptionsExpiry(thisMonthExpiry, 1); secondExpiry = FindNextOptionsExpiry(thisMonthExpiry, 4); thirdExpiry = FindNextOptionsExpiry(thisMonthExpiry, 7); fourthExpiry = FindNextOptionsExpiry(thisMonthExpiry, 10); } expiries.Add(1, firstExpiry); expiries.Add(2, secondExpiry); expiries.Add(3, thirdExpiry); expiries.Add(4, fourthExpiry); 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; month = month + addedMonths; // Adjust if bigger than 12 if(month > 12){ month = month % 12; year = year + 1; } 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) { // 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())); return new DateTime(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) { int k = 1; Symbol optSymbol; var enumerator = thisChain.GetEnumerator(); //Debug(" |||||||||||||||||||||||||||||||| NEW OPTION SYMBOL CHAIN |||||||||||||||||||||||||||||||"); //Debug("There are " + thisChain.Count() + " options symbols in this list of chains. "); 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."); while (matrixEnum.MoveNext()) { thisColumn = matrixEnum.Current; //Debug("Iterated " + k + " times"); //Debug(thisColumn.description); 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."); Debug(",Ticker,Stock Price,Ex-Date,# Dividends,Dividend,Dollars,Days In,Interest,Expiry,PutStrike,PutASK,CallStrike,CallBid, PutDelta, CallDelta, NetOptions,Net Income,Haircut,ROC,Upside,Downside,ROR"); foreach (SSQRColumn thisColumn in thisOrdMatrix) { //Debug("Iterated " + k + " times"); Debug(thisColumn.description2); //Debug(" "); k++; if (k == 11) break; } } } }
using QuantConnect.Securities.Option; namespace QuantConnect.Algorithm.CSharp { public partial class CollarAlgorithm : QCAlgorithm { private bool goodThresh2 = false; /////////////////////////////////////////////////////////////////////////////////// // // KillTheCollar // //////////////////////////////////////////////////////////////////////////////////// public void KillTheCollar(TradePerfRec killRec, int killRecIndx, DateTime killDate, string reason) { if (haltProcessing) { Debug(" HALTED IN KILLTHECOLLAR "); } doTheTrade = true; var stockTicket = MarketOrder(killRec.uSymbol, -killRec.uQty); // sell the stock if (stockTicket.Status == OrderStatus.Filled) { 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); } doTheTrade = true; var closeCallTicket = MarketOrder(killRec.cSymbol, -killRec.cQty); // buy the calls if (closeCallTicket.Status == OrderStatus.Filled) { killRec.cEndPrice = closeCallTicket.AverageFillPrice; } doTheTrade = true; var closePutTicket = MarketOrder(killRec.pSymbol, -killRec.uQty/100); // sell the puts if (closePutTicket.Status == OrderStatus.Filled) { killRec.pEndPrice = closePutTicket.AverageFillPrice; } killRec.reasonForClose = reason; killRec.endDate = killDate; // set the end date of this collar Debug("-"); } /////////////////////////////////////////////////////////////////////////////////// // // RollTheCollar // //////////////////////////////////////////////////////////////////////////////////// public void RollTheCollar(Slice data, Symbol thisSymbol, string reason) { if (haltProcessing) { Debug(" HALTED 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.uSymbol.Equals(thisSymbol)).FirstOrDefault(); int oldTradeRecIndx = tradeRecs.IndexOf(oldTradeRec); Symbol oldShortCallSymb = oldTradeRec.cSymbol; Symbol oldLongPutSymb = oldTradeRec.pSymbol; // Cannot execute options spread orders at this time in QuantConnect, so do the collar as // individual legs // 1st sell the long put doTheTrade = true; var closePutTicket = MarketOrder(oldLongPutSymb, -oldTradeRec.pQty); // sell the existing puts if (closePutTicket.Status == OrderStatus.Filled) { oldTradeRec.pEndPrice = closePutTicket.AverageFillPrice; } // 2nd, buy back the long call doTheTrade = true; var closeCallTicket = MarketOrder(oldShortCallSymb, -oldTradeRec.cQty); if (closeCallTicket.Status == OrderStatus.Filled) { oldTradeRec.cEndPrice = closeCallTicket.AverageFillPrice; } // Keep the stock, but close this trade performance record. oldTradeRec.uEndPrice = stockPrice; oldTradeRec.reasonForClose = reason; oldTradeRec.isOpen = false; oldTradeRec.endDate = data.Time; // Put on a new collar and start a new trade performance record // make a new TradePerfRec tradeRecCount = tradeRecCount + 1; // increment trade record count TradePerfRec thisNewTPRec = new TradePerfRec(); thisNewTPRec.uSymbol = thisSymbol; // keep the underlying symbol 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.tradeRecCount = tradeRecCount; // count the trades thisNewTPRec.startDate = data.Time; // set the start date thisNewTPRec.pStrike = bestSSQRColumn.putStrike; thisNewTPRec.cStrike = bestSSQRColumn.callStrike; thisNewTPRec.expDate = bestSSQRColumn.optExpiry; // set the options Expiry thisNewTPRec.ROC = bestSSQRColumn.ROC; thisNewTPRec.ROR = bestSSQRColumn.ROR; thisNewTPRec.RORThresh = RORThresh; thisNewTPRec.ROCThresh = ROCThresh; thisNewTPRec.tradeCriteria = switchROC ? "ROC" : "ROR"; 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 Option logCorrespondingPut = AddCorrespondingPut(tradableCall); // Add the corresponding put here so system tracks its price for Ex-dividend approachment doTheTrade = true; var putTicket = MarketOrder(tradablePut, optionsToTrade); if (putTicket.Status == OrderStatus.Filled) { thisNewTPRec.pSymbol = tradablePut; thisNewTPRec.pStartPrice = putTicket.AverageFillPrice; thisNewTPRec.pQty = putTicket.QuantityFilled; } doTheTrade = true; var callTicket = MarketOrder(tradableCall, -optionsToTrade); if (callTicket.Status == OrderStatus.Filled) { thisNewTPRec.cSymbol = tradableCall; thisNewTPRec.cStartPrice = callTicket.AverageFillPrice; thisNewTPRec.cQty = callTicket.QuantityFilled; } /// roll is done. save the new trade performance record //IterateTradeRecord(thisNewTPRec); tradeRecs.Add(thisNewTPRec); } /////////////////////////////////////////////////////////////////////////////////// // // GetBestSSQR // //////////////////////////////////////////////////////////////////////////////////// public SSQRColumn GetBestSSQR(Slice thisSlice, Symbol thisStock, DateTime nextExDivDate) { // First get the underlying stock price in this Slice // Alex: we are both changing the same file so it's hard to get it. :D // Anyway, I replied to your last email. 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(" @@@@@@ BAD DATE IN GetBestSSQR"); } decimal stockPrice = thisSlice[thisStock].Price; // Second get its options symbols var allUnderlyingOptionsSymbols = OptionChainProvider.GetOptionContractList(thisStock, thisSlice.Time); if (allUnderlyingOptionsSymbols.Count() == 0) { SSQRColumn blankSSQR = new SSQRColumn(); return blankSSQR; } 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(nextExDivDate, thisMonthExpiry); // 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 new SSQRColumn(); /////////////////////// GET DELTAS AND OPEN INTEREST FROM thisSlice DATA //////////////////////// foreach (SSQRColumn ssqrC in ssqrMatrix) /// 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); } else { Debug(" HEY NO OPTIONS IN THIS SLICE " + thisSlice.Time); } if (thisSlice.OptionChains.TryGetValue(ssqrCallSymbol, out ssqrCallChain)) { } } /////////////////////// ATTEMPTS TO ISOLATE ITERATION TO TRADE EVENTS SOLELY /////////////////// //IOrderedEnumable<SSQRColumn> orderedSSQRMatrix = new IOrderedEnumerable<SSQRColumn>(); //List<SSQRColumn> orderedSSQRMatrix = new List<SSQRColumn>(); // Get the SSQRColumn with the best reward to risk SSQRColumn bestTradableColumn = new SSQRColumn(); if (switchROC ) { bestTradableColumn = ssqrMatrix.Where(p => p.netIncome > 0) .OrderByDescending(p => p.netIncome/p.haircut) .FirstOrDefault(); } else { bestTradableColumn = ssqrMatrix.Where(p => p.netIncome > 0) .OrderByDescending(p => p.upsidePotential/-p.downsideRisk) .FirstOrDefault(); } var thisROC = bestTradableColumn.netIncome/bestTradableColumn.haircut; var thisROR = bestTradableColumn.upsidePotential/-bestTradableColumn.downsideRisk; if (switchROC ) { goodThresh = (thisROC >= ROCThresh); } else { goodThresh = (thisROR >= RORThresh); } if (switchROC) { var orderedSSQRMatrix = ssqrMatrix.Where(p => p.netIncome > 0).OrderByDescending(p => p.netIncome/p.haircut); if (goodThresh) IterateOrderedSSQRMatrix(orderedSSQRMatrix); // Debugging Code -- Examine SSQRMatrix in reward/committed-capital descending order } else { /// ******************* what about where there is no downside risk? && p.downsideRisk > 0) var orderedSSQRMatrix = ssqrMatrix.Where(p => p.netIncome > 0 ).OrderByDescending(p => (p.downsideRisk == 0 ? p.upsidePotential/-.01M : p.upsidePotential/-p.downsideRisk)); if (goodThresh) IterateOrderedSSQRMatrix(orderedSSQRMatrix); // Debugging Code -- Examine SSQRMatrix in reward/risk descending order } return bestTradableColumn; } public void IterateTradeRecord(TradePerfRec tR) { //Debug("@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%"); Debug("@%@%@%@%@%@%@%@%@%@%@% TRADE PERFORMANCE RECORD @%@%@%@%@%@%@%@%@%@%@%@%"); Debug("@ OPEN @ uSymbol @ pSymbol @ cSymbol @ uQty @ pQty @ cQty @ uStartPrice @ pStartPrice @ cStartPrice"); Debug("@ " + tR.isOpen + "@ " + tR.uSymbol + "@ " + tR.pSymbol + "@ " + tR.cSymbol + "@ " + tR.uQty + "@ " + tR.pQty + "@ " + tR.cQty + "@ " + tR.uStartPrice + "@ " + tR.pStartPrice + "@ " + tR.cStartPrice + "@ "); Debug("@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%"); } } }
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; namespace QuantConnect.Algorithm.CSharp { public partial class CollarAlgorithm : QCAlgorithm { public class TradePerfRec { public Symbol uSymbol; // Underlying Symbol public bool isOpen; // Is the trade ongoing (open)? public bool isInitializer; // Is this the collar-initializing trade public int tradeRecCount; // counter for trade records -- use in the single-stock use case public DateTime startDate; // starting date for collar public DateTime endDate; // ending date for the collar public string reasonForClose; // reason why collar was killed (ITM options roll, etc.) public DateTime expDate; // expiration date for collar public Symbol pSymbol; // Put Symbol public Symbol cSymbol; // Call Symbol public decimal pStrike; // put strike public decimal cStrike; // call strike public decimal pDelta; // put Delta public decimal cDelta; // all Delta public decimal uQty; // number of underlying shares public decimal pQty; // number of put contracts public decimal cQty; // number of call contracts public decimal uStartPrice; // Underlying Price when trade put on public decimal pStartPrice; // Put Price when trade put on public decimal cStartPrice; // Call Price when trade put on public decimal uEndPrice; // Underlying Price when trade taken off public decimal pEndPrice; // Put Price when trade taken off public decimal cEndPrice; // Call Price when trade taken off public int numDividends; // # of dividends collected during the trade public decimal divIncome; // $'s collected in Dividend income during the trade public decimal betaValue; // beta value of underlying when trade put on public decimal RORThresh; // Threshold for ROR public decimal ROCThresh; // Threshold for ROC public string tradeCriteria; // ROR or ROC public decimal ROR; // ROR calculation from SSQR Matrix public decimal ROC; // ROC calculation from SSQR Matrix public decimal stockADX; // Average Directional Index Value public decimal stockADXR; // Average Directional Index Rating public decimal stockOBV; // On Balance Volume public decimal stockAD; // Accumulation/Distribution public decimal stockADOSC; // Accumulation/Distribution Oscillator public decimal stockSTO; // Stochastic value public decimal stockVariance; // Variance of underlying stock } public string ConvertTradePerfRec(List<TradePerfRec> tPR) { string tPRString = ""; string jasonString = ""; tPRString = ",^^^,"; tPRString = tPRString + "uSymbol,"; tPRString = tPRString + "isOpen,"; tPRString = tPRString + "isInitializer,"; tPRString = tPRString + "tradeRecCount,"; tPRString = tPRString + "StartDate,"; tPRString = tPRString + "EndDate,"; tPRString = tPRString + "reasonForClose,"; tPRString = tPRString + "Expiration,"; tPRString = tPRString + "pSymbol,"; tPRString = tPRString + "cSymbol,"; tPRString = tPRString + "pStrike,"; tPRString = tPRString + "cStrike,"; tPRString = tPRString + "pDelta,"; tPRString = tPRString + "cDelta,"; tPRString = tPRString + "uQty,"; tPRString = tPRString + "pQty,"; tPRString = tPRString + "cQty,"; tPRString = tPRString + "uStartPrice,"; tPRString = tPRString + "pStartPrice,"; tPRString = tPRString + "cStartPrice,"; tPRString = tPRString + "uEndPrice,"; tPRString = tPRString + "pEndPrice,"; tPRString = tPRString + "cEndPrice,"; tPRString = tPRString + "numDividends,"; tPRString = tPRString + "divIncome,"; tPRString = tPRString + "betaValue,"; tPRString = tPRString + "RORThresh,"; tPRString = tPRString + "ROCThresh,"; tPRString = tPRString + "tradeCriteria,"; tPRString = tPRString + "ROR,"; tPRString = tPRString + "ROC,"; tPRString = tPRString + "ADXValue,"; tPRString = tPRString + "ADXRValue,"; tPRString = tPRString + "OBVValue,"; tPRString = tPRString + "ADValue,"; tPRString = tPRString + "ADOSCValue,"; tPRString = tPRString + "stochValue,"; tPRString = tPRString + "stockVariance,"; //tPRString = tPRString + Environment.NewLine; 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 = ",^^^"; foreach (var field in typeof(TradePerfRec).GetFields()) { //Console.WriteLine("{0} = {1}", field.Name, field.GetValue(thisPerfRec)); jasonString = jasonString + "\"" + field.Name + "\":\"" + field.GetValue(thisPerfRec) + "\""; tPRString = tPRString + ", " + field.GetValue(thisPerfRec); } jasonString = jasonString + "}," + Environment.NewLine; Debug(tPRString); } return jasonString; } } }