| Overall Statistics |
|
Total Trades 66 Average Win 0.35% Average Loss -0.37% Compounding Annual Return 6.809% Drawdown 1.100% Expectancy 0.135 Net Profit 1.582% Sharpe Ratio 1.755 Probabilistic Sharpe Ratio 66.343% Loss Rate 42% Win Rate 58% Profit-Loss Ratio 0.94 Alpha 0.049 Beta 0.103 Annual Standard Deviation 0.038 Annual Variance 0.001 Information Ratio -0.935 Tracking Error 0.116 Treynor Ratio 0.649 Total Fees $52.00 |
using System;
using System.Diagnostics;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities.Option;
namespace QuantConnect.Algorithm.CSharp
{
public class BasicTemplateOptionsHistoryAlgorithm : QCAlgorithm
{
private decimal deltaStart = 0.8m;
private decimal deltaEnd = 0.9m;
private decimal deltaAdjust = 0.4m;
private bool isShort = false;
private List<PositionGroup> Underlying;
private DateTime currentDay = DateTime.Now;
private decimal cash = 40000;
private PositionProvider positionProvider;
private DateTime startDate = new DateTime(2016, 6, 1);
private DateTime endDate = new DateTime(2016,8, 28);
private int liquidateBefore = 1;
private int optionsFrom = 3;
private int optionsTo = 6;
private int daysPass = 0;
public override void Initialize()
{
SetCash(cash);
SetStartDate(startDate);
SetEndDate(endDate);
positionProvider = new PositionProvider(Portfolio);
var s2 = new PositionGroup();
var option2 = AddOption("MSFT");
var eq2 = AddEquity("MSFT");
s2.OptionSymbol = option2.Symbol;
s2.UnderlyingSymbol = eq2.Symbol;
positionProvider.PositionList.Add(s2);
//var s3 = new PositionGroup();
//var option3 = AddOption("FB");
//var eq3 = AddEquity("FB");
//s3.OptionSymbol = option3.Symbol;
//s3.UnderlyingSymbol = eq3.Symbol;
//positionProvider.PositionList.Add(s3);
//var s4 = new PositionGroup();
//var option4 = AddOption("JNJ");
//var eq4 = AddEquity("JNJ");
//s4.OptionSymbol = option4.Symbol;
//s4.UnderlyingSymbol = eq4.Symbol;
//positionProvider.PositionList.Add(s4);
//var s5 = new PositionGroup();
//var option5 = AddOption("PG");
//var eq5 = AddEquity("PG");
//s5.OptionSymbol = option5.Symbol;
//s5.UnderlyingSymbol = eq5.Symbol;
//positionProvider.PositionList.Add(s5);
//var s6 = new PositionGroup();
//var option6 = AddOption("V");
//var eq6 = AddEquity("V");
//s6.OptionSymbol = option6.Symbol;
//s6.UnderlyingSymbol = eq6.Symbol;
//positionProvider.PositionList.Add(s6);
//var s7 = new PositionGroup();
//var option7 = AddOption("INTC");
//var eq7 = AddEquity("INTC");
//s7.OptionSymbol = option7.Symbol;
//s7.UnderlyingSymbol = eq7.Symbol;
//positionProvider.PositionList.Add(s7);
//var s1 = new PositionGroup();
//var option1 = AddOption("AAPL");
//var eq1 = AddEquity("AAPL");
//s1.OptionSymbol = option1.Symbol;
//s1.UnderlyingSymbol = eq1.Symbol;
//positionProvider.PositionList.Add(s1);
//var s8 = new PositionGroup();
//var option8 = AddOption("JPM");
//var eq8 = AddEquity("JPM");
//s8.OptionSymbol = option8.Symbol;
//s8.UnderlyingSymbol = eq8.Symbol;
//positionProvider.PositionList.Add(s8);
//option1.PriceModel = OptionPriceModels.CrankNicolsonFD();
//option1.SetFilter(universe => from symbol in universe
// .IncludeWeeklys()
// .Expiration(TimeSpan.FromDays(optionsFrom), TimeSpan.FromDays(optionsTo))
// select symbol);
option2.PriceModel = OptionPriceModels.CrankNicolsonFD();
option2.SetFilter(universe => from symbol in universe
.IncludeWeeklys()
.Expiration(TimeSpan.FromDays(optionsFrom), TimeSpan.FromDays(optionsTo))
select symbol);
//option3.PriceModel = OptionPriceModels.CrankNicolsonFD();
//option3.SetFilter(universe => from symbol in universe
// .IncludeWeeklys()
// .Expiration(TimeSpan.FromDays(optionsFrom), TimeSpan.FromDays(optionsTo))
// select symbol);
//option4.PriceModel = OptionPriceModels.CrankNicolsonFD();
//option4.SetFilter(universe => from symbol in universe
// .IncludeWeeklys()
// .Expiration(TimeSpan.FromDays(optionsFrom), TimeSpan.FromDays(optionsTo))
// select symbol);
//option5.PriceModel = OptionPriceModels.CrankNicolsonFD();
//option5.SetFilter(universe => from symbol in universe
// .IncludeWeeklys()
// .Expiration(TimeSpan.FromDays(optionsFrom), TimeSpan.FromDays(optionsTo))
// select symbol);
//option6.PriceModel = OptionPriceModels.CrankNicolsonFD();
//option6.SetFilter(universe => from symbol in universe
// .IncludeWeeklys()
// .Expiration(TimeSpan.FromDays(optionsFrom), TimeSpan.FromDays(optionsTo))
// select symbol);
//option7.PriceModel = OptionPriceModels.CrankNicolsonFD();
//option7.SetFilter(universe => from symbol in universe
// .IncludeWeeklys()
// .Expiration(TimeSpan.FromDays(optionsFrom), TimeSpan.FromDays(optionsTo))
// select symbol);
//option8.PriceModel = OptionPriceModels.CrankNicolsonFD();
//option8.SetFilter(universe => from symbol in universe
// .IncludeWeeklys()
// .Expiration(TimeSpan.FromDays(optionsFrom), TimeSpan.FromDays(optionsTo))
// select symbol);
SetWarmUp(TimeSpan.FromDays(7));
SetBenchmark("SPY");
}
public override void OnData(Slice slice)
{
if (IsWarmingUp) return;
if (currentDay.Date != slice.Time.Date)
{
currentDay = slice.Time;
positionProvider.CollectData(slice, deltaStart, deltaEnd);
//LogHolding(slice);
daysPass++;
if (daysPass % 10 == 0)
{
LogHolding(slice);
}
}
else
{
return;
}
Stopwatch stopWatch = new Stopwatch();
//stopWatch.Start();
foreach (var pg in positionProvider.PositionList.Where(x => !x.Deleted))
{
var businessDays = TradingCalendar.GetTradingDays(startDate.Date, endDate.Date).Where(x => x.BusinessDay);
// Log(" expiry : " + pg.Expiry.Date + " slice:" + slice.Time.AddDays(-liquidateBefore).Date);
if ( false && pg.Expiry.Date == slice.Time.AddDays(liquidateBefore).Date)
{
Log("Expiring");
foreach (var p in pg.Positions)
{
if (p.SecurityType == SecurityType.Equity)
{
Liquidate(pg.UnderlyingSymbol);
}
else
{
Liquidate(p.Symbol);
}
}
pg.Deleted = true;
}
}
if (slice.Time.Year == 2010)
{
return;
}
OrderHighestIv(slice);
OptionChain chain;
foreach (var pos in positionProvider.PositionList)
{
if (slice.OptionChains.TryGetValue(pos.OptionSymbol, out chain))
{
var q = Portfolio[pos.UnderlyingSymbol].Quantity;
var positionDelta = -((pos.Delta * GetVolume() * 100) + q);
var reverseDelta = q + (pos.Delta * GetVolume() * 100);
//Debug ("delta:"+pg.Delta + " reverseDelta:"+ reverseDelta + " position:"+ q);
if (positionDelta > 40 * GetVolume() || positionDelta < -40 * GetVolume())
{
var k = pos.Positions.Where(x => x.SecurityType == SecurityType.Equity).FirstOrDefault();
if (k != null)
{
k.Quantity = k.Quantity - (int)(Math.Ceiling(pos.Delta) * GetVolume());
}
else
{
Position p = new Position();
p.Symbol = pos.UnderlyingSymbol;
p.Quantity = (int)-(Math.Ceiling(pos.Delta) * GetVolume());
p.SecurityType = SecurityType.Equity;
pos.Positions.Add(p);
}
//Log("PositionDelta :" + positionDelta + " Delta:" + pg.Delta);
//Debug("Delta:" + pg.Delta);
if (pos.Delta != 0)
{
// Log("Starting adjustment" + positionDelta);
// var tik = MarketOrder(pos.UnderlyingSymbol, -reverseDelta);
}
else
{
// Liquidate(pos.UnderlyingSymbol);
}
}
}
}
GetPositionProfitLoss(slice);
}
public int GetPositionProfitLoss(Slice slice)
{
Debug("profit and loss");
foreach (var p in Portfolio.Where(x => x.Value.Invested))
{
PositionGroup pg = null;
foreach (var pr in positionProvider.PositionList)
{
var poss = pr.Positions.Where(x => x.Symbol == p.Key && x.SecurityType == SecurityType.Option);
if (poss != null)
{
pg = pr;
break;
}
}
var premiumCollected = 0m;
var currentPremium = 0m;
OptionChain chain;
if (slice.OptionChains.TryGetValue(pg.OptionSymbol, out chain))
{
foreach (var pu in pg.Positions.Where(b => b.SecurityType == SecurityType.Option))
{
var option = Portfolio.Where(x => x.Key == pu.Symbol).FirstOrDefault();
if (option.Value != null)
{
premiumCollected = pg.PremiumCollected + option.Value.AveragePrice;
var uu = chain.Where(x => x.Symbol == pu.Symbol).FirstOrDefault();
if (uu != null)
{
currentPremium = currentPremium + ((uu.BidPrice + uu.AskPrice) / 2);
}
}
}
}
Log("premium collected: "+ premiumCollected + " currentPremium:" + currentPremium );
pg.PremiumCollected = premiumCollected;
pg.CurrentPremium = currentPremium;
if (pg.CurrentPremium > pg.PremiumCollected * 3 || pg.CurrentPremium < pg.PremiumCollected * 0.5m)
{
Log("liquidate stop loss take profit");
foreach (var pos in pg.Positions)
{
Liquidate(pos.Symbol);
}
}
}
return 0;
}
public bool IsMonday(Slice slice)
{
if (slice.Time.DayOfWeek == DayOfWeek.Monday && slice.Time.Hour == 9 && slice.Time.Minute == 31)
{
return true;
}
return false;
}
public int GetVolume()
{
return 1;
}
public override void OnAssignmentOrderEvent(OrderEvent e)
{
Log("assignment , liquidating--------------------------");
foreach (var pg in positionProvider.PositionList)
{
pg.Deleted = true;
}
Liquidate();
}
public override void OnOrderEvent(OrderEvent e)
{
if (e.FillQuantity == 0)
{
return;
}
}
public OptionContract OrderHighestIv(Slice slice)
{
//LogHolding(slice);
OptionChain chain;
// Debug ("Count:" + positionProvider.SymbolList.Count());
foreach (var pg in positionProvider.PositionList)
{
var toClearup = false;
foreach (var poss in pg.Positions.Where(x => x.SecurityType == SecurityType.Option))
{
var portfolioExist = Portfolio.Where(x => x.Value.Invested && x.Key == poss.Symbol);
if (portfolioExist == null)
{
toClearup = true;
break;
}
}
if (toClearup)
{
Log("liquidate missing leg");
foreach (var poss in pg.Positions)
{
Liquidate(poss.Symbol);
}
continue;
}
if (slice.OptionChains.TryGetValue(pg.OptionSymbol, out chain))
{
var pp = chain.Where(x => x.Greeks.Delta < (decimal)-deltaStart && x.Greeks.Delta > (decimal)-deltaEnd && x.Right == OptionRight.Put).OrderByDescending(x => (x.BidPrice + x.AskPrice) / 2).FirstOrDefault();
if (pp != null)
{
LogOption(pp);
var cc = chain.Where(x => x.Greeks.Delta > (decimal)deltaStart && x.Greeks.Delta < (decimal)deltaEnd && x.Right == OptionRight.Call && x.Expiry.Date == pp.Expiry.Date).OrderByDescending(x => (x.BidPrice + x.AskPrice) / 2).FirstOrDefault();
if (cc != null)
{
// LogOption(pp);
// LogOption(cc);
// if (pg.IvRank > 70)
// {
var ticket = MarketOrder(cc.Symbol, -GetVolume());
var ticket2 = MarketOrder(pp.Symbol, -GetVolume());
pg.Positions = new List<Position>();
pg.Tickets.Add(ticket);
pg.Tickets.Add(ticket2);
pg.Delta = cc.Greeks.Delta + pp.Greeks.Delta;
pg.Expiry = cc.Symbol.ID.Date;
Log("CALL: BID:" + cc.BidPrice + " ASK: " + cc.AskPrice);
Log("CALL: Strike:" + cc.Strike + " UNDERLYING: " + chain.Underlying.Price);
Log("PUT: BID:" + pp.BidPrice + " ASK: " + pp.AskPrice);
Log("PUT: Strike:" + pp.Strike + " UNDERLYING: " + chain.Underlying.Price);
var expiry = pg.Expiry;
var businessDays = TradingCalendar.GetTradingDays(startDate, endDate).Where(x => x.BusinessDay);
if (!businessDays.Select(x => x.Date).Contains(pg.Expiry))
{
var u = businessDays.Where(x => x.Date < pg.Expiry).OrderByDescending(x => x.Date).FirstOrDefault();
pg.Expiry = u.Date;
}
var p = new Position();
p.Symbol = cc.Symbol;
p.Quantity = GetVolume();
p.SecurityType = SecurityType.Option;
var c = new Position();
c.Symbol = pp.Symbol;
c.Quantity = GetVolume();
c.SecurityType = SecurityType.Option;
pg.Positions.Add(c);
pg.Positions.Add(p);
// }
}
}
}
}
return null;
}
public void LogOption(OptionContract pp)
{
Log("-----------------");
Log("Symbol" + pp.Symbol);
Log("Expiry:" + pp.Expiry);
Log("Strike:" + pp.Strike);
Log("Delta:" + pp.Greeks.Delta);
Log("Underlying:" + pp.UnderlyingLastPrice);
Log("MId Price:" + (pp.BidPrice + pp.AskPrice) / 2);
}
public void LogHolding(Slice slice)
{
Debug("---------HOLDING " + slice.Time.Date);
OptionChain chain;
foreach (var h in Portfolio.Where(x => x.Value.Invested))
{
Log("Symbol:" + h.Key);
Log("Quantity" + h.Value.Quantity);
Log("Expiry:" + h.Key.ID.Date);
Log("Price:" + h.Value.Price);
}
Debug("----Holding END------------");
}
}
}namespace QuantConnect {
public class Position
{
public Symbol Symbol {get; set;}
public double AvgPrice {get;set;}
public int Quantity {get; set;}
public SecurityType SecurityType {get;set;}
}
}namespace QuantConnect {
public class IVRank
{
public IVRank(decimal iv , DateTime date){
this.IV = iv;
this.Date = date;
}
public decimal IV {get; set; }
public DateTime Date {get; set; }
}
}namespace QuantConnect {
public class PositionGroup {
public List<Position> Positions = new List<Position>();
public decimal Delta;
public Symbol OptionSymbol;
public Symbol UnderlyingSymbol;
public decimal PremiumCollected;
public decimal CurrentPremium;
public decimal AdjustmentProfit;
public decimal AdjustmentQuantity;
public DateTime Expiry ;
public List<OrderTicket> Tickets = new List<OrderTicket>();
public bool Deleted = false;
public decimal IvRank;
public List<decimal> iv = new List<decimal>();
}
}namespace QuantConnect
{
using System;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities.Option;
public class PositionProvider
{
public List<PositionGroup> PositionList = new List<PositionGroup>();
private SecurityPortfolioManager portfolioManager;
public PositionProvider(SecurityPortfolioManager portfolio)
{
this.portfolioManager = portfolio;
}
public void AddCombo(List<Position> positions)
{
var pg = new PositionGroup();
pg.Positions.AddRange(positions);
PositionList.Add(pg);
}
public void CollectData(Slice slice, decimal deltaStart, decimal deltaEnd)
{
foreach (var pg in PositionList)
{
OptionChain chain;
if (slice.OptionChains.TryGetValue(pg.OptionSymbol, out chain))
{
var positions = pg.Positions;
var options = chain.Where(x => pg.Positions.Where(b => b.SecurityType == SecurityType.Option).Select(y => y.Symbol.ID).Contains(x.Symbol.ID));
pg.Delta = options.Sum(x => x.Greeks.Delta);
var symbol = pg.Positions.Where(b => b.SecurityType == SecurityType.Equity).FirstOrDefault();
if (symbol == null)
{
pg.AdjustmentProfit = 0;
pg.AdjustmentQuantity = 0;
}
else
{
var ticks = pg.Tickets.Where(x => x.Symbol == symbol.Symbol);
pg.AdjustmentProfit = ticks.Sum(fill => fill.QuantityFilled * fill.AverageFillPrice);
pg.AdjustmentQuantity = ticks.Sum(fill => fill.QuantityFilled);
}
var sumQuantity = pg.Tickets.Sum(fill => fill.QuantityFilled);
var pgOptions = pg.Positions.Where(x=>x.SecurityType == SecurityType.Option);
if (sumQuantity != 0)
{
pg.PremiumCollected = pg.AdjustmentProfit / sumQuantity;
}
else
{
pg.PremiumCollected = 0;
}
var put = chain.Where(x => x.Greeks.Delta < (decimal)-deltaStart && x.Greeks.Delta > (decimal)-deltaEnd && x.Right == OptionRight.Put).FirstOrDefault();
if (put != null)
{
pg.iv.Add(put.ImpliedVolatility);
GetIv(pg, put);
}
}
}
}
public decimal GetDeltaQuantity(PositionGroup pg)
{
var symbol = pg.Positions.Where(b => b.SecurityType == SecurityType.Equity).FirstOrDefault();
if (symbol == null)
{
return 0;
}
return pg.Tickets.Where(x => x.Symbol == symbol.Symbol).Sum(fill => fill.QuantityFilled);
}
public void GetIv(PositionGroup symbolData, OptionContract option)
{
var low = symbolData.iv.Where(x => x < option.ImpliedVolatility).Count();
var count = symbolData.iv.Count();
if (count > 90)
{
symbolData.iv = symbolData.iv.Skip(count - 90).ToList();
}
if (count < 10)
{
symbolData.IvRank = 0;
}
symbolData.IvRank = ((decimal)low / (decimal)symbolData.iv.Count()) * 100;
}
}
}