Overall Statistics |
Total Trades 12 Average Win 0.04% Average Loss -0.02% Compounding Annual Return 15.292% Drawdown 1.000% Expectancy 1.988 Net Profit 1.104% Sharpe Ratio 4.028 Loss Rate 20% Win Rate 80% Profit-Loss Ratio 2.73 Alpha 0.344 Beta -14.53 Annual Standard Deviation 0.028 Annual Variance 0.001 Information Ratio 3.432 Tracking Error 0.028 Treynor Ratio -0.008 Total Fees $11.75 |
using QuantConnect.Securities.Option; namespace QuantConnect.Algorithm.CSharp { /// <summary> /// Basic template algorithm simply initializes the date range and cash. This is a skeleton /// framework you can use for designing an algorithm. /// </summary> public class ShortStraddleDeltaHedge : QCAlgorithm { private decimal PREMIUM = 0.01m; private int MAX_EXPIRY = 30; private int _no_K = 2; private decimal LEV = 1.0m; private decimal DELTA_THRESHOLD = 0.05m; private decimal MIN_THRESHOLD_LIQUIDATE = 0.05m; private decimal OPTION_MULTIPLE = 100.0m; private bool _assignedOption = false; private DateTime expiry; private DateTime last_trading_day; private Equity equity; private Symbol equity_symbol; private Symbol option_symbol; private Symbol put; private Symbol call; private decimal previous_delta = 0.0m; private decimal delta; /// <summary> /// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized. /// </summary> public override void Initialize() { SetStartDate(2017, 1, 1); //Set Start Date SetEndDate(2017, 12, 30); //Set End Date SetCash(100000); //Set Strategy Cash equity = AddEquity("SPY", Resolution.Minute); equity.SetDataNormalizationMode(DataNormalizationMode.Raw); // IMPORTANT: default equity_symbol = equity.Symbol; // Add options var option = AddOption("SPY", Resolution.Minute); option_symbol = option.Symbol; // Debug("Options Symbol: " + option.Symbol.ToString()); // set our strike/expiry filter for this option chain option.SetFilter(u => u.IncludeWeeklys() .Strikes(-this._no_K, this._no_K) .Expiration(TimeSpan.Zero, TimeSpan.FromDays(MAX_EXPIRY))); option.PriceModel = OptionPriceModels.CrankNicolsonFD(); SetWarmUp(TimeSpan.FromDays(3)); //Warm up 7 days of data. put = null; call = null; // schedule the closing of options on last trading day Schedule.On(DateRules.EveryDay(equity_symbol), TimeRules.BeforeMarketClose(equity_symbol, 10), () =>{ this.close_options(); }); } /** * Liquidate opts (with some value) and underlying */ private void close_options() { if (last_trading_day != Time.Date) return; Log("On last trading day: liquidate options with value and underlying"); // liquidate options (if invested and in the money [otherwise their price is min of $0.01) foreach (Securities.SecurityHolding holding in Portfolio.Values) { if (holding.Invested) { if (Securities[holding.Symbol].AskPrice >= MIN_THRESHOLD_LIQUIDATE) { Liquidate(holding.Symbol); } } } if (Portfolio[this.equity_symbol].Invested) { Liquidate(equity.Symbol); } } /// <summary> /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. /// </summary> /// <param name="data">Slice object keyed by symbol containing the stock data</param> public override void OnData(Slice slice) { try { if (IsWarmingUp) return; // #1. deal with any early assignments if (_assignedOption) { // can't figure out Linq Portfolio.Where(x => x.Value.Invested).Foreach(x => {}) ?? foreach (Securities.SecurityHolding holding in Portfolio.Values) { if (holding.Invested) { Liquidate(holding.Symbol); } } _assignedOption = false; } // #2. sell options, if none if (!Portfolio.Invested) { //Log("get contract"); get_contracts(slice); if (call == null || put == null) { return; } decimal unit_price = Securities[equity_symbol].Price * OPTION_MULTIPLE; int qnty = Convert.ToInt32(Portfolio.TotalPortfolioValue / unit_price); if (call != null) Sell(call, qnty); if (put != null) MarketOrder(put, -qnty); } // # 3. delta-hedge any existing option if (Portfolio.Invested && HourIsMinute(10, 1)) { this.get_greeks(slice); if (Math.Abs(previous_delta - delta) > DELTA_THRESHOLD) { Debug("delta_hedging: call: "+call+", put: "+put+", delta: "+ delta); Log("delta_hedging: call: "+call+", put: "+put+", delta: "+ delta); SetHoldings(equity_symbol, delta); previous_delta = delta; // update previous } } } catch (Exception e) { Error(e.ToString()); } } /** * */ private bool HourIsMinute(int hour, int minute) { return Time.Hour == hour && Time.Minute == minute; } /** * Get ATM call and put */ private void get_contracts(Slice slice) { // OptionChain chain; foreach (KeyValuePair<Symbol, OptionChain> chains in slice.OptionChains) { if (chains.Key != option_symbol) continue; OptionChain chain = chains.Value; decimal spot_price = chain.Underlying.Price; Log("spot_price: " + spot_price); // 1. get furthest expiry OptionContract[] sortedByExpiry = chain .OrderByDescending(x => x.Expiry).ToArray(); expiry = sortedByExpiry[0].Expiry.Date; Debug("Expiry: " + expiry.ToShortDateString()); last_trading_day = this.getLastTradingDay(expiry); // // get contracts with further expiry and sort them by strike OptionContract[] sortedByStrike = chain .Where(x => x.Expiry == expiry) .OrderBy(x => x.Strike).ToArray(); Log("Expiry used "+expiry.ToShortDateString()+" and shortest "+sortedByExpiry[sortedByExpiry.Length-1].Expiry.Date.ToShortDateString()); Debug("Expiry used "+expiry.ToShortDateString()+" and shortest "+sortedByExpiry[sortedByExpiry.Length-1].Expiry.Date.ToShortDateString()); // // 2a. get the ATM closest CALL to short OptionContract[] callOptionContracts = sortedByStrike.Where(x => x.Right == OptionRight.Call && x.Strike >= spot_price).ToArray(); if (callOptionContracts.Length > 0) { call = callOptionContracts[0].Symbol; } // 2b. get the ATM closest put to short OptionContract[] putOptionContracts = sortedByStrike.Where(x => x.Right == OptionRight.Put && x.Strike <= spot_price).ToArray(); if (putOptionContracts.Length > 0) { put = putOptionContracts[putOptionContracts.Length - 1].Symbol; } } } /** * */ private void get_greeks(Slice slice) { if (put == null || call == null) return; OptionChain chain; if (slice.OptionChains.TryGetValue(option_symbol, out chain)) { var traded_contracts = chain.Where(x => x.Symbol == put || x.Symbol == call); if (traded_contracts == null) return; delta = traded_contracts.Sum(x => x.Greeks.Delta); } } /** * */ private DateTime getLastTradingDay(DateTime expiry) { Debug("Checking Expiry: " + expiry.ToShortDateString()); //# American options cease trading on the third Friday, at the close of business //# - Weekly options expire the same day as their last trading day, which will usually be a Friday (PM-settled), [or Mondays? & Wednesdays?] //# //# SPX cash index options (and other cash index options) expire on the Saturday following the third Friday of the expiration month. //# However, the last trading day is the Thursday before that third Friday. Settlement price Friday morning opening (AM-settled). //# http://www.daytradingbias.com/?p=84847 if (DayOfWeek.Saturday == expiry.DayOfWeek) { Debug("Expiry on Saturday"); return expiry.AddDays(-1); } if (TradingCalendar.GetDaysByType(TradingDayType.PublicHoliday, expiry, expiry) .ToList().Count != 0) { Debug("Expiry on Holiday"); return expiry.AddDays(-1); } return expiry; } /** * */ } }