Overall Statistics |
Total Trades 1564 Average Win 1.81% Average Loss -0.04% Compounding Annual Return -80.892% Drawdown 25.600% Expectancy -0.881 Net Profit -23.970% Sharpe Ratio -2.078 Loss Rate 100% Win Rate 0% Profit-Loss Ratio 45.69 Alpha -1.148 Beta -0.374 Annual Standard Deviation 0.57 Annual Variance 0.325 Information Ratio -2.163 Tracking Error 0.594 Treynor Ratio 3.171 Total Fees $391.00 |
using System; using System.Linq; using QuantConnect.Data; using QuantConnect.Data.Market; using QuantConnect.Orders; using QuantConnect.Securities; using QuantConnect.Securities.Option; namespace QuantConnect.Algorithm.CSharp { public class IronCondor { public decimal premium = 0; public OptionContract shortCall; public OptionContract longCall; public OptionContract shortPut; public OptionContract longPut; } /// <summary> /// Implementation of Iron Condor https//www.tastytrade.com/tt/shows/market-measures/episodes/backtesting-iron-condors-in-spx-06-29-2016 /// </summary> public class TastyIronCondorAlgorithm:QCAlgorithm { private const string UnderlyingTicker = "SPY"; const int contracts = 1; const decimal shortDelta = .3M; const decimal longDelta = .05M; const decimal cash = 10000; const decimal stopProfit = .5M; const decimal stopLoss = 2M; public readonly Symbol Underlying = QuantConnect.Symbol.Create(UnderlyingTicker, SecurityType.Equity, Market.USA); public readonly Symbol OptionSymbol = QuantConnect.Symbol.Create(UnderlyingTicker, SecurityType.Option, Market.USA); IronCondor ironCondor; public override void Initialize() { this.SetStartDate(2015, 1, 1); this.SetEndDate(2015, 2, 21); this.SetCash(cash); var equity = this.AddEquity(UnderlyingTicker); var option = this.AddOption(UnderlyingTicker); // TODO monthly only option.SetFilter(universe => from symbol in universe // .WeeklysOnly() .Expiration(TimeSpan.FromDays(30), TimeSpan.FromDays(40)) select symbol); option.PriceModel = OptionPriceModels.CrankNicolsonFD(); // use the underlying equity as the benchmark this.SetBenchmark(equity.Symbol); this.SetWarmUp(TimeSpan.FromDays(10)); } public override void OnData(Slice slice) { if(this.IsWarmingUp) { return; } if (!this.Portfolio.Invested) { this.Enter(slice); } else { this.Exit(); } } private OptionContract FindShortPut(Slice slice) { OptionChain chain; if (slice.OptionChains.TryGetValue(this.OptionSymbol, out chain)) { // find put contract with farthest expiration return ( from optionContract in chain .OrderByDescending(x => x.Expiry) .ThenByDescending(x => x.Strike) where optionContract.Right == OptionRight.Put where optionContract.Greeks.Delta >= -shortDelta select optionContract ).FirstOrDefault(); } return null; } private OptionContract FindLongPut(Slice slice, DateTime expirationDate) { OptionChain chain; if (slice.OptionChains.TryGetValue(this.OptionSymbol, out chain)) { // find put contract with farthest expiration return ( from optionContract in chain .OrderByDescending(x => x.Strike) where optionContract.Expiry == expirationDate where optionContract.Right == OptionRight.Put where optionContract.Greeks.Delta >= -longDelta select optionContract ).FirstOrDefault(); } return null; } private OptionContract FindShortCall(Slice slice, DateTime expirationDate) { OptionChain chain; if (slice.OptionChains.TryGetValue(this.OptionSymbol, out chain)) { // find put contract with farthest expiration return ( from optionContract in chain .OrderByDescending(x => x.Strike) where optionContract.Expiry == expirationDate where optionContract.Right == OptionRight.Call where optionContract.Greeks.Delta <= shortDelta select optionContract ).LastOrDefault(); } return null; } private OptionContract FindLongCall(Slice slice, DateTime expirationDate) { OptionChain chain; if (slice.OptionChains.TryGetValue(this.OptionSymbol, out chain)) { // find put contract with farthest expiration return ( from optionContract in chain .OrderByDescending(x => x.Strike) where optionContract.Expiry == expirationDate where optionContract.Right == OptionRight.Call where optionContract.Greeks.Delta <= longDelta select optionContract ).LastOrDefault(); } return null; } private void Enter(Slice slice) { OptionContract shortPut = this.FindShortPut(slice); if(shortPut == null){ return; } DateTime expirationDate = shortPut.Expiry; OptionContract longPut = this.FindLongPut(slice, expirationDate); if(longPut == null){ return; } OptionContract shortCall = this.FindShortCall(slice, expirationDate); if(shortCall == null){ return; } OptionContract longCall = this.FindLongCall(slice, expirationDate); if(longCall == null){ return; } this.ironCondor = new IronCondor(); this.ironCondor.shortPut = shortPut; this.ironCondor.longPut = longPut; this.ironCondor.shortCall = shortCall; this.ironCondor.longCall = longCall; this.ironCondor.premium = shortCall.BidPrice + shortPut.BidPrice - longCall.AskPrice - longPut.AskPrice; // enter trade MarketOrder(longCall.Symbol, contracts); MarketOrder(shortCall.Symbol, -contracts); MarketOrder(shortPut.Symbol, -contracts); MarketOrder(longPut.Symbol, contracts); Log("premium=" + this.ironCondor.premium); Log("longCall.Delta=" + this.ironCondor.longCall.Greeks.Delta); Log("shortCall.Delta=" + this.ironCondor.shortCall.Greeks.Delta); Log("shortPut.Delta=" + this.ironCondor.shortPut.Greeks.Delta); Log("longPut.Delta=" + this.ironCondor.longPut.Greeks.Delta); } private void Exit() { var pnl = this.ironCondor.premium - (this.ironCondor.shortCall.BidPrice + this.ironCondor.shortPut.BidPrice - this.ironCondor.longCall.AskPrice - this.ironCondor.longPut.AskPrice); // % of premium that can be taken as profit var gain = pnl / this.ironCondor.premium; var closing = false; if(gain >= stopProfit) { closing = true; Log("Take " + stopProfit + " of max profit"); } else if(pnl <= -(this.ironCondor.premium * stopLoss)) { closing = true; Log("Stop loss: " + stopLoss); } else if(this.ironCondor.shortCall.Expiry.Date == Time.Date) { closing = true; Log("Close before expiration"); } if(closing) { MarketOrder(this.ironCondor.longCall.Symbol, -contracts); MarketOrder(this.ironCondor.shortCall.Symbol, contracts); MarketOrder(this.ironCondor.shortPut.Symbol, contracts); MarketOrder(this.ironCondor.longPut.Symbol, -contracts); this.ironCondor = null; } } } }