From a232b7ffbe0c8f16a4d6e2e5b39c28faa0c8b8db Mon Sep 17 00:00:00 2001 From: Miha Kralj Date: Mon, 17 Apr 2023 09:32:00 -0700 Subject: [PATCH] New: Trailing Stop indicator --- .../Pair_TSeries_Abstract.cs | 0 .../Single_TBars_Abstract.cs | 0 .../Single_TSeries_Abstract.cs | 0 .../{Basics => ClassStructures}/TBars.cs | 0 Calculations/ClassStructures/TOrders.cs | 34 +++++++ .../{Basics => ClassStructures}/TSeries.cs | 54 ++++++----- Calculations/Statistics/SDEV_Series.cs | 2 +- Indicators/Charts/TrailingStop.cs | 89 +++++++++++++++++++ Tests/Basics/TSeries_Test.cs | 2 +- 9 files changed, 155 insertions(+), 26 deletions(-) rename Calculations/{Basics => ClassStructures}/Pair_TSeries_Abstract.cs (100%) rename Calculations/{Basics => ClassStructures}/Single_TBars_Abstract.cs (100%) rename Calculations/{Basics => ClassStructures}/Single_TSeries_Abstract.cs (100%) rename Calculations/{Basics => ClassStructures}/TBars.cs (100%) create mode 100644 Calculations/ClassStructures/TOrders.cs rename Calculations/{Basics => ClassStructures}/TSeries.cs (53%) create mode 100644 Indicators/Charts/TrailingStop.cs diff --git a/Calculations/Basics/Pair_TSeries_Abstract.cs b/Calculations/ClassStructures/Pair_TSeries_Abstract.cs similarity index 100% rename from Calculations/Basics/Pair_TSeries_Abstract.cs rename to Calculations/ClassStructures/Pair_TSeries_Abstract.cs diff --git a/Calculations/Basics/Single_TBars_Abstract.cs b/Calculations/ClassStructures/Single_TBars_Abstract.cs similarity index 100% rename from Calculations/Basics/Single_TBars_Abstract.cs rename to Calculations/ClassStructures/Single_TBars_Abstract.cs diff --git a/Calculations/Basics/Single_TSeries_Abstract.cs b/Calculations/ClassStructures/Single_TSeries_Abstract.cs similarity index 100% rename from Calculations/Basics/Single_TSeries_Abstract.cs rename to Calculations/ClassStructures/Single_TSeries_Abstract.cs diff --git a/Calculations/Basics/TBars.cs b/Calculations/ClassStructures/TBars.cs similarity index 100% rename from Calculations/Basics/TBars.cs rename to Calculations/ClassStructures/TBars.cs diff --git a/Calculations/ClassStructures/TOrders.cs b/Calculations/ClassStructures/TOrders.cs new file mode 100644 index 00000000..bb1a1e71 --- /dev/null +++ b/Calculations/ClassStructures/TOrders.cs @@ -0,0 +1,34 @@ +namespace QuanTAlib; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Data; +using System.Linq; + + +public enum OType { + NIL = 0, // No position + BTO = 1, // Buy to Open + STC = 2, // Sell to Close + STO = 3, // Sell to Open + BTC = 4, // Buy to Close + END = 5, // Exit the trade +} + + +public class TOrders : List<(DateTime t, OType o)> { + + public void Add((DateTime t, OType o) TOrder, bool update = false) + { + if (update) { this[^1] = TOrder; } + else { base.Add(TOrder); } + OnEvent(update); + } + + + protected virtual void OnEvent(bool update = false) { + Pub?.Invoke(this, new TSeriesEventArgs { update = update }); } + public delegate void NewDataEventHandler(object source, TSeriesEventArgs args); + public event NewDataEventHandler Pub; + +} \ No newline at end of file diff --git a/Calculations/Basics/TSeries.cs b/Calculations/ClassStructures/TSeries.cs similarity index 53% rename from Calculations/Basics/TSeries.cs rename to Calculations/ClassStructures/TSeries.cs index 9dd6488d..eafec24d 100644 --- a/Calculations/Basics/TSeries.cs +++ b/Calculations/ClassStructures/TSeries.cs @@ -2,56 +2,62 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Data; using System.Linq; /* -TSeries is the cornerstone of all QuanTAlib classess. - TSeries is a single List of tuples (time, value) and contains several operators, casts, overloads - and other helpers that simplify usage of library. +TSeries is the cornerstone of all QuanTAlib classes. + TSeries is a single List of tuples (time, value) and contains several operators, casts, overloads + and other helpers that simplify usage of library. Think of TSeries as an equivalent of Numpy array. - + - includes Length property (to mimic array's method) - includes publishing and subscribing methods that attach to events */ + + +public class TSeriesEventArgs : EventArgs{ + public bool update { get; set; } +} + public class TSeries : List<(DateTime t, double v)> { public static implicit operator (DateTime t, double v)(TSeries l) => l[^1]; public static implicit operator double(TSeries l) => l[^1].v; public static implicit operator DateTime(TSeries l) => l[^1].t; - public ReadOnlyCollection t => this.Select(item => item.t).ToList().AsReadOnly(); - public ReadOnlyCollection v => this.Select(item => item.v).ToList().AsReadOnly(); - public int Length => Count; + public List t => this.Select(item => item.t).ToList(); + public List v => this.Select(item => item.v).ToList(); + public int Length => this.Count; public TSeries Tail(int count = 10) { var tailSeries = new TSeries(); tailSeries.AddRange(this.Skip(Math.Max(0, this.Count - count)).Take(count)); return tailSeries; } - public void Add((DateTime t, double v) TValue, bool update = false) { + public (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) { if (update) { this[^1] = TValue; } else { base.Add(TValue); } OnEvent(update); + return TValue; } public void Add(DateTime t, double v, bool update = false) => this.Add((t, v), update); - public void Add(double v, bool update = false) => this.Add((DateTime.Now, v), update); - protected virtual void OnEvent(bool update = false) { - Pub?.Invoke(this, new TSeriesEventArgs { update = update }); } + public void Add(double v, bool update = false) => this.Add((DateTime.Now, v), update); + protected virtual void OnEvent(bool update = false) { + Pub?.Invoke(this, new TSeriesEventArgs { update = update }); + } - public delegate void NewDataEventHandler(object source, TSeriesEventArgs args); - public event NewDataEventHandler Pub; + public delegate void NewDataEventHandler(object source, TSeriesEventArgs args); + public event NewDataEventHandler Pub; - public void Sub(object source, TSeriesEventArgs e) { - TSeries ss = (TSeries)source; - if (ss.Count > 0) { - this.AddRange(ss); - } else { - Add(ss[^1], e.update); + public void Sub(object source, TSeriesEventArgs e) { + TSeries ss = (TSeries)source; + if (ss.Count > 0) { + this.AddRange(ss); } - } -} - -public class TSeriesEventArgs : EventArgs{ - public bool update { get; set; } + else { + this.Add(ss[^1], e.update); + } + } } diff --git a/Calculations/Statistics/SDEV_Series.cs b/Calculations/Statistics/SDEV_Series.cs index 88f6ed8f..b85c3a16 100644 --- a/Calculations/Statistics/SDEV_Series.cs +++ b/Calculations/Statistics/SDEV_Series.cs @@ -18,7 +18,7 @@ Remark: public class SDEV_Series : Single_TSeries_Indicator { - public SDEV_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN) + public SDEV_Series(TSeries source, int period=0, bool useNaN = false) : base(source, period, useNaN) { if (base._data.Count > 0) { base.Add(base._data); } } diff --git a/Indicators/Charts/TrailingStop.cs b/Indicators/Charts/TrailingStop.cs new file mode 100644 index 00000000..3df040ca --- /dev/null +++ b/Indicators/Charts/TrailingStop.cs @@ -0,0 +1,89 @@ +using System; +using System.Diagnostics; +using System.Drawing; +using System.Linq; +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class TrailingStop_chart : Indicator { + #region Parameters + + [InputParameter("Period", 0, 1, 100, 1, 1)] + protected int _period = 30; + + [InputParameter("Factor", 1, 1, 100, 0.1, 1)] + protected double _factor = 10; + + [InputParameter("Long TS", 2)] + private bool _LongTS = true; + + [InputParameter("Short TS", 3)] + private bool _ShortTS = false; + + #endregion Parameters + + /////// + private HistoricalData History; + private TBars bars; + private ATR_Series _atr; + + private double _tslineL, _ratchetL, _tslineS, _ratchetS; + /////// + + public TrailingStop_chart() :base() { + Name = $"ATR Trailing Stop"; + AddLineSeries(lineName: "TrailingATR Long", lineColor: Color.Yellow, lineWidth: 1,lineStyle: LineStyle.Dot); + AddLineSeries(lineName: "Ratchet Long", lineColor: Color.Yellow, lineWidth: 3, lineStyle: LineStyle.Solid); + + AddLineSeries(lineName: "TrailingATR Short", lineColor: Color.Yellow, lineWidth: 1, lineStyle: LineStyle.Dot); + AddLineSeries(lineName: "Ratchet Short", lineColor: Color.Yellow, lineWidth: 3, lineStyle: LineStyle.Solid); + SeparateWindow = false; + } + + + protected override void OnInit() { + this.Name = $"Trailing Stop (ATR:{_period}, Mult:{_factor:f2})"; + this.bars = new(); + + this.History = this.Symbol.GetHistory(period: this.HistoricalData.Period, fromTime: HistoricalData.FromTime); + for (int i = this.History.Count - 1; i >= 0; i--) { + var rec = this.History[i, SeekOriginHistory.Begin]; + bars.Add(rec.TimeLeft, rec[PriceType.Open], + rec[PriceType.High], rec[PriceType.Low], + rec[PriceType.Close], rec[PriceType.Volume]); + } + _atr = new(source: bars, _period, useNaN: true); + _ratchetL = Double.NegativeInfinity; + _ratchetS = Double.PositiveInfinity; + + this.LinesSeries[0].Visible = _LongTS; + this.LinesSeries[1].Visible = _LongTS; + this.LinesSeries[2].Visible = _ShortTS; + this.LinesSeries[3].Visible = _ShortTS; + } + + protected override void OnUpdate(UpdateArgs args) { + bool update = !(args.Reason == UpdateReason.NewBar || + args.Reason == UpdateReason.HistoricalBar); + this.bars.Add(this.Time(), this.GetPrice(PriceType.Open), + this.GetPrice(PriceType.High), + this.GetPrice(PriceType.Low), + this.GetPrice(PriceType.Close), + this.GetPrice(PriceType.Volume), update); + + _tslineL = bars.High[^1].v - (_factor * _atr[^1].v); + _ratchetL = Math.Max(_tslineL,_ratchetL); + _ratchetL = (_ratchetL > bars.Low[^1].v) ? _tslineL : _ratchetL; + + _tslineS = bars.Low[^1].v + (_factor * _atr[^1].v); + _ratchetS = Math.Min(_tslineS, _ratchetS); + _ratchetS = (_ratchetS < bars.High[^1].v) ? _tslineS : _ratchetS; + + + this.SetValue(_tslineL, lineIndex: 0); + this.SetValue(_ratchetL, lineIndex: 1); + this.SetValue(_tslineS, lineIndex: 2); + this.SetValue(_ratchetS, lineIndex: 3); + } +} + diff --git a/Tests/Basics/TSeries_Test.cs b/Tests/Basics/TSeries_Test.cs index 84bf9d42..2344e85c 100644 --- a/Tests/Basics/TSeries_Test.cs +++ b/Tests/Basics/TSeries_Test.cs @@ -9,7 +9,7 @@ public class TSeries_Test public void InsertingTuple() { TSeries s = new() { (t: DateTime.Today, v: double.Epsilon) }; - Assert.Equal((DateTime.Today, double.Epsilon), s); + Assert.Equal((DateTime.Today, double.Epsilon), s[^1]); } [Fact]