New: Trailing Stop indicator

This commit is contained in:
Miha Kralj
2023-04-17 09:32:27 -07:00
parent b655c05d72
commit a232b7ffbe
9 changed files with 155 additions and 26 deletions
+34
View File
@@ -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;
}
@@ -2,56 +2,62 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Data;
using System.Linq; using System.Linq;
/* <summary> /* <summary>
TSeries is the cornerstone of all QuanTAlib classess. TSeries is the cornerstone of all QuanTAlib classes.
TSeries is a single List of tuples (time, value) and contains several operators, casts, overloads TSeries is a single List of tuples (time, value) and contains several operators, casts, overloads
and other helpers that simplify usage of library. and other helpers that simplify usage of library.
Think of TSeries as an equivalent of Numpy array. Think of TSeries as an equivalent of Numpy array.
- includes Length property (to mimic array's method) - includes Length property (to mimic array's method)
- includes publishing and subscribing methods that attach to events - includes publishing and subscribing methods that attach to events
</summary> */ </summary> */
public class TSeriesEventArgs : EventArgs{
public bool update { get; set; }
}
public class TSeries : List<(DateTime t, double v)> { public class TSeries : List<(DateTime t, double v)> {
public static implicit operator (DateTime t, double v)(TSeries l) => l[^1]; 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 double(TSeries l) => l[^1].v;
public static implicit operator DateTime(TSeries l) => l[^1].t; public static implicit operator DateTime(TSeries l) => l[^1].t;
public ReadOnlyCollection<DateTime> t => this.Select(item => item.t).ToList().AsReadOnly(); public List<DateTime> t => this.Select(item => item.t).ToList();
public ReadOnlyCollection<double> v => this.Select(item => item.v).ToList().AsReadOnly(); public List<double> v => this.Select(item => item.v).ToList();
public int Length => Count; public int Length => this.Count;
public TSeries Tail(int count = 10) { public TSeries Tail(int count = 10) {
var tailSeries = new TSeries(); var tailSeries = new TSeries();
tailSeries.AddRange(this.Skip(Math.Max(0, this.Count - count)).Take(count)); tailSeries.AddRange(this.Skip(Math.Max(0, this.Count - count)).Take(count));
return tailSeries; 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; } if (update) { this[^1] = TValue; }
else { base.Add(TValue); } else { base.Add(TValue); }
OnEvent(update); OnEvent(update);
return TValue;
} }
public void Add(DateTime t, double v, bool update = false) => this.Add((t, v), update); 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); public void Add(double v, bool update = false) => this.Add((DateTime.Now, v), update);
protected virtual void OnEvent(bool update = false) { protected virtual void OnEvent(bool update = false) {
Pub?.Invoke(this, new TSeriesEventArgs { update = update }); } Pub?.Invoke(this, new TSeriesEventArgs { update = update });
}
public delegate void NewDataEventHandler(object source, TSeriesEventArgs args); public delegate void NewDataEventHandler(object source, TSeriesEventArgs args);
public event NewDataEventHandler Pub; public event NewDataEventHandler Pub;
public void Sub(object source, TSeriesEventArgs e) { public void Sub(object source, TSeriesEventArgs e) {
TSeries ss = (TSeries)source; TSeries ss = (TSeries)source;
if (ss.Count > 0) { if (ss.Count > 0) {
this.AddRange(ss); this.AddRange(ss);
} else {
Add(ss[^1], e.update);
} }
} else {
} this.Add(ss[^1], e.update);
}
public class TSeriesEventArgs : EventArgs{ }
public bool update { get; set; }
} }
+1 -1
View File
@@ -18,7 +18,7 @@ Remark:
public class SDEV_Series : Single_TSeries_Indicator 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); } if (base._data.Count > 0) { base.Add(base._data); }
} }
+89
View File
@@ -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);
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ public class TSeries_Test
public void InsertingTuple() public void InsertingTuple()
{ {
TSeries s = new() { (t: DateTime.Today, v: double.Epsilon) }; 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] [Fact]