| Overall Statistics |
|
Total Orders 10 Average Win 23.45% Average Loss -19.68% Compounding Annual Return 1.398% Drawdown 1.000% Expectancy 0.096 Start Equity 200000 End Equity 202805 Net Profit 1.402% Sharpe Ratio 0.206 Sortino Ratio 0.441 Probabilistic Sharpe Ratio 24.277% Loss Rate 50% Win Rate 50% Profit-Loss Ratio 1.19 Alpha 0.002 Beta 0.009 Annual Standard Deviation 0.017 Annual Variance 0 Information Ratio -0.572 Tracking Error 0.275 Treynor Ratio 0.369 Total Fees $0.00 Estimated Strategy Capacity $9200000.00 Lowest Capacity Asset SPX XL80P4UFCXOU|SPX 31 Portfolio Turnover 0.36% |
#region imports
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Drawing;
using QuantConnect;
using QuantConnect.Algorithm.Framework;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Algorithm.Framework.Portfolio.SignalExports;
using QuantConnect.Algorithm.Framework.Execution;
using QuantConnect.Algorithm.Framework.Risk;
using QuantConnect.Algorithm.Selection;
using QuantConnect.Api;
using QuantConnect.Parameters;
using QuantConnect.Benchmarks;
using QuantConnect.Brokerages;
using QuantConnect.Configuration;
using QuantConnect.Util;
using QuantConnect.Interfaces;
using QuantConnect.Algorithm;
using QuantConnect.Indicators;
using QuantConnect.Data;
using QuantConnect.Data.Auxiliary;
using QuantConnect.Data.Consolidators;
using QuantConnect.Data.Custom;
using QuantConnect.Data.Custom.IconicTypes;
using QuantConnect.DataSource;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.Market;
using QuantConnect.Data.Shortable;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Notifications;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Fills;
using QuantConnect.Orders.OptionExercise;
using QuantConnect.Orders.Slippage;
using QuantConnect.Orders.TimeInForces;
using QuantConnect.Python;
using QuantConnect.Scheduling;
using QuantConnect.Securities;
using QuantConnect.Securities.Equity;
using QuantConnect.Securities.Future;
using QuantConnect.Securities.Option;
using QuantConnect.Securities.Positions;
using QuantConnect.Securities.Forex;
using QuantConnect.Securities.Crypto;
using QuantConnect.Securities.CryptoFuture;
using QuantConnect.Securities.Interfaces;
using QuantConnect.Securities.Volatility;
using QuantConnect.Storage;
using QuantConnect.Statistics;
using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
#endregion
namespace QuantConnect
{
public class VixBullSpreadAlgorithm : QCAlgorithm
{
private Symbol _optionSymbol;
public override void Initialize()
{
SetStartDate(2020, 1, 1);
SetEndDate(2021, 1, 1);
SetCash(200000);
// Asynchronous can use computational resources efficiently
UniverseSettings.Asynchronous = true;
// Filter to get ATM calls expiring in 180 days to form the Bull Call Spread
var option = AddIndexOption("SPX");
option.SetFilter((u) => u.CallsOnly().Strikes(-2, +2).Expiration(0, 180));
_optionSymbol = option.Symbol;
}
public override void OnData(Slice slice)
{
if (!Portfolio.Invested && IsMarketOpen(_optionSymbol))
{
// Make sure getting the updated VIX option chain
if (slice.OptionChains.TryGetValue(_optionSymbol, out var chain))
{
var expiry = chain.Max(x => x.Expiry);
var callContracts = chain
.Where(x => x.Expiry == expiry)
.OrderBy(x => x.Strike)
.ToList();
// Need 2 contracts to form a call spread
if (callContracts.Count < 2)
{
return;
}
// Obtain 2 call contracts with different strike price to form the call spread
var longCall = callContracts.First();
var shortCall = callContracts.First(contract => contract.Strike > longCall.Strike);
// Use all the buying power, but need to ensure the order size of the long and short call are the same
var quantity = new[] {
CalculateOrderQuantity(shortCall.Symbol, -1m),
CalculateOrderQuantity(longCall.Symbol, 1m) }
.Min(x=> Math.Abs(x));
MarketOrder(shortCall.Symbol, -quantity);
MarketOrder(longCall.Symbol, quantity);
var expectedMarginUsage = Math.Max((longCall.Strike - shortCall.Strike) * Securities[longCall.Symbol].SymbolProperties.ContractMultiplier * quantity, 0);
if (expectedMarginUsage != Portfolio.TotalMarginUsed)
{
throw new Exception("Unexpect margin used!");
}
}
}
}
public override void OnSecuritiesChanged(SecurityChanges changes)
{
foreach (var security in changes.AddedSecurities)
{
if (security.Type == SecurityType.IndexOption)
{
// Historical data
var history = History(security.Symbol, 10, Resolution.Minute);
Debug($"We got {history.Count()} from our history request for {security.Symbol}");
}
}
}
}
}