Overall Statistics |
Total Trades 233 Average Win 7.42% Average Loss -4.29% Compounding Annual Return 153.040% Drawdown 45.800% Expectancy 0.553 Net Profit 625.279% Sharpe Ratio 1.574 Loss Rate 43% Win Rate 57% Profit-Loss Ratio 1.73 Alpha 0.799 Beta -0.479 Annual Standard Deviation 0.48 Annual Variance 0.231 Information Ratio 1.334 Tracking Error 0.5 Treynor Ratio -1.579 Total Fees $3047.79 |
using QuantConnect.Algorithm; using QuantConnect.Orders; using QuantConnect.Securities; using System; namespace QuantConnect { public abstract class FixedSetHoldingsAlgorithm : QCAlgorithm { /// <summary> /// Alias for SetHoldings to avoid the M-decimal errors. /// </summary> /// <param name="symbol">string symbol we wish to hold</param> /// <param name="percentage">double percentage of holdings desired</param> /// <param name="liquidateExistingHoldings">liquidate existing holdings if neccessary to hold this stock</param> /// <seealso cref="MarketOrder"/> public void FixedSetHoldings(Symbol symbol, double percentage, bool liquidateExistingHoldings = false) { FixedSetHoldings(symbol, (decimal)percentage, liquidateExistingHoldings); } /// <summary> /// Alias for SetHoldings to avoid the M-decimal errors. /// </summary> /// <param name="symbol">string symbol we wish to hold</param> /// <param name="percentage">float percentage of holdings desired</param> /// <param name="liquidateExistingHoldings">bool liquidate existing holdings if neccessary to hold this stock</param> /// <param name="tag">Tag the order with a short string.</param> /// <seealso cref="MarketOrder"/> public void FixedSetHoldings(Symbol symbol, float percentage, bool liquidateExistingHoldings = false, string tag = "") { FixedSetHoldings(symbol, (decimal)percentage, liquidateExistingHoldings, tag); } /// <summary> /// Alias for SetHoldings to avoid the M-decimal errors. /// </summary> /// <param name="symbol">string symbol we wish to hold</param> /// <param name="percentage">float percentage of holdings desired</param> /// <param name="liquidateExistingHoldings">bool liquidate existing holdings if neccessary to hold this stock</param> /// <param name="tag">Tag the order with a short string.</param> /// <seealso cref="MarketOrder"/> public void FixedSetHoldings(Symbol symbol, int percentage, bool liquidateExistingHoldings = false, string tag = "") { FixedSetHoldings(symbol, (decimal)percentage, liquidateExistingHoldings, tag); } /// <summary> /// Automatically place an order which will set the holdings to between 100% or -100% of *PORTFOLIO VALUE*. /// E.g. SetHoldings("AAPL", 0.1); SetHoldings("IBM", -0.2); -> Sets portfolio as long 10% APPL and short 20% IBM /// E.g. SetHoldings("AAPL", 2); -> Sets apple to 2x leveraged with all our cash. /// </summary> /// <param name="symbol">Symbol indexer</param> /// <param name="percentage">decimal fraction of portfolio to set stock</param> /// <param name="liquidateExistingHoldings">bool flag to clean all existing holdings before setting new faction.</param> /// <param name="tag">Tag the order with a short string.</param> /// <seealso cref="MarketOrder"/> public void FixedSetHoldings(Symbol symbol, decimal percentage, bool liquidateExistingHoldings = false, string tag = "") { //Initialize Requirements: Security security; if (!Securities.TryGetValue(symbol, out security)) { Error(symbol.ToString() + " not found in portfolio. Request this data when initializing the algorithm."); return; } //If they triggered a liquidate if (liquidateExistingHoldings) { foreach (var kvp in Portfolio) { var holdingSymbol = kvp.Key; var holdings = kvp.Value; if (holdingSymbol != symbol && holdings.AbsoluteQuantity > 0) { //Go through all existing holdings [synchronously], market order the inverse quantity: Order(holdingSymbol, -holdings.Quantity, false, tag); } } } //Only place trade if we've got > 1 share to order. var quantity = FixedCalculateOrderQuantity(symbol, percentage); if (Math.Abs(quantity) > 0) { MarketOrder(symbol, quantity, false, tag); } } private bool TryOrderQuantity(int orderQuantity, Security security, decimal marginRemaining, decimal targetOrderValue) { //note that margin requirements and order value + fees are assumed to be monotonic w.r.t. orderQuantity, //otherwise binary search would not work and an exhaustive search would be necessary var order = new MarketOrder(security.Symbol, orderQuantity, UtcTime); var orderValue = order.GetValue(security); var orderFees = security.FeeModel.GetOrderFee(security, order); // calculate the margin required for the order var marginRequired = security.MarginModel.GetInitialMarginRequiredForOrder(security, order); return marginRequired <= marginRemaining && orderValue + orderFees <= targetOrderValue; } /// <summary> /// Calculate the order quantity to achieve target-percent holdings. /// </summary> /// <param name="symbol">Security object we're asking for</param> /// <param name="target">Target percentag holdings, this is an unlevered value, so /// if you have 2x leverage and request 100% holdings, it will utilize half of the /// available margin</param> /// <returns>Order quantity to achieve this percentage</returns> public int FixedCalculateOrderQuantity(Symbol symbol, decimal target) { var security = Securities[symbol]; return FixedCalculateOrderQuantity(symbol, target, (int)security.Holdings.Quantity); } public int FixedCalculateOrderQuantity(Symbol symbol, decimal target, int initialQuantity) { var security = Securities[symbol]; var price = security.Price; if (price == 0) price = (security.BidPrice + security.AskPrice) / 2; // if targeting zero, simply return the negative of the quantity if (target == 0) return (int)-initialQuantity; // can't order it if we don't have data if (price == 0) return 0; // this is the value in dollars that we want our holdings to have var targetPortfolioValue = target * Portfolio.TotalPortfolioValue; var quantity = initialQuantity; var currentHoldingsValue = price * quantity; // remove directionality, we'll work in the land of absolutes var targetOrderValue = Math.Abs(targetPortfolioValue - currentHoldingsValue); var direction = targetPortfolioValue > currentHoldingsValue ? OrderDirection.Buy : OrderDirection.Sell; // determine the unit price in terms of the account currency var unitPrice = new MarketOrder(symbol, 1, UtcTime).GetValue(security); // calculate the total margin available var marginRemaining = Portfolio.GetMarginRemaining(symbol, direction); if (marginRemaining <= 0) return 0; // compute the initial order quantity int orderQuantity; int maxOrderQuantity = (int)(targetOrderValue / unitPrice); //upper bound int minOrderQuantity = 1; //lower bound if (TryOrderQuantity(maxOrderQuantity, security, marginRemaining, targetOrderValue)) { orderQuantity = maxOrderQuantity; } else if (!TryOrderQuantity(minOrderQuantity, security, marginRemaining, targetOrderValue)) { orderQuantity = 0; } else { //binary search for (;;) { orderQuantity = (maxOrderQuantity + minOrderQuantity) / 2; if (orderQuantity == minOrderQuantity) { orderQuantity = minOrderQuantity; break; } if (TryOrderQuantity(orderQuantity, security, marginRemaining, targetOrderValue)) { minOrderQuantity = orderQuantity; } else { maxOrderQuantity = orderQuantity; } } } //Rounding off Order Quantity to the nearest multiple of Lot Size if (orderQuantity % Convert.ToInt32(security.SymbolProperties.LotSize) != 0) { orderQuantity = orderQuantity - (orderQuantity % Convert.ToInt32(security.SymbolProperties.LotSize)); } // add directionality back in return (direction == OrderDirection.Sell ? -1 : 1) * orderQuantity; } } }
using QuantConnect.Data; using QuantConnect.Data.Custom; using QuantConnect.Data.Market; using QuantConnect.Indicators; using QuantConnect.Securities; using System; namespace QuantConnect.Algorithm.CSharp { public class SmartVIXAlgorithm : FixedSetHoldingsAlgorithm { private Security _xiv; private Security _vxx; private Security _tqqq; private Security _spyg; private Quandl _lastVIX; private Quandl _lastVXV; private Quandl _lastV1; private Quandl _lastV2; private string _dataVIX = "CBOE/VIX"; private string _dataVXV = "CBOE/VXV"; private string _dataV1 = "CHRIS/CBOE_VX1"; private string _dataV2 = "CHRIS/CBOE_VX2"; private MovingAverageConvergenceDivergence _macd; private Identity _xivPrice; private RollingWindow<decimal> _xivPriceHistory = new RollingWindow<decimal>(2); //for ML private decimal _prevXivPrice; private class VIXQuandl : Quandl { public VIXQuandl() : base(valueColumnName: "VIX CLOSE") { } } private class VFQuandl : Quandl { public VFQuandl() : base(valueColumnName: "SETTLE") { } } public override void Initialize() { SetStartDate(2015, 9, 1); SetEndDate(DateTime.Now.Date.AddDays(-1)); SetCash(30000); _xiv = AddSecurity(SecurityType.Equity, "XIV", Resolution.Minute); _vxx = AddSecurity(SecurityType.Equity, "VXX", Resolution.Minute); _tqqq = AddSecurity(SecurityType.Equity, "TQQQ", Resolution.Minute); _spyg = AddSecurity(SecurityType.Equity, "SPYG", Resolution.Minute); _macd = MACD(_spyg.Symbol, 12, 26, 9, MovingAverageType.Simple, Resolution.Daily); _xivPrice = Identity(_xiv.Symbol, Resolution.Daily); AddData<VIXQuandl>(_dataVIX, Resolution.Daily); AddData<Quandl>(_dataVXV, Resolution.Daily); AddData<VFQuandl>(_dataV1, Resolution.Daily); AddData<VFQuandl>(_dataV2, Resolution.Daily); Schedule.On(DateRules.EveryDay(_xiv.Symbol), TimeRules.AfterMarketOpen(_xiv.Symbol, 1), Rebalance); Schedule.On(DateRules.EveryDay(_xiv.Symbol), TimeRules.AfterMarketOpen(_xiv.Symbol, 34), RebalanceXIV); } private bool _xivDay; private decimal _vix = -1; private void Rebalance() { var last_vix = _lastVIX.Price; var last_vx1 = _lastV1.Price; var last_vx2 = _lastV2.Price; var last_vxv = _lastVXV.Price; if (last_vix == 0 || last_vx1 == 0 || last_vx2 == 0 || last_vxv == 0) { Debug("Zero Quandl price"); return; } if (_xivPrice.Current == 0) { Debug("Zero XIV price"); return; } // Calculating the gap between spot vix and the first month vix future var last_ratio_v_v1 = last_vix / last_vx1; // Calculating the contango ratio of the front and second month VIX Futures var last_ratio_v1_v2 = last_vx1 / last_vx2; // Blending the previous two ratios together using a weighted average var ratio_weight = 0.7m; var last_ratio = (ratio_weight * last_ratio_v_v1) + ((1 - ratio_weight) * last_ratio_v1_v2) - 1; var vix_vxv_ratio = last_vix / last_vxv; // Retrieve SPY MACD data var macd = _macd - _macd.Signal; // Calculate how much vix moved the previous day decimal vix_ratio; if (_vix > 0) vix_ratio = last_vix / _vix - 1; else vix_ratio = 0; _vix = last_vix; while (!_xivPriceHistory.IsReady) _xivPriceHistory.Add(_xivPrice.Current); _xivPriceHistory.Add(_xivPrice.Current); var xiv_ratio = _xivPriceHistory[0] / _xivPriceHistory[1] - 1; // Setting thresholds var threshold_vix_too_low = 10.76m; // 0 var threshold_xiv = -0.049m; // 1 var threshold_vxv_xiv = 0.87m; // 2 var threshold_uvxy = 0.049m; // 3 var threshold_macd = -0.55m; // 3 var threshold_vxv_uvxy = 1.3m; // 4 var threshold_vix_high = 19.9m; // 5 var threshold_vc_low = -0.148m; // 6 var threshold_vc_high = 0.046m; // 8 var threshold_vc_high_2 = -0.06m; // 8 var threshold_xiv_ratio = -0.053m; // 10 var threshold_uvxy_ratio = 0.08m; // 11 Security target_sid = null; int thecase = -1; if (last_vix < threshold_vix_too_low) // if VIX is too low, invest in UVXY witht he hope of a spike { thecase = 0; target_sid = _vxx; } else if (last_ratio < threshold_xiv) // if contango is high, invest in XIV to gain from decay { thecase = 1; target_sid = _xiv; } else if (vix_vxv_ratio < threshold_vxv_xiv) // if short term vol is low compared to mid term, invest in XIV to gain from decay { thecase = 2; target_sid = _xiv; } else if (last_ratio > threshold_uvxy && macd > threshold_macd) // if backwardation is high, invest in UVXY to gain from decay { thecase = 3; target_sid = _vxx; } else if (vix_vxv_ratio > threshold_vxv_uvxy) // if short term vol is high compared to mid term, invest in UVXY to gain from growth { thecase = 4; target_sid = _vxx; } else if (last_vix > threshold_vix_high) // if VIX is too high, invest in XIV expecting that VIX will drop { thecase = 5; target_sid = _xiv; } else if (vix_ratio < threshold_vc_low) // Vix down sharply, invest in XIV expecting that futures curve gets pulled down { thecase = 6; target_sid = _xiv; } else if (vix_ratio > threshold_vc_high) // Vix up sharply, invest in UVXY expecting that futures curve gets pulled up { thecase = 7; target_sid = _vxx; } else if (vix_ratio > threshold_vc_high_2) //have to think { thecase = 8; target_sid = _xiv; } else { thecase = 9; target_sid = _vxx; } if (target_sid == _xiv && xiv_ratio < threshold_xiv_ratio) { // indicators say XIV but it just dropped overnight, so got for TQQQ thecase = 10; target_sid = _tqqq; } else if (target_sid == _vxx && xiv_ratio > threshold_uvxy_ratio) { // indicators say VXX but it just dropped overnight, so got for TQQQ thecase = 11; target_sid = _tqqq; } if (target_sid != null && !target_sid.HoldStock) { if (target_sid == _vxx || target_sid == _tqqq) { FixedSetHoldings(_tqqq.Symbol, target_sid == _tqqq ? 1 : 0); FixedSetHoldings(_vxx.Symbol, target_sid == _vxx ? 1 : 0); FixedSetHoldings(_xiv.Symbol, 0); } else if (target_sid == _xiv) _xivDay = true; } } private void RebalanceXIV() { if (!_xivDay) return; _xivDay = false; FixedSetHoldings(_tqqq.Symbol, 0); FixedSetHoldings(_vxx.Symbol, 0); FixedSetHoldings(_xiv.Symbol, 1); } private void GetDataIfPossible<V>(DataDictionary<V> dict, Symbol key, ref V dest) { V safeToOverwrite; if (dict.TryGetValue(key, out safeToOverwrite)) dest = safeToOverwrite; } public override void OnData(Slice data) { // gets all Quandl data from our 'Slice' object var quandls = data.Get<Quandl>(); if (quandls.Count > 0) { GetDataIfPossible(quandls, _dataVIX, ref _lastVIX); GetDataIfPossible(quandls, _dataVXV, ref _lastVXV); GetDataIfPossible(quandls, _dataV1, ref _lastV1); GetDataIfPossible(quandls, _dataV2, ref _lastV2); } } } }