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