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