Overall Statistics
Total Trades
5
Average Win
4.94%
Average Loss
-2.74%
Compounding Annual Return
-6.888%
Drawdown
2.100%
Expectancy
-0.066
Net Profit
-0.740%
Sharpe Ratio
-0.791
Loss Rate
67%
Win Rate
33%
Profit-Loss Ratio
1.80
Alpha
-0.156
Beta
6.711
Annual Standard Deviation
0.067
Annual Variance
0.004
Information Ratio
-1.022
Tracking Error
0.067
Treynor Ratio
-0.008
Total Fees
$26.25
namespace net.phobot.quant {
	
	public class ExecutionData
	{
		public decimal Price { get; set; }
		public decimal Quantity { get; set; }
	}
	
    public class MyExecutionModel : ExecutionModel
    {
		private static decimal LEG_PRICE_LOWER_LIMIT = 0.03M;
		
        private readonly Dictionary<Symbol, Security> _symbolData;
        private Dictionary<DateTime, IPortfolioTarget[]> _targetsCollection;
        
        public MyExecutionModel()
        {
            _symbolData = new Dictionary<Symbol, Security>();
            _targetsCollection = new Dictionary<DateTime, IPortfolioTarget[]>();
        }
        
        public override void Execute(QCAlgorithmFramework algorithm, IPortfolioTarget[] targets)
        {
        	var prices_by_symbol = new Dictionary<Symbol,Dictionary<Symbol,ExecutionData>>();
            foreach (var target in targets)
            {
                var existing = algorithm.Securities[target.Symbol].Holdings.Quantity
                    + algorithm.Transactions.GetOpenOrders(target.Symbol).Sum(o => o.Quantity);
                var quantity = target.Quantity - existing;
                if (quantity != 0)
                {
                	var limit_price = 0.0m; 

                	if (target.Symbol.EndsWith("181019C00034000"))
                	{
                		limit_price = 2.35m;
                	}
                	if (target.Symbol.EndsWith("181019C00036000"))
                	{
                		limit_price = 1.20m;
                	}                	
					
            		if (target.Symbol.HasUnderlying)
            		{
            			var underlying = target.Symbol.Underlying;
            			if(! prices_by_symbol.ContainsKey(underlying))
            			{
            				prices_by_symbol[underlying] = new Dictionary<Symbol,ExecutionData>();
            			}
            			prices_by_symbol[underlying][target.Symbol] = 
            				new ExecutionData{ Price = limit_price, Quantity = quantity};
            		}
            		else
            		{
            			if (Math.Abs(limit_price) < LEG_PRICE_LOWER_LIMIT)
            			{
                			algorithm.LimitOrder(target.Symbol, quantity, limit_price);
            			}
            		}

                }
            }
            
            foreach (var underlying in prices_by_symbol.Keys)
            {
            	var legs = prices_by_symbol[underlying];
	           	if(legs.Values.Select(data => data.Price).Where(price => Math.Abs(price) < LEG_PRICE_LOWER_LIMIT).Any())
            	{
            		continue;
            	}
            	
        		foreach (var leg_symbol in legs.Keys)
        		{
        			var execution_data = legs[leg_symbol];
        			algorithm.LimitOrder(leg_symbol, execution_data.Quantity, execution_data.Price);

        			var log_msg = $"Entered limit order for {execution_data.Quantity} contracts of {leg_symbol} at price {execution_data.Price}";
        			Security security;
		        	if (_symbolData.TryGetValue(leg_symbol, out security))
		        	{
		        		log_msg += $" Bid: {security.BidPrice} Ask: {security.AskPrice}";
		        	}
        			algorithm.Log(log_msg);
        		}
            }
        }
        
        public override void OnSecuritiesChanged(QCAlgorithmFramework algorithm, SecurityChanges changes)
        {
            foreach (var added in changes.AddedSecurities)
            {
                if (!_symbolData.ContainsKey(added.Symbol))
                {
                    _symbolData[added.Symbol] = added;
                }
            }

            foreach (var removed in changes.RemovedSecurities)
            {
                if (_symbolData.ContainsKey(removed.Symbol) && IsSafeToRemove(algorithm, removed.Symbol))
                {
                    _symbolData.Remove(removed.Symbol);
                }
            }
        }
        
        protected virtual bool IsSafeToRemove(QCAlgorithmFramework algorithm, Symbol symbol)
        {
            // confirm the security isn't currently a member of any universe
            return !algorithm.UniverseManager.Any(kvp => kvp.Value.ContainsMember(symbol));
        }
    }
}
namespace net.phobot.quant {
        
	public class MyPortfolioConstructionModel : PortfolioConstructionModel
	{
	    public override IEnumerable<IPortfolioTarget> CreateTargets(QCAlgorithmFramework algorithm, Insight[] insights)
	    {
	    	var number_of_contracts = 21;
	    	return insights.Select(insight => new PortfolioTarget(insight.Symbol, ((int) insight.Direction * number_of_contracts)));
	    }
	}
}
using QuantConnect.Securities.Option;

namespace net.phobot.quant {

	public class MyAlphaModel : AlphaModel
	{
		List<Security> _securities = new List<Security>();

	    public override void OnSecuritiesChanged(QCAlgorithmFramework algorithm, SecurityChanges changes)
	    {
	    	foreach(var added in changes.AddedSecurities)
	        {
	        	_securities.Add(added);
	        }
	        
	        foreach(var removed in changes.RemovedSecurities)
	        {
	        	_securities.RemoveAll(item => item.Equals(removed));
	        }
	    }
	    
	    public override IEnumerable<Insight> Update(QCAlgorithmFramework algorithm, Slice data)
        {
	        var insights = new List<Insight>();
	
	        var date_str = algorithm.Time.ToString("yyyy-MM-dd HH:mm");
	        if (date_str.Equals("2018-09-18 09:31"))
	        {
	        	foreach (var security in _securities)
	        	{
	        		if (security.Symbol.EndsWith("181019C00034000"))
	        		{
	        			insights.Add(new Insight(security.Symbol, new TimeSpan(6, 30, 0), InsightType.Price, InsightDirection.Down));
	        		}
	        		if (security.Symbol.EndsWith("181019C00036000"))
	        		{
	        			insights.Add(new Insight(security.Symbol, new TimeSpan(6, 30, 0), InsightType.Price, InsightDirection.Up));
	        		}
	         	}
	        }
	        
	        // If we've been assigned or have exercised and are holding stock, liquidate the entire position:
	        foreach (var underlying_symbol in UnderlyingsOfHoldings(algorithm))
	        {
	            var equity_holdings = EquityHoldings(underlying_symbol, algorithm);
	            if (equity_holdings.Any())
	            {
	            	var option_holdings = OptionHoldingsMatching(underlying_symbol, algorithm);
	                LiquidatePosition(option_holdings, algorithm);
	                LiquidateEquity(underlying_symbol, algorithm);
	            }
  	        }
	        
	        return insights;
        }
        

	    private IEnumerable<Symbol> UnderlyingsOfHoldings(QCAlgorithmFramework algorithm)
	    {
	        var option_underlyings = 
	        	algorithm.Securities.Keys
    				.Where(symbol => symbol.HasUnderlying)
    				.Where(symbol => algorithm.Securities[symbol].Holdings.Quantity != 0)
    				.Select(symbol => symbol.Underlying);
	    	var underlying_symbols = new HashSet<Symbol>(option_underlyings);
	    	
	        var equity_holdings = 
	        	algorithm.Securities.Keys
    				.Where(symbol => ! symbol.HasUnderlying)
    				.Where(symbol => algorithm.Securities[symbol].Holdings.Quantity != 0);
			var equity_symbols = new HashSet<Symbol>(equity_holdings);
			
	        underlying_symbols.UnionWith(equity_symbols);
	        return underlying_symbols;
	    }
	        
	    private IEnumerable<OptionHolding> OptionHoldingsMatching(Symbol equity_symbol, QCAlgorithmFramework algorithm)
	    {
	        var holdings = 
	        	algorithm.Securities.Keys
    				.Where(symbol => symbol.HasUnderlying)
    				.Where(symbol => symbol.Underlying.Value.Equals(equity_symbol.Value))
    				.Where(symbol => algorithm.Securities[symbol].Holdings.Quantity != 0)
    				.Select(symbol => (OptionHolding) algorithm.Securities[symbol].Holdings);

	        return holdings;
	    }
	
	    private IEnumerable<SecurityHolding> EquityHoldings(Symbol equity_symbol, QCAlgorithmFramework algorithm)
	    {
	        var holdings = 
	        	algorithm.Securities.Keys
    				.Where(symbol => ! symbol.HasUnderlying)
    				.Where(symbol => symbol.Value.Equals(equity_symbol.Value))
    				.Where(symbol => algorithm.Securities[symbol].Holdings.Quantity != 0)
    				.Select(symbol => algorithm.Securities[symbol].Holdings);

	        return holdings;
	    }

	    private void LiquidatePosition(IEnumerable<SecurityHolding> security_holdings, QCAlgorithmFramework algorithm)
	    {
	         foreach (var holding in security_holdings)
	         {
	             algorithm.RemoveSecurity(holding.Symbol);
	         }
	    }
	
	    private void LiquidateEquity(Symbol symbol, QCAlgorithmFramework algorithm)
	    {
			// algorithm.RemoveSecurity(symbol);
	        algorithm.SetHoldings(symbol, 0);
	    }
	}
}
namespace net.phobot.quant {
	
	static class ExtensionMethods
	{
		public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dict, K key)
		{
		    return dict.GetValueOrDefault(key, default(V));
		}
		
		public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dict, K key, V defVal)
		{
		    V value;
		    return dict.TryGetValue(key, out value) ? value : defVal;
		}
	
		public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dict, K key, Func<V> defValSelector)
		{
		    V value;
		    return dict.TryGetValue(key, out value) ? value : defValSelector();
		}
	}
}
namespace net.phobot.quant
{
    public class SchaefferCs : QCAlgorithm
    {

        public override void Initialize()
        {
	        SetStartDate(2018, 9, 11);
	        SetEndDate(2018, 10, 18);
	        
	        SetCash(100000);
	        UniverseSettings.Resolution = Resolution.Hour;
	
	        SetUniverseSelection(new MyOptionUniverseSelectionModel());
	        SetAlpha(new MyAlphaModel());
	        SetPortfolioConstruction(new MyPortfolioConstructionModel());
	        SetExecution(new MyExecutionModel());
	        SetRiskManagement(new NullRiskManagementModel());
        }
    }
}
namespace net.phobot.quant {

    public class MyOptionUniverseSelectionModel : FundamentalUniverseSelectionModel
    {
    	
	    public MyOptionUniverseSelectionModel(
	    	Boolean filterFineData = true, 
	    	UniverseSettings universeSettings = null, 
			ISecurityInitializer securityInitializer = null): base(filterFineData, universeSettings, securityInitializer)
		{
			
		}
	
	    public override IEnumerable<Symbol> SelectCoarse(QCAlgorithmFramework algorithm, IEnumerable<CoarseFundamental> coarse)
	    {
	        var filtered = coarse
	        		.Where(asset => asset.Symbol.Value.Equals("TRN"))
	        		.Select(asset => asset.Symbol);
	        return filtered;
	    }
	      
	    public override IEnumerable<Symbol> SelectFine(QCAlgorithmFramework algorithm, IEnumerable<FineFundamental> fine)
	    {
	        var current_date = NextTradingDay(algorithm);
	        var equity_symbols = fine.Select(asset => asset.Symbol);
	        var symbols_to_return = new List<Symbol>(equity_symbols);

	        foreach (Symbol equity_symbol in equity_symbols)
	        {
	        	var symbol_string = equity_symbol.Value;
		        var equity = algorithm.AddEquity(symbol_string);
		        equity.SetDataNormalizationMode(DataNormalizationMode.Raw); // Remove warning messages

	            var contracts = 
	            	algorithm.OptionChainProvider
	            		.GetOptionContractList(equity_symbol, current_date)
	            		.Where(symbol => symbol.EndsWith("181019C00034000") || symbol.EndsWith("181019C00036000"))
	            		.OrderBy(symbol => symbol.ID.StrikePrice)
	            		.ToList();
	            		
				if(contracts.Count() == 0)
				{
					continue;
				}
				
		        var short_option = contracts[0];
	            algorithm.AddOptionContract(short_option);
	            symbols_to_return.Add(short_option);
	            
		        var long_option = contracts[1];
	            algorithm.AddOptionContract(long_option);
	            symbols_to_return.Add(long_option);
	        }
	        
	        return symbols_to_return;
	    }
	    
	    private DateTime NextTradingDay(QCAlgorithmFramework algorithm)
	    {
	        var tomorrow = algorithm.Time.AddDays(1);
	        var five_days_after = tomorrow.AddDays(5);
	        var next_trading_days = algorithm.TradingCalendar.GetDaysByType(TradingDayType.BusinessDay, tomorrow, five_days_after);
	        return next_trading_days.First().Date;
	    }
    }
}