Overall Statistics |
Total Trades 65 Average Win 0.01% Average Loss 0% Compounding Annual Return -0.992% Drawdown 1.900% Expectancy 0 Net Profit -0.582% Sharpe Ratio -0.566 Probabilistic Sharpe Ratio 9.123% Loss Rate 0% Win Rate 100% Profit-Loss Ratio 0 Alpha -0.01 Beta 0.005 Annual Standard Deviation 0.014 Annual Variance 0 Information Ratio -2.165 Tracking Error 0.169 Treynor Ratio -1.605 Total Fees $0.00 |
namespace QuantConnect.Algorithm.CSharp { public class TachyonMultidimensionalSplitter : QCAlgorithm { public bool enableLonging; public bool enableShorting; public Symbol symbol; public string symbolString; public StandardDeviation stdev; public SimpleMovingAverage mean; public RollingWindow<decimal> priceSeries; private OrderTicket loLower; private OrderTicket loUpper; private OrderTicket loClose; private decimal percentageOfEquity; private decimal lvl1; private int window; public decimal qty { get { return Portfolio[symbol].Quantity; } } public bool isFlat { get { return !Portfolio[symbol].IsLong && !Portfolio[symbol].IsShort; } } public bool isLong { get { return Portfolio[symbol].IsLong; } } public bool isShort { get { return Portfolio[symbol].IsShort; } } public override void Initialize() { SetStartDate(2020, 6, 1); SetEndDate(2021, 1, 1); SetCash(100000); symbolString = GetParameter("asset_name"); symbol = AddForex(symbolString, Resolution.Minute).Symbol; Securities[symbolString].SetSlippageModel(new CustomSlippageModel(this)); window = int.Parse(GetParameter("window")); percentageOfEquity = decimal.Parse(GetParameter("percentage_of_equity")); lvl1 = decimal.Parse(GetParameter("lvl1")); enableLonging = int.Parse(GetParameter("enable_longing")) == 1; enableShorting = int.Parse(GetParameter("enable_shorting")) == 1; mean = new SimpleMovingAverage(window); stdev = new StandardDeviation(window); priceSeries = new RollingWindow<decimal>(window + 1); // var zChart = new Chart("Z-Score"); // var pChart = new Chart("Market Close"); // zChart.AddSeries(new Series("Line Plot", SeriesType.Line, 0)); // pChart.AddSeries(new Series("Line Plot", SeriesType.Line, 0)); // AddChart(zChart); // AddChart(pChart); foreach (var bar in History<QuoteBar>(symbolString, window * 2 + 1, Resolution.Minute).ToList()) { priceSeries.Add(bar.Close); if (priceSeries.IsReady) { var diff = priceSeries[0] - priceSeries[window]; stdev.Update(bar.Time, diff); mean.Update(bar.Time, diff); } } } public override void OnData(Slice data) { if (!data.ContainsKey(symbol)) { return; } var priceNow = data[symbol].Close; priceSeries.Add(priceNow); var latestReturn = priceNow - priceSeries[window]; mean.Update(Time, latestReturn); stdev.Update(Time, latestReturn); if (!stdev.IsReady || !mean.IsReady || !priceSeries.IsReady) { throw new Exception("Indicator not ready: Should never happen!"); } var zscore = GetZScore(); // var startTime = new DateTime(2021, 1, 5, 11, 40, 0); // var endTime = new DateTime(2021, 1, 5, 11, 58, 0); // if (Time < startTime || Time > endTime) { return; } // Plot("Market Close", "Line Plot", priceNow); // Plot("Z-Score", "Line Plot", zscore); if (isFlat) { UpdateEntryOrder(); return; } if (Portfolio[symbol].UnrealizedProfitPercent < -0.1m) { Debug("Liquidate"); Liquidate(symbol); } } public void UpdateEntryOrder() { if (enableLonging) { var targetPriceL = GetPriceAtZScore(-lvl1); if (loLower == null) { int qty = (int)Math.Floor(Portfolio.Cash * percentageOfEquity / priceSeries[0]); Debug(String.Format("Set new long entry level at {0}", targetPriceL)); loLower = LimitOrder(symbol, qty, targetPriceL); } else { loLower.Update(new UpdateOrderFields() { LimitPrice = targetPriceL }); } } if (enableShorting) { var targetPriceS = GetPriceAtZScore(+lvl1); if (loUpper == null) { int qty = (int)Math.Floor(Portfolio.Cash * percentageOfEquity / priceSeries[0]); Debug(String.Format("Set new short entry level at {0}", targetPriceS)); loUpper = LimitOrder(symbol, -qty, targetPriceS); } else { loUpper.Update(new UpdateOrderFields() { LimitPrice = targetPriceS }); } } } public void UpdateExitOrder() { var targetPrice = GetPriceAtZScore(0m); if (loClose == null) { int qty = (int)Math.Floor(Portfolio[symbol].Quantity); Debug(String.Format("Set new exit level at {0}", targetPrice)); if (isLong) { loClose = LimitOrder(symbol, -qty, targetPrice, "exit long"); } else { loClose = LimitOrder(symbol, qty, targetPrice, "exit short"); } } else { loClose.Update(new UpdateOrderFields() { LimitPrice = targetPrice }); } } public void OnExitFilled() { if (qty > 0m || qty < 0m) { Liquidate(symbol); } if (loLower != null && loLower.Status == OrderStatus.PartiallyFilled) { loLower.Cancel(); } if (loUpper != null && loUpper.Status == OrderStatus.PartiallyFilled) { loUpper.Cancel(); } loLower = null; loUpper = null; loClose = null; } public void OnEntryFilled(decimal fillPrice) { var direction = isLong ? "long" : "short"; Debug(String.Format("Entry {0} filled at {1}", direction, fillPrice)); UpdateExitOrder(); } public override void OnOrderEvent(OrderEvent evt) { if (enableLonging) { if (loLower != null && evt.OrderId == loLower.OrderId) { if (evt.Status == OrderStatus.PartiallyFilled || evt.Status == OrderStatus.Filled) { // cancel opposite side entry order if (loUpper != null) { loUpper.Cancel(); loUpper = null; } OnEntryFilled(evt.FillPrice); } return; } } if (enableShorting) { if (loUpper != null && evt.OrderId == loUpper.OrderId) { if (evt.Status == OrderStatus.PartiallyFilled || evt.Status == OrderStatus.Filled) { // cancel opposite side entry order if (loLower != null) { loLower.Cancel(); loLower = null; } OnEntryFilled(evt.FillPrice); } return; } } if (loClose != null && evt.OrderId == loClose.OrderId) { if (evt.Status == OrderStatus.PartiallyFilled) { Liquidate(symbol); } if (evt.Status == OrderStatus.Filled) { Debug(String.Format("Exit filled at {0}", evt.FillPrice)); OnExitFilled(); UpdateEntryOrder(); return; } return; } if (evt.Status == OrderStatus.Submitted) { return; } if (isFlat) { var orderName = ""; if (loUpper != null && evt.OrderId == loUpper.OrderId) { orderName = "enter short"; } if (loLower != null && evt.OrderId == loLower.OrderId) { orderName = "enter long"; } if (loClose != null && evt.OrderId == loClose.OrderId) { orderName = "exit"; } Debug(String.Format("Order event while flat: {0} {1}", evt.Status, orderName)); } } public decimal GetPriceAtZScore(decimal zscore) { return Math.Round(zscore * 1.5m * stdev.Current.Value + mean.Current.Value + priceSeries[window], 5); } public decimal GetZScore() { decimal zscore = 0m; if (stdev.Current.Value > 0m || stdev.Current.Value < 0m) { zscore = (priceSeries[0] - priceSeries[window] - mean.Current.Value) / stdev.Current.Value; } return zscore; } } public class CustomSlippageModel : ISlippageModel { private readonly QCAlgorithm _algorithm; public CustomSlippageModel(QCAlgorithm algorithm) { _algorithm = algorithm; } public decimal GetSlippageApproximation(Security asset, Order order) { return 0m; } } }