| Overall Statistics |
|
Total Trades 26 Average Win 6.04% Average Loss -3.42% Compounding Annual Return 7.013% Drawdown 63.900% Expectancy -0.171 Net Profit 17.809% Sharpe Ratio 0.458 Probabilistic Sharpe Ratio 15.015% Loss Rate 70% Win Rate 30% Profit-Loss Ratio 1.76 Alpha -0.017 Beta 0.397 Annual Standard Deviation 0.122 Annual Variance 0.015 Information Ratio -0.842 Tracking Error 0.151 Treynor Ratio 0.141 Total Fees $32.00 Estimated Strategy Capacity $32000000.00 Lowest Capacity Asset AAPL R735QTJ8XC9X |
using System;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using System.Collections.Generic;
using QuantConnect.Securities.Option.StrategyMatcher;
using QuantConnect.Orders;
using QuantConnect.Securities.Option;
using QuantConnect.Securities.Positions;
using QuantConnect.Securities.Equity;
namespace QuantConnect.Algorithm.CSharp.S89
{
/// <summary>
/// Regression algorithm exercising an equity Call Calendar Spread option strategy and asserting it's being detected by Lean and works as expected
/// </summary>
public class OptionEquityCallCalendarSpreadRegressionAlgorithmS89 : OptionEquityBaseStrategyRegressionAlgorithmS89
{
/// <summary>
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
/// </summary>
/// <param name="slice">Slice object keyed by symbol containing the stock data</param>
public override void OnData(Slice slice)
{
if (equity != null)
{
this.RemoveSecurity(equity.Symbol);
equity = null;
}
foreach (var kvp in ActiveSecurities)
{
if (kvp.Value.Type != SecurityType.Option)
this.RemoveSecurity(kvp.Value.Symbol);
}
if (!Portfolio.Invested)
{
OptionChain chain;
if (IsMarketOpen(_optionSymbol) && slice.OptionChains.TryGetValue(_optionSymbol, out chain))
{
var contracts = chain
.Where(contract => contract.Right == OptionRight.Call)
.OrderBy(x => x.Expiry)
.ThenBy(x => x.Strike)
.ToList();
var shortCall = contracts.FirstOrDefault();
var longCall = contracts.FirstOrDefault(contract => contract.Expiry > shortCall.Expiry && contract.Strike == shortCall.Strike);
if (shortCall == null || longCall == null) return;
var initialMargin = Portfolio.MarginRemaining;
MarketOrder(shortCall.Symbol, -10);
MarketOrder(longCall.Symbol, +10);
}
}
}
}
/// <summary>
/// Base class for equity option strategy regression algorithms which holds some basic shared setup logic
/// </summary>
public abstract class OptionEquityBaseStrategyRegressionAlgorithmS89 : QCAlgorithm
{
protected Equity equity;
protected decimal _paidFees;
protected Symbol _optionSymbol;
public override void Initialize()
{
SetStartDate(2019, 01, 24);
SetEndDate(2021, 06, 24);
SetCash(200000);
equity = AddEquity("AAPL");//, leverage: 4
var option = AddOption(equity.Symbol);
_optionSymbol = option.Symbol;
// set our strike/expiry filter for this option chain
option.SetFilter(u => u.Strikes(-1, +1)
// Expiration method accepts TimeSpan objects or integer for days.
// The following statements yield the same filtering criteria
.Expiration(28, 180)
.CallsOnly()
//.IncludeWeeklys()
);
}
protected decimal GetPriceSpreadDifference(params Symbol[] symbols)
{
var spreadPaid = 0m;
foreach (var symbol in symbols)
{
var security = Securities[symbol];
var actualQuantity = security.Holdings.AbsoluteQuantity;
var spread = 0m;
if (security.Holdings.IsLong)
{
if (security.AskPrice != 0)
{
spread = security.Price - security.AskPrice;
}
}
else if (security.BidPrice != 0)
{
spread = security.BidPrice - security.Price;
}
spreadPaid += spread * actualQuantity * security.SymbolProperties.ContractMultiplier;
}
return spreadPaid;
}
/// <summary>
/// Order fill event handler. On an order fill update the resulting information is passed to this method.
/// </summary>
/// <param name="orderEvent">Order event details containing details of the evemts</param>
/// <remarks>This method can be called asynchronously and so should only be used by seasoned C# experts. Ensure you use proper locks on thread-unsafe objects</remarks>
public override void OnOrderEvent(OrderEvent orderEvent)
{
if (orderEvent.Status == OrderStatus.Filled)
{
_paidFees += orderEvent.OrderFee.Value.Amount;
if (orderEvent.Symbol.SecurityType.IsOption())
{
var security = Securities[orderEvent.Symbol];
var premiumPaid = orderEvent.Quantity * orderEvent.FillPrice * security.SymbolProperties.ContractMultiplier;
Log($"{orderEvent}. Premium paid: {premiumPaid}");
return;
}
}
Log($"{orderEvent}");
}
}
}