| 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;
}
}
}