| Overall Statistics |
|
Total Trades 895 Average Win 0.11% Average Loss -0.01% Compounding Annual Return -0.562% Drawdown 2.900% Expectancy -0.360 Net Profit -1.121% Sharpe Ratio -0.341 Probabilistic Sharpe Ratio 2.117% Loss Rate 94% Win Rate 6% Profit-Loss Ratio 9.55 Alpha -0.005 Beta 0.001 Annual Standard Deviation 0.013 Annual Variance 0 Information Ratio -1.006 Tracking Error 0.227 Treynor Ratio -5.724 Total Fees $0.00 |
using System;
using System.Collections.Generic;
using IntrinsicTime;
namespace QuantConnect.Algorithm.CSharp
{
public class AlphaEngine : QCAlgorithm
{
public Dictionary<Symbol, IntrinsicTimeChart> Charts;
private Dictionary<Symbol, decimal?> StopLoss;
private List<Symbol> _symbols;
private int _lookback;
private int _size;
public override void Initialize()
{
SetStartDate(2019, 1, 1);
SetCash(100000);
List<string> pairs = new List<string> {
"AUDJPY", "AUDNZD", "AUDUSD", "CADJPY", "CHFJPY",
"EURAUD", "EURCAD", "EURCHF", "EURGBP",
"EURJPY", "EURNZD", "EURUSD", "GBPAUD", "GBPCAD",
"GBPCHF", "GBPJPY", "GBPUSD", "NZDCAD",
"NZDJPY", "NZDUSD", "USDCAD", "USDCHF", "USDJPY",
};
_symbols = new List<Symbol> {};
_lookback = int.Parse(GetParameter("lookback"));
_size = int.Parse(GetParameter("size"));
decimal precision = Decimal.Parse(GetParameter("change_percentage"));
Charts = new Dictionary<Symbol, IntrinsicTimeChart>();
StopLoss = new Dictionary<Symbol, decimal?>();
for (int i = 0; i < pairs.Count; i++)
{
Forex fx = AddForex(pairs[i], Resolution.Minute);
_symbols.Add(fx.Symbol);
var chart = new IntrinsicTimeChart(5, fx.Symbol, precision, _lookback);
chart.OnChartUpdate += OnChartUpdate;
Charts.Add(fx.Symbol, chart);
StopLoss.Add(fx.Symbol, null);
}
}
public override void OnData(Slice data)
{
for (int i = 0; i < _symbols.Count; i++)
{
Symbol s = _symbols[i];
if (data.QuoteBars.ContainsKey(s))
{
Charts[s].Update(Time, data.QuoteBars[s].Ask);
}
if (Portfolio[s].Invested && data.QuoteBars[s].Ask.Low < StopLoss[s].Value)
{
Liquidate(s);
StopLoss[s] = null;
}
}
}
public void OnChartUpdate(object sender, Symbol symbol)
{
IntrinsicTimeChart chart = Charts[symbol];
if (chart.Window.Count < 2 || chart.Window[0].Reversal)
{
return;
}
IntrinsicTimeBar new_bar = chart.Window[0];
IntrinsicTimeBar last_bar = chart.Window[1];
if (new_bar.High > last_bar.High)
{
MarketOrder(symbol, _size);
StopLoss[symbol] = new_bar.Low;
}
}
}
}namespace IntrinsicTime
{
public class IntrinsicTimeChart
{
public int Precision;
public decimal? High;
public decimal? Low;
public int WindowLength;
public Symbol SecuritySymbol;
public RollingWindow<IntrinsicTimeBar> Window;
public event EventHandler<Symbol> OnChartUpdate;
private decimal _maxPercentageChange;
public IntrinsicTimeChart(int precision, Symbol s, decimal maxPercentageChange)
{
Precision = precision;
WindowLength = 3;
SecuritySymbol = s;
_maxPercentageChange = maxPercentageChange;
Window = new RollingWindow<IntrinsicTimeBar>(3);
}
public IntrinsicTimeChart(int precision, Symbol symbol, decimal maxPercentageChange, int length)
{
if (length < 3)
{
throw new ArgumentException("length must be 3 or more");
}
Precision = precision;
WindowLength = length;
SecuritySymbol = symbol;
_maxPercentageChange = maxPercentageChange;
Window = new RollingWindow<IntrinsicTimeBar>(length);
}
public void Update(DateTime time, Bar bar)
{
Update(time, bar.Open);
var distanceToLow = Math.Abs(Math.Round(bar.Open - bar.Low, Precision));
var distanceToHigh = Math.Abs(Math.Round(bar.Open - bar.High, Precision));
if (distanceToLow < distanceToHigh)
{
Update(time, bar.Low);
Update(time, bar.High);
}
else
{
Update(time, bar.High);
Update(time, bar.Low);
}
Update(time, bar.Close);
}
public void Update(DateTime time, decimal price)
{
High = High == null ? price : Math.Max(price, High.Value);
Low = Low == null ? price : Math.Min(price, Low.Value);
var delta = Math.Round(High.Value - Low.Value, Precision);
var mid = Math.Round((High.Value + Low.Value) * new Decimal(0.5), Precision);
var barHeight = Math.Round(mid * _maxPercentageChange, Precision);
var possibleNewUpper = Math.Round(Low.Value + barHeight, Precision);
var possibleNewLower = Math.Round(High.Value - barHeight, Precision);
if (delta < barHeight)
{
return;
}
if (price >= possibleNewUpper)
{
var isReversal = false;
if (Window.Count > 1)
{
isReversal = Window[1].Low > Window[0].Low;
}
Window.Add(new IntrinsicTimeBar(time, possibleNewUpper, Low.Value, isReversal));
High = possibleNewUpper;
Low = possibleNewUpper;
}
else if (price <= possibleNewLower)
{
var isReversal = false;
if (Window.Count > 1)
{
isReversal = Window[1].High < Window[0].High;
}
Window.Add(new IntrinsicTimeBar(time, High.Value, possibleNewLower, isReversal));
High = possibleNewLower;
Low = possibleNewLower;
}
else
{
throw new Exception("The impossible has happened");
}
if (Window.Count > 0)
{
OnChartUpdate?.Invoke(this, SecuritySymbol);
}
Update(time, price);
}
}
public class IntrinsicTimeBar
{
public bool Reversal;
public decimal High;
public decimal Low;
public DateTime Time;
public IntrinsicTimeBar(DateTime time, decimal high, decimal low, bool reversal)
{
Time = time;
Reversal = reversal;
High = high;
Low = low;
}
}
}