Overall Statistics |
Total Trades 3 Average Win 1.61% Average Loss 0% Compounding Annual Return 6.606% Drawdown 1.200% Expectancy 0 Net Profit 0.542% Sharpe Ratio 1.038 Loss Rate 0% Win Rate 100% Profit-Loss Ratio 0 Alpha 0.065 Beta 0.082 Annual Standard Deviation 0.059 Annual Variance 0.003 Information Ratio 0.301 Tracking Error 0.381 Treynor Ratio 0.745 Total Fees $0.75 |
/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ using System; using System.Linq; using QuantConnect.Data; using QuantConnect.Data.Market; using QuantConnect.Orders; using QuantConnect.Securities; namespace QuantConnect.Algorithm.CSharp { public class LongCallOnStockDipAlgorithm : QCAlgorithm { private const int NUM_DAYS = 5; private const string UnderlyingTicker = "GOOG"; public readonly Symbol Underlying = QuantConnect.Symbol.Create(UnderlyingTicker, SecurityType.Equity, Market.USA); public Symbol OptionSymbol; private const decimal GoalRate = 0.3M; private const decimal GoalFactor = 1 + GoalRate; private Maximum MaxWindow = new Maximum(NUM_DAYS); private DateTime LastPlot = DateTime.MinValue; private string OrderTrackingDebugStr = ""; Chart plotter = new Chart("Plotter"); bool HasOrderOrHolding = false; public override void Initialize() { SetStartDate(2015, 08, 1); SetEndDate(2015, 08, 31); SetCash(100000); // Setup underlying equity var equity = AddEquity(UnderlyingTicker); Securities[UnderlyingTicker].SetDataNormalizationMode(DataNormalizationMode.Raw); // Required for options backtesting // Must add option universe filter var option = AddOption(UnderlyingTicker); OptionSymbol = option.Symbol; option.SetFilter(universe => from symbol in universe // default is monthly expirations only .Expiration(TimeSpan.FromDays(30), TimeSpan.FromDays(50)) where symbol.ID.OptionRight == OptionRight.Call where Math.Abs(symbol.ID.StrikePrice - universe.Underlying.Price) < 20 select symbol); // use the underlying equity as the benchmark SetBenchmark(equity.Symbol); // Use a daily consolidator to track the maximum value per day var dailyConsolidator = new TradeBarConsolidator(TimeSpan.FromDays(1)); dailyConsolidator.DataConsolidated += OnDataDaily; SubscriptionManager.AddConsolidator(equity.Symbol, dailyConsolidator); // Plot useful values plotter.AddSeries(new Series("Current-Value", SeriesType.Line, index: 0)); plotter.AddSeries(new Series(NUM_DAYS.ToString()+"_MaxValue", SeriesType.Line, index: 0)); plotter.AddSeries(new Series("PercentOfMax", SeriesType.Line, index: 1)); plotter.AddSeries(new Series("HoldingGoal", SeriesType.Line, index: 2)); plotter.AddSeries(new Series("HoldingValue", SeriesType.Line, index: 2)); AddChart(plotter); // Useful for debugging.. Show price action on open position. Schedule.On(DateRules.EveryDay(UnderlyingTicker),TimeRules.Every(new TimeSpan(2, 0, 0)), () => // Every 2 hours { ShowOrderTracking(); }); } public override void OnData(Slice slice) { if ((MaxWindow.IsReady) && (slice.Bars.ContainsKey(UnderlyingTicker))) { var currentChangePercent = ((slice.Bars[UnderlyingTicker].Close - MaxWindow.Current.Value) / MaxWindow.Current.Value); if ((Time - LastPlot).TotalSeconds > (900)) // Rate limit plotting to 15 minutes { LastPlot = Time; Plot("Plotter", "Current-Value", slice.Bars[UnderlyingTicker].Close); Plot("Plotter", NUM_DAYS.ToString()+"_MaxValue", MaxWindow.Current.Value); Plot("Plotter", "PercentOfMax", currentChangePercent); } ExecuteLong(slice, currentChangePercent); ExecuteShort(slice); } } private void OnDataDaily(object sender, TradeBar consolidated) { // Track the daily maximum value if (consolidated.Symbol == UnderlyingTicker) { MaxWindow.Update(Time, consolidated.High); } } public void ExecuteLong(Slice slice, Decimal percentOfMax) { if (HasOrderOrHolding) { return; } // We already have a position if ((percentOfMax < -0.025m) && (percentOfMax > -0.125m) // -2.5 to -12.5% ) { OptionChain chain; if (slice.OptionChains.TryGetValue(OptionSymbol, out chain)) { var contract = ( from optionContract in chain.OrderByDescending(x => x.Strike) //where optionContract.Right == OptionRight.Call // not necessary here since its in our filter already //where Math.Abs(optionContract.Strike - chain.Underlying.Price) < 20 // not necessary here since its in our filter already select optionContract ).FirstOrDefault(); if (contract != null) { HasOrderOrHolding = true; MarketOrder(contract.Symbol, 1); } } } } public void ExecuteShort(Slice slice) { foreach (var security_option in Securities) { if ((security_option.Value.Symbol.SecurityType == SecurityType.Option) && (security_option.Value.Invested)) { Decimal midPrice = MidPrice(security_option.Value.BidPrice, security_option.Value.AskPrice); var currentGoal = ((security_option.Value.Holdings.AveragePrice * security_option.Value.Holdings.AbsoluteQuantity * 100) * GoalFactor) + security_option.Value.Holdings.TotalFees; var currentValue = (midPrice * security_option.Value.Holdings.AbsoluteQuantity * 100); Plot("Plotter", "HoldingGoal", currentGoal); Plot("Plotter", "HoldingValue", currentValue); OrderTrackingDebugStr = String.Format("{0} - POSITION IN {1} {2:0.00} {3} {4:MM/dd/yy} -- Cost {5:0.00} -- Will exit at {6:0.00} and current value={7:0.00}", Time.ToString(), security_option.Value.Symbol.Underlying, security_option.Value.Symbol.ID.StrikePrice, security_option.Value.Symbol.ID.OptionRight, security_option.Value.Symbol.ID.Date, security_option.Value.Holdings.AbsoluteHoldingsCost, currentGoal, currentValue ); if (currentValue >= currentGoal) { HasOrderOrHolding = false; Liquidate(); } } } } private void ShowOrderTracking() { if (HasOrderOrHolding) Debug(OrderTrackingDebugStr); } public override void OnOrderEvent(OrderEvent orderEvent) { Log(orderEvent.ToString()); } private Decimal MidPrice(Decimal Low, Decimal High) { return Math.Round(((High - Low) / 2) + Low, 2); } } }