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