diff --git a/Calculations/Logic/EQUITY_Series.cs b/Calculations/Logic/EQUITY_Series.cs index 63e8eea5..1257352d 100644 --- a/Calculations/Logic/EQUITY_Series.cs +++ b/Calculations/Logic/EQUITY_Series.cs @@ -13,46 +13,75 @@ EQUITY - Generates P&L portfolio based on trades signals and equity prices //optional: warmup period: warmup public class EQUITY_Series : Single_TSeries_Indicator { - int trade_state = 0; - readonly int _warmup = 0; - double eq_value = 0; - readonly TSeries _prices; - readonly bool _long, _short; - public EQUITY_Series(TSeries trades, TSeries prices, bool Long = true, bool Short = false, int Warmup = 0) : base(trades, period: 0, useNaN: false) { - _prices = prices; - _long = Long; - _short = Short; - _warmup = Warmup; + readonly TSeries inmarket; //for every bar + private readonly TSeries _price; + private double _equity; + private readonly double _capital; + + readonly int _warmup; + double _cash; + int _units; + private bool _longbuy, _longsell; + double _long_order, _open_order; + double _investment_value; + short _inmarket; + + public EQUITY_Series(TSeries signal, TSeries price, int warmup = 0, double capital = 1000) : base(signal, period: 0, useNaN: false) { + _capital = capital; + _cash = _capital; + _investment_value = 0; + _warmup = (warmup > 0) ? warmup : 1; + + inmarket = new(); + _longbuy = _longsell = false; + _open_order = 0; + _inmarket = 0; + _units = 0; + _long_order = 0; + + _price = price; //we buy on the Open price of the NEXT bar + _long_order = 0; + if (base._data.Count > 0) { base.Add(base._data); } } public override void Add((System.DateTime t, double v) TValue, bool update) { - if (this.Count != 0) - eq_value = this[this.Count - 1].v; - //buy signal - if (TValue.v == 1 && this.Count > _warmup) { - //we are not in-market and we can do long trades - if (_short) { trade_state = 0; } - if (_long) { trade_state = 1; } + if (this.Count > _warmup) { + + // harvest the gain-loss from previous day + _investment_value = _units * _price[this.Count - 1].v; + _equity = _cash + _investment_value; + + + //execute orders from previous bar + if (_longbuy && _inmarket == 0) { //time to execute the long buy + _units = (int)(_cash / _price[this.Count - 1].v); + _long_order = _units * _price[this.Count - 1].v; + _cash -= _long_order; + _open_order = _long_order; + _equity = _cash + _open_order; + _inmarket = 1; + _longbuy = false; + } + + if (_longsell && _inmarket == 1) { //time to execute the long sell + _long_order = (_units * _price[this.Count - 1].v); + _cash += _long_order; + _units = 0; + + _open_order = 0; + _equity = _cash + _open_order; + _inmarket = 0; + _longsell = false; + } + + if (_inmarket == 0 && TValue.v == 1) { _longbuy = true; } //out of market, enter long + if (_inmarket == 1 && TValue.v == -1) { _longsell = true; } //long market, exit long + + //Console.WriteLine($"{TValue.v,3}\t {(_inmarket)} : {_cash,10:f2} + {_units*_price[this.Count-1].v,7:f2} = {_equity-_capital:f2}"); } - - //sell signal - if (TValue.v == -1 && this.Count > _warmup) { - //we are in-market and we can do long trades - if (_long) { trade_state = 0; } - if (_short) { trade_state = -1; } - } - - if (trade_state == 1) { - eq_value = this[this.Count - 1].v + (_prices[this.Count].v - _prices[this.Count - 1].v); - - } - - if (trade_state == -1) { - eq_value = this[this.Count - 1].v + (_prices[this.Count - 1].v - _prices[this.Count].v); - - } - base.Add((TValue.t, eq_value), update, _NaN); + inmarket.Add(TValue.t, (double)_inmarket); + base.Add((TValue.t, _equity), update, _NaN); } } \ No newline at end of file diff --git a/Calculations/Trends/DEMA_Series.cs b/Calculations/Trends/DEMA_Series.cs index 1a568c85..6dea06e5 100644 --- a/Calculations/Trends/DEMA_Series.cs +++ b/Calculations/Trends/DEMA_Series.cs @@ -55,9 +55,6 @@ public class DEMA_Series : Single_TSeries_Indicator } else if (_len <= _period && _useSMA && _period != 0) { _sum += TValue.v; - if (_period != 0 && _len > _period) { - _sum -= (_data[base.Count - _period - (update ? 1 : 0)].v); - } _ema1 = _sum / Math.Min(_len, _period); _ema2 = _ema1; } diff --git a/Calculations/Trends/JMA_Series.cs b/Calculations/Trends/JMA_Series.cs index d9964848..f3d221a3 100644 --- a/Calculations/Trends/JMA_Series.cs +++ b/Calculations/Trends/JMA_Series.cs @@ -97,8 +97,8 @@ public class JMA_Series : Single_TSeries_Indicator { /// from avolty to rolty double rvolty = (avolty != 0) ? volty / avolty : 0; double len1 = (Math.Log(Math.Sqrt(_p)) / Math.Log(2.0)) + 2; - if (len1 < 0) - len1 = 0; + if (len1 < 0) { len1 = 0; } + double pow1 = Math.Max(len1 - 2.0, 0.5); if (rvolty > Math.Pow(len1, 1.0 / pow1)) { rvolty = Math.Pow(len1, 1.0 / pow1); } if (rvolty < 1) { rvolty = 1; } diff --git a/Calculations/Volatility/CMO_Series.cs b/Calculations/Volatility/CMO_Series.cs index 9295dfa5..6cabc235 100644 --- a/Calculations/Volatility/CMO_Series.cs +++ b/Calculations/Volatility/CMO_Series.cs @@ -26,7 +26,7 @@ public class CMO_Series : Single_TSeries_Indicator { public override void Add((DateTime t, double v) TValue, bool update) { if (this.Count == 0) { _plast_value = _last_value = TValue.v; } - if (update) _last_value = _plast_value; else _plast_value = _last_value; + if (update) {_last_value = _plast_value;} else {_plast_value = _last_value;} Add_Replace_Trim(_buff_up, (TValue.v > _last_value) ? TValue.v-_last_value : 0, _p, update); Add_Replace_Trim(_buff_dn, (TValue.v < _last_value) ? _last_value-TValue.v : 0, _p, update); @@ -40,8 +40,7 @@ public class CMO_Series : Single_TSeries_Indicator { } double _cmo = 100 * (_cmo_up - _cmo_dn) / (_cmo_up + _cmo_dn); - if (_cmo_up + _cmo_dn == 0) - _cmo = 0; + if (_cmo_up + _cmo_dn == 0) {_cmo = 0;} base.Add((TValue.t, _cmo), update, _NaN); } } \ No newline at end of file diff --git a/Indicators/Basics/QuanTAlib_Indicator.cs b/Indicators/Basics/QuanTAlib_Indicator.cs deleted file mode 100644 index 483ca3c5..00000000 --- a/Indicators/Basics/QuanTAlib_Indicator.cs +++ /dev/null @@ -1,55 +0,0 @@ -using TradingPlatform.BusinessLayer; -using System.Drawing; -using QuanTAlib; -using System; -using TradingPlatform.BusinessLayer.Chart; - -namespace QuanTAlib; - -public abstract class QuanTAlib_Indicator : Indicator { - protected TBars bars; - protected IChartWindow mainWindow; - protected Graphics graphics; - protected int firstOnScreenBarIndex, lastOnScreenBarIndex; - protected HistoricalData History; - protected int HistPeriod; - - protected override void OnInit() { - base.OnInit(); - bars = new(); - var dur1 = this.HistoricalData.FromTime; - var dur = this.HistoricalData.Period.Duration.TotalSeconds * (HistPeriod*4) ; //seconds of two periods - - 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]); - } - } - - protected override void OnUpdate(UpdateArgs args) { - base.OnUpdate(args); - bars.Add(Time(), GetPrice(PriceType.Open), - GetPrice(PriceType.High), - GetPrice(PriceType.Low), - GetPrice(PriceType.Close), - GetPrice(PriceType.Volume), - update: !(args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar)); - } - public override void OnPaintChart(PaintChartEventArgs args) { - base.OnPaintChart(args); - if (this.CurrentChart == null) return; - graphics = args.Graphics; - mainWindow = this.CurrentChart.MainWindow; - - DateTime leftTime = mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Left); - DateTime rightTime = mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Right); - firstOnScreenBarIndex = (int)mainWindow.CoordinatesConverter.GetBarIndex(leftTime); - lastOnScreenBarIndex = (int)Math.Ceiling(mainWindow.CoordinatesConverter.GetBarIndex(rightTime)); - } -} \ No newline at end of file diff --git a/Indicators/Charts/JMA_chart.cs b/Indicators/Charts/JMA_chart.cs index e2166ef4..366d759b 100644 --- a/Indicators/Charts/JMA_chart.cs +++ b/Indicators/Charts/JMA_chart.cs @@ -3,9 +3,10 @@ using System.Diagnostics; using System.Drawing; using System.Linq; using TradingPlatform.BusinessLayer; +using TradingPlatform.BusinessLayer.Chart; namespace QuanTAlib; -public class JMA_chart : QuanTAlib_Indicator { +public class JMA_chart : Indicator { #region Parameters [InputParameter("Data source", 0, variants: new object[] @@ -31,6 +32,12 @@ public class JMA_chart : QuanTAlib_Indicator { private JMA_Series indicator; /////// + protected TBars bars; + protected IChartWindow mainWindow; + protected Graphics graphics; + protected int firstOnScreenBarIndex, lastOnScreenBarIndex; + protected HistoricalData History; + protected int HistPeriod; public JMA_chart() :base() { Name = "JMA - Jurik Moving Avg"; Description = "Jurik Moving Average description"; @@ -42,6 +49,21 @@ public class JMA_chart : QuanTAlib_Indicator { protected override void OnInit() { base.OnInit(); + bars = new(); + var dur1 = this.HistoricalData.FromTime; + var dur = this.HistoricalData.Period.Duration.TotalSeconds * (HistPeriod * 4); //seconds of two periods + + 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]); + } + indicator = new(source: bars.Select(DataSource), period: Period, phase: Jphase, vshort: Vshort, vlong: Vlong, useNaN: true); @@ -49,6 +71,26 @@ public class JMA_chart : QuanTAlib_Indicator { protected override void OnUpdate(UpdateArgs args) { base.OnUpdate(args); + bars.Add(Time(), GetPrice(PriceType.Open), + GetPrice(PriceType.High), + GetPrice(PriceType.Low), + GetPrice(PriceType.Close), + GetPrice(PriceType.Volume), + update: !(args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar)); + this.SetValue(indicator[^1].v, lineIndex: 0); } + public override void OnPaintChart(PaintChartEventArgs args) { + base.OnPaintChart(args); + if (this.CurrentChart == null) + return; + graphics = args.Graphics; + mainWindow = this.CurrentChart.MainWindow; + + DateTime leftTime = mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Left); + DateTime rightTime = mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Right); + firstOnScreenBarIndex = (int)mainWindow.CoordinatesConverter.GetBarIndex(leftTime); + lastOnScreenBarIndex = (int)Math.Ceiling(mainWindow.CoordinatesConverter.GetBarIndex(rightTime)); + } + } diff --git a/Indicators/Charts/xMA_chart.cs b/Indicators/Charts/xMA_chart.cs index fbfb9731..a6b412b6 100644 --- a/Indicators/Charts/xMA_chart.cs +++ b/Indicators/Charts/xMA_chart.cs @@ -216,7 +216,7 @@ public class MovingAverage_chart : Indicator { overunder = new(MA1, MA2); trades = new(MA1, MA2); - equity = new(trades,prices: bars.Open,Long:LongTrades,Short:ShortTrades,Warmup:MA1Period+MA2Period); + equity = new(trades,price: bars.Open,warmup:MA1Period+MA2Period); } protected override void OnUpdate(UpdateArgs args) { @@ -253,7 +253,7 @@ public class MovingAverage_chart : Indicator { } public override void OnPaintChart(PaintChartEventArgs args) { base.OnPaintChart(args); - if (this.CurrentChart == null) return; + if (this.CurrentChart == null) {return;} Graphics graphics = args.Graphics; var mainWindow = this.CurrentChart.MainWindow; int leftIndex = (int)mainWindow.CoordinatesConverter.GetBarIndex(mainWindow.CoordinatesConverter.GetTime(mainWindow.ClientRectangle.Left)); diff --git a/Strategies/SimpleMACross1.cs b/Strategies/SimpleMACross1.cs index 98e14aa2..117af5ca 100644 --- a/Strategies/SimpleMACross1.cs +++ b/Strategies/SimpleMACross1.cs @@ -38,8 +38,8 @@ namespace SimpleMACross { } protected override void OnRun() { - if (this.CurrentAccount != null && this.CurrentAccount.State == BusinessObjectState.Fake) this.CurrentAccount = Core.Instance.GetAccount(this.CurrentAccount.CreateInfo()); - if (this.CurrentSymbol != null && this.CurrentSymbol.State == BusinessObjectState.Fake) this.CurrentSymbol = Core.Instance.GetSymbol(this.CurrentSymbol.CreateInfo()); + if (this.CurrentAccount != null && this.CurrentAccount.State == BusinessObjectState.Fake) {this.CurrentAccount = Core.Instance.GetAccount(this.CurrentAccount.CreateInfo());} + if (this.CurrentSymbol != null && this.CurrentSymbol.State == BusinessObjectState.Fake) {this.CurrentSymbol = Core.Instance.GetSymbol(this.CurrentSymbol.CreateInfo());} if (this.CurrentSymbol == null || this.CurrentAccount == null || this.CurrentSymbol.ConnectionId != this.CurrentAccount.ConnectionId) { this.Log("Incorrect input parameters... Symbol or Account are not specified or they have different connectionID.", StrategyLoggingLevel.Error); return; } @@ -59,12 +59,12 @@ namespace SimpleMACross { private void OnUpdate() { bool update = hdm.Last().TimeLeft - prev_time < this.period.Duration ? true : false; - if (!update) prev_time = hdm.Last().TimeLeft; + if (!update) {prev_time = hdm.Last().TimeLeft;} bars.Add(hdm.Last().TimeLeft, hdm.Last()[PriceType.Open], hdm.Last()[PriceType.High], hdm.Last()[PriceType.Low], hdm.Last()[PriceType.Close], hdm.Last()[PriceType.Volume], update); - if (!update) this.LogInfo($"{bars.Close.Last().t} OHLC4:{(double)bars.OHLC4}"); + if (!update) {this.LogInfo($"{bars.Close.Last().t} OHLC4:{(double)bars.OHLC4}");} }