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]