mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-24 05:28:05 +00:00
Atr, FlowIndicator and fixes
This commit is contained in:
@@ -90,6 +90,7 @@ public class Ema : AbstractBase
|
|||||||
_k = alpha;
|
_k = alpha;
|
||||||
_useSma = false;
|
_useSma = false;
|
||||||
_sma = new(1);
|
_sma = new(1);
|
||||||
|
Name = "Ema";
|
||||||
_period = 1;
|
_period = 1;
|
||||||
WarmupPeriod = (int)Math.Ceiling(Math.Log(0.05) / Math.Log(1 - _k)); //95th percentile
|
WarmupPeriod = (int)Math.Ceiling(Math.Log(0.05) / Math.Log(1 - _k)); //95th percentile
|
||||||
Init();
|
Init();
|
||||||
|
|||||||
+12
-9
@@ -6,7 +6,7 @@ namespace QuanTAlib;
|
|||||||
|
|
||||||
public class Jma : AbstractBase
|
public class Jma : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly int _period;
|
private readonly double _period;
|
||||||
private readonly double _phase;
|
private readonly double _phase;
|
||||||
private readonly CircularBuffer _vsumBuff;
|
private readonly CircularBuffer _vsumBuff;
|
||||||
private readonly CircularBuffer _avoltyBuff;
|
private readonly CircularBuffer _avoltyBuff;
|
||||||
@@ -22,6 +22,7 @@ public class Jma : AbstractBase
|
|||||||
public double UpperBand { get; set; }
|
public double UpperBand { get; set; }
|
||||||
public double LowerBand { get; set; }
|
public double LowerBand { get; set; }
|
||||||
public double Volty { get; set; }
|
public double Volty { get; set; }
|
||||||
|
public double Factor { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the Jma class with the specified parameters.
|
/// Initializes a new instance of the Jma class with the specified parameters.
|
||||||
@@ -31,18 +32,19 @@ public class Jma : AbstractBase
|
|||||||
/// <exception cref="ArgumentOutOfRangeException">
|
/// <exception cref="ArgumentOutOfRangeException">
|
||||||
/// Thrown when period is less than 1.
|
/// Thrown when period is less than 1.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public Jma(int period, int phase = 0)
|
public Jma(int period, int phase = 0, double factor = 0.45)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
||||||
}
|
}
|
||||||
|
Factor = factor;
|
||||||
_period = period;
|
_period = period;
|
||||||
_phase = Math.Clamp((phase * 0.01) + 1.5, 0.5, 2.5);
|
_phase = Math.Clamp((phase * 0.01) + 1.5, 0.5, 2.5);
|
||||||
|
|
||||||
_vsumBuff = new CircularBuffer(10);
|
_vsumBuff = new CircularBuffer(10);
|
||||||
_avoltyBuff = new CircularBuffer(65);
|
_avoltyBuff = new CircularBuffer(65);
|
||||||
_beta = 0.45 * (period - 1) / (0.45 * (period - 1) + 2);
|
_beta = factor * (_period - 1) / (factor * (_period - 1) + 2);
|
||||||
|
|
||||||
WarmupPeriod = period * 2;
|
WarmupPeriod = period * 2;
|
||||||
Name = $"JMA({period})";
|
Name = $"JMA({period})";
|
||||||
@@ -114,9 +116,10 @@ public class Jma : AbstractBase
|
|||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
|
|
||||||
double price = Input.Value;
|
double price = Input.Value;
|
||||||
if (_index == 1)
|
if (_index <= 1)
|
||||||
{
|
{
|
||||||
_upperBand = _lowerBand = price;
|
_upperBand = _lowerBand = price;
|
||||||
|
_prevMa1 = _prevJma = price;
|
||||||
}
|
}
|
||||||
|
|
||||||
double del1 = price - _upperBand;
|
double del1 = price - _upperBand;
|
||||||
@@ -124,7 +127,7 @@ public class Jma : AbstractBase
|
|||||||
double volty = Math.Max(Math.Abs(del1), Math.Abs(del2));
|
double volty = Math.Max(Math.Abs(del1), Math.Abs(del2));
|
||||||
|
|
||||||
_vsumBuff.Add(volty, Input.IsNew);
|
_vsumBuff.Add(volty, Input.IsNew);
|
||||||
_vSum += (_vsumBuff[^1] - _vsumBuff[0]) / 10;
|
_vSum += (_vsumBuff[^1] - _vsumBuff[0]) / _vsumBuff.Count;
|
||||||
_avoltyBuff.Add(_vSum, Input.IsNew);
|
_avoltyBuff.Add(_vSum, Input.IsNew);
|
||||||
double avgvolty = _avoltyBuff.Average();
|
double avgvolty = _avoltyBuff.Average();
|
||||||
|
|
||||||
@@ -137,15 +140,15 @@ public class Jma : AbstractBase
|
|||||||
_upperBand = (del1 >= 0) ? price : price - (Kv * del1);
|
_upperBand = (del1 >= 0) ? price : price - (Kv * del1);
|
||||||
_lowerBand = (del2 <= 0) ? price : price - (Kv * del2);
|
_lowerBand = (del2 <= 0) ? price : price - (Kv * del2);
|
||||||
|
|
||||||
double alpha = Math.Pow(_beta, pow2);
|
double _alpha = Math.Pow(_beta, pow2);
|
||||||
double ma1 = (1 - alpha) * Input.Value + alpha * _prevMa1;
|
double ma1 = Input.Value + _alpha * (_prevMa1 - Input.Value); //original: (1 - _alpha) * Input.Value + _alpha * _prevMa1;
|
||||||
_prevMa1 = ma1;
|
_prevMa1 = ma1;
|
||||||
|
|
||||||
double det0 = (price - ma1) * (1 - _beta) + _beta * _prevDet0;
|
double det0 = price + _beta * (_prevDet0 - price + ma1) - ma1; //original: (price - ma1) * (1 - _beta) + _beta * _prevDet0;
|
||||||
_prevDet0 = det0;
|
_prevDet0 = det0;
|
||||||
double ma2 = ma1 + _phase * det0;
|
double ma2 = ma1 + _phase * det0;
|
||||||
|
|
||||||
double det1 = ((ma2 - _prevJma) * (1 - alpha) * (1 - alpha) ) + (alpha * alpha * _prevDet1);
|
double det1 = ((ma2 - _prevJma) * (1 - _alpha) * (1 - _alpha) ) + (_alpha * _alpha * _prevDet1);
|
||||||
_prevDet1 = det1;
|
_prevDet1 = det1;
|
||||||
double jma = _prevJma + det1;
|
double jma = _prevJma + det1;
|
||||||
_prevJma = jma;
|
_prevJma = jma;
|
||||||
|
|||||||
+101
-33
@@ -1,18 +1,18 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// RMA: Relative Moving Average (also known as Wilder's Moving Average)
|
/// RMA: Relative Moving Average (also known as Wilder's Moving Average)
|
||||||
/// RMA is similar to EMA but uses a different smoothing factor.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
|
/// RMA is similar to EMA but uses a different smoothing factor.
|
||||||
|
///
|
||||||
/// Key characteristics:
|
/// Key characteristics:
|
||||||
/// - Uses no buffer, relying only on the previous RMA value.
|
/// - Uses no buffer, relying only on the previous RMA value.
|
||||||
/// - The weight of new data points (alpha) is calculated as 1 / period.
|
/// - The weight of new data points (alpha) is calculated as 1 / period.
|
||||||
/// - Provides a smoother curve compared to SMA and EMA, reacting more slowly to price changes.
|
/// - Provides a smoother curve compared to SMA and EMA, reacting more slowly to price changes.
|
||||||
///
|
///
|
||||||
/// Calculation method:
|
/// Calculation method:
|
||||||
/// RMA = (Previous RMA * (period - 1) + New Data) / period
|
/// This implementation can use SMA for the first Period bars as a seeding value for RMA when useSma is true.
|
||||||
///
|
///
|
||||||
/// Sources:
|
/// Sources:
|
||||||
/// - https://www.tradingview.com/pine-script-reference/v5/#fun_ta{dot}rma
|
/// - https://www.tradingview.com/pine-script-reference/v5/#fun_ta{dot}rma
|
||||||
@@ -20,75 +20,143 @@ namespace QuanTAlib;
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class Rma : AbstractBase
|
public class Rma : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly int _period;
|
// inherited _index
|
||||||
private double _lastRma;
|
// inherited _value
|
||||||
private readonly double _alpha;
|
|
||||||
private double _savedLastRma;
|
|
||||||
|
|
||||||
public Rma(int period)
|
/// <summary>
|
||||||
|
/// The period for the RMA calculation.
|
||||||
|
/// </summary>
|
||||||
|
private readonly int _period;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Circular buffer for SMA calculation.
|
||||||
|
/// </summary>
|
||||||
|
private CircularBuffer _sma;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The last calculated RMA value.
|
||||||
|
/// </summary>
|
||||||
|
private double _lastRma, _p_lastRma;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compensator for early RMA values.
|
||||||
|
/// </summary>
|
||||||
|
private double _e, _p_e;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The smoothing factor for RMA calculation.
|
||||||
|
/// </summary>
|
||||||
|
private readonly double _k;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flags to track initialization status.
|
||||||
|
/// </summary>
|
||||||
|
private bool _isInit, _p_isInit;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flag to determine whether to use SMA for initial values.
|
||||||
|
/// </summary>
|
||||||
|
private readonly bool _useSma;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the Rma class with a specified period.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="period">The period for RMA calculation.</param>
|
||||||
|
/// <param name="useSma">Whether to use SMA for initial values. Default is true.</param>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
|
||||||
|
public Rma(int period, bool useSma = true)
|
||||||
{
|
{
|
||||||
if (period < 1)
|
if (period < 1)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period));
|
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
||||||
}
|
}
|
||||||
_period = period;
|
_period = period;
|
||||||
WarmupPeriod = period * 2;
|
_k = 1.0 / _period; // Wilder's smoothing factor
|
||||||
_alpha = 1.0 / _period; // Wilder's smoothing factor
|
_useSma = useSma;
|
||||||
Name = $"Rma({_period})";
|
_sma = new(period);
|
||||||
|
Name = "Rma";
|
||||||
|
WarmupPeriod = _period * 2; // RMA typically needs more warmup periods
|
||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Rma(object source, int period) : this(period)
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the Rma class with a specified source and period.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">The source object for event subscription.</param>
|
||||||
|
/// <param name="period">The period for RMA calculation.</param>
|
||||||
|
/// <param name="useSma">Whether to use SMA for initial values. Default is true.</param>
|
||||||
|
public Rma(object source, int period, bool useSma = true) : this(period, useSma)
|
||||||
{
|
{
|
||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the Rma instance.
|
||||||
|
/// </summary>
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
|
_e = 1.0;
|
||||||
_lastRma = 0;
|
_lastRma = 0;
|
||||||
_savedLastRma = 0;
|
_isInit = false;
|
||||||
|
_p_isInit = false;
|
||||||
|
_sma = new(_period);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manages the state of the Rma instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isNew">Indicates whether the input is new.</param>
|
||||||
protected override void ManageState(bool isNew)
|
protected override void ManageState(bool isNew)
|
||||||
{
|
{
|
||||||
if (isNew)
|
if (isNew)
|
||||||
{
|
{
|
||||||
_savedLastRma = _lastRma;
|
_p_lastRma = _lastRma;
|
||||||
_lastValidValue = Input.Value;
|
_p_isInit = _isInit;
|
||||||
|
_p_e = _e;
|
||||||
_index++;
|
_index++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_lastRma = _savedLastRma;
|
_lastRma = _p_lastRma;
|
||||||
|
_isInit = _p_isInit;
|
||||||
|
_e = _p_e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs the RMA calculation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The calculated RMA value.</returns>
|
||||||
protected override double Calculation()
|
protected override double Calculation()
|
||||||
{
|
{
|
||||||
|
double result, _rma;
|
||||||
ManageState(Input.IsNew);
|
ManageState(Input.IsNew);
|
||||||
|
|
||||||
double rma;
|
// when _UseSma == true, use SMA calculation until we have enough data points
|
||||||
|
if (!_isInit && _useSma)
|
||||||
if (_index == 1)
|
|
||||||
{
|
{
|
||||||
rma = Input.Value;
|
_sma.Add(Input.Value, Input.IsNew);
|
||||||
}
|
_rma = _sma.Average();
|
||||||
else if (_index <= _period)
|
result = _rma;
|
||||||
{
|
if (_index >= _period)
|
||||||
// Simple average during initial period
|
{
|
||||||
rma = (_lastRma * (_index - 1) + Input.Value) / _index;
|
_isInit = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Wilder's smoothing method
|
// compensator for early rma values
|
||||||
rma = _alpha * (_lastRma - Input.Value) + _lastRma;
|
_e = (_e > 1e-10) ? (1 - _k) * _e : 0;
|
||||||
|
|
||||||
|
_rma = _k * Input.Value + (1 - _k) * _lastRma;
|
||||||
|
|
||||||
|
// _useSma decides if we use compensator or not
|
||||||
|
result = (_useSma || _e <= double.Epsilon) ? _rma : _rma / (1 - _e);
|
||||||
}
|
}
|
||||||
|
_lastRma = _rma;
|
||||||
_lastRma = rma;
|
|
||||||
IsHot = _index >= WarmupPeriod;
|
IsHot = _index >= WarmupPeriod;
|
||||||
|
return result;
|
||||||
return rma;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,12 @@ public abstract class AbstractBase : ITValue
|
|||||||
Input2 = new(Time: Input.Time, Value: double.NaN, IsNew: Input.IsNew, IsHot: Input.IsHot);
|
Input2 = new(Time: Input.Time, Value: double.NaN, IsNew: Input.IsNew, IsHot: Input.IsHot);
|
||||||
return Process(input.Value, input.Time, input.IsNew);
|
return Process(input.Value, input.Time, input.IsNew);
|
||||||
}
|
}
|
||||||
|
public virtual TValue Calc(double value, bool IsNew)
|
||||||
|
{
|
||||||
|
Input = new(this.Time, Value: value, IsNew: IsNew, IsHot: false);
|
||||||
|
Input2 = new(this.Time, double.NaN, false, false);
|
||||||
|
return Process(Input.Value, Input.Time, Input.IsNew);
|
||||||
|
}
|
||||||
|
|
||||||
public virtual TValue Calc(TBar barInput)
|
public virtual TValue Calc(TBar barInput)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ public TBar() : this(DateTime.UtcNow, 0, 0, 0, 0, 0) { }
|
|||||||
public TBar(double Open, double High, double Low, double Close, double Volume, bool IsNew = true) : this(DateTime.UtcNow, Open, High, Low, Close, Volume, IsNew) { }
|
public TBar(double Open, double High, double Low, double Close, double Volume, bool IsNew = true) : this(DateTime.UtcNow, Open, High, Low, Close, Volume, IsNew) { }
|
||||||
public TBar(double value) : this(Time: DateTime.UtcNow, Open: value, High: value, Low: value, Close: value, Volume: value, IsNew: true) { }
|
public TBar(double value) : this(Time: DateTime.UtcNow, Open: value, High: value, Low: value, Close: value, Volume: value, IsNew: true) { }
|
||||||
public TBar(TValue value) : this(Time: value.Time, Open: value.Value, High: value.Value, Low: value.Value, Close: value.Value, Volume: value.Value, IsNew: value.IsNew) { }
|
public TBar(TValue value) : this(Time: value.Time, Open: value.Value, High: value.Value, Low: value.Value, Close: value.Value, Volume: value.Value, IsNew: value.IsNew) { }
|
||||||
|
public TBar(TBar v) : this(Time: v.Time, Open: v.Open, High: v.High, Low: v.Low, Close: v.Close, Volume: v.Volume, IsNew: true) { }
|
||||||
|
|
||||||
|
|
||||||
public static implicit operator double(TBar bar) => bar.Close;
|
public static implicit operator double(TBar bar) => bar.Close;
|
||||||
public static implicit operator DateTime(TBar tv) => tv.Time;
|
public static implicit operator DateTime(TBar tv) => tv.Time;
|
||||||
|
|||||||
+2
-2
@@ -52,13 +52,13 @@ public class TSeries : List<TValue>
|
|||||||
var pubEvent = source.GetType().GetEvent("Pub");
|
var pubEvent = source.GetType().GetEvent("Pub");
|
||||||
if (pubEvent != null)
|
if (pubEvent != null)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
var nameProperty = source.GetType().GetProperty("Name");
|
var nameProperty = source.GetType().GetProperty("Name");
|
||||||
if (nameProperty != null)
|
if (nameProperty != null)
|
||||||
{
|
{
|
||||||
Name = nameProperty.GetValue(nameProperty)?.ToString()!;
|
Name = nameProperty.GetValue(nameProperty)?.ToString()!;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
pubEvent.AddEventHandler(source, new ValueSignal(Sub));
|
pubEvent.AddEventHandler(source, new ValueSignal(Sub));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-19
@@ -6,11 +6,11 @@ public class GbmFeed : TBarSeries
|
|||||||
{
|
{
|
||||||
private readonly double _mu, _sigma;
|
private readonly double _mu, _sigma;
|
||||||
private readonly RandomNumberGenerator _rng;
|
private readonly RandomNumberGenerator _rng;
|
||||||
private double _lastClose, _lastHigh, _lastLow;
|
private double _lastClose;
|
||||||
|
|
||||||
public GbmFeed(double initialPrice = 100.0, double mu = 0.05, double sigma = 0.2)
|
public GbmFeed(double initialPrice = 100.0, double mu = 0.05, double sigma = 0.2)
|
||||||
{
|
{
|
||||||
_lastClose = _lastHigh = _lastLow = initialPrice;
|
_lastClose = initialPrice;
|
||||||
_mu = mu;
|
_mu = mu;
|
||||||
_sigma = sigma;
|
_sigma = sigma;
|
||||||
_rng = RandomNumberGenerator.Create();
|
_rng = RandomNumberGenerator.Create();
|
||||||
@@ -24,9 +24,7 @@ public class GbmFeed : TBarSeries
|
|||||||
DateTime startTime = DateTime.UtcNow - TimeSpan.FromHours(count);
|
DateTime startTime = DateTime.UtcNow - TimeSpan.FromHours(count);
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
Add(startTime, true);
|
Add(startTime, isNew: true);
|
||||||
Add(startTime, false);
|
|
||||||
Add(startTime, false);
|
|
||||||
startTime = startTime.AddHours(1);
|
startTime = startTime.AddHours(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,27 +34,29 @@ public class GbmFeed : TBarSeries
|
|||||||
double dt = 1.0 / 252;
|
double dt = 1.0 / 252;
|
||||||
double drift = (_mu - 0.5 * _sigma * _sigma) * dt;
|
double drift = (_mu - 0.5 * _sigma * _sigma) * dt;
|
||||||
double diffusion = _sigma * Math.Sqrt(dt) * GenerateNormalRandom();
|
double diffusion = _sigma * Math.Sqrt(dt) * GenerateNormalRandom();
|
||||||
double newClose = _lastClose * Math.Exp(drift + diffusion);
|
|
||||||
|
|
||||||
double open = _lastClose;
|
double open = _lastClose;
|
||||||
double high = Math.Max(_lastHigh, Math.Max(open, newClose) * (1 + GenerateRandomDouble() * 0.01));
|
double close = open * Math.Exp(drift + diffusion);
|
||||||
double low = Math.Min(_lastLow, Math.Min(open, newClose) * (1 - GenerateRandomDouble() * 0.01));
|
|
||||||
|
// Generate intra-bar price movements
|
||||||
|
double maxMove = Math.Abs(close - open) * 1.5; // Allow for some extra movement within the bar
|
||||||
|
double high = Math.Max(open, close) + maxMove * GenerateRandomDouble();
|
||||||
|
double low = Math.Min(open, close) - maxMove * GenerateRandomDouble();
|
||||||
|
|
||||||
|
// Ensure high is always greater than or equal to both open and close
|
||||||
|
high = Math.Max(high, Math.Max(open, close));
|
||||||
|
|
||||||
|
// Ensure low is always less than or equal to both open and close
|
||||||
|
low = Math.Min(low, Math.Min(open, close));
|
||||||
|
|
||||||
double volume = 1000 + GenerateRandomDouble() * 1000;
|
double volume = 1000 + GenerateRandomDouble() * 1000;
|
||||||
|
|
||||||
if (isNew)
|
if (isNew)
|
||||||
{
|
{
|
||||||
_lastClose = newClose;
|
_lastClose = close;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
high = Math.Max(_lastHigh, high);
|
|
||||||
low = Math.Min(_lastLow, low);
|
|
||||||
}
|
|
||||||
_lastHigh = high;
|
|
||||||
_lastLow = low;
|
|
||||||
|
|
||||||
TBar bar = new(time, open, high, low, newClose, volume, isNew);
|
return new TBar(time, open, high, low, close, volume, isNew);
|
||||||
return bar;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private double GenerateNormalRandom()
|
private double GenerateNormalRandom()
|
||||||
@@ -73,4 +73,4 @@ public class GbmFeed : TBarSeries
|
|||||||
_rng.GetBytes(bytes);
|
_rng.GetBytes(bytes);
|
||||||
return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue;
|
return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
<PackageIconUrl>https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png</PackageIconUrl>
|
<PackageIconUrl>https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png</PackageIconUrl>
|
||||||
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||||
|
<NoWarn>$(NoWarn);NU1903;NU5104</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
+21
-16
@@ -4,13 +4,14 @@ namespace QuanTAlib;
|
|||||||
/// Represents an Average True Range (ATR) calculator, a measure of market volatility.
|
/// Represents an Average True Range (ATR) calculator, a measure of market volatility.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The ATR class calculates the average true range using an Exponential Moving Average (EMA)
|
/// The ATR class calculates the average true range using a Relative Moving Average (RMA)
|
||||||
/// of the true range. The true range is the greatest of: current high - current low,
|
/// of the true range. The true range is the greatest of: current high - current low,
|
||||||
/// absolute value of current high - previous close, or absolute value of current low - previous close.
|
/// absolute value of current high - previous close, or absolute value of current low - previous close.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class Atr : AbstractBase
|
public class Atr : AbstractBase
|
||||||
{
|
{
|
||||||
private readonly Ema _ma;
|
public double Tr { get; private set; }
|
||||||
|
private readonly Rma _ma;
|
||||||
private double _prevClose, _p_prevClose;
|
private double _prevClose, _p_prevClose;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -26,7 +27,7 @@ public class Atr : AbstractBase
|
|||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
||||||
}
|
}
|
||||||
_ma = new(1.0 / period);
|
_ma = new(period, useSma: true);
|
||||||
WarmupPeriod = _ma.WarmupPeriod;
|
WarmupPeriod = _ma.WarmupPeriod;
|
||||||
Name = $"ATR({period})";
|
Name = $"ATR({period})";
|
||||||
}
|
}
|
||||||
@@ -50,6 +51,7 @@ public class Atr : AbstractBase
|
|||||||
base.Init();
|
base.Init();
|
||||||
_ma.Init();
|
_ma.Init();
|
||||||
_prevClose = double.NaN;
|
_prevClose = double.NaN;
|
||||||
|
Tr = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -76,7 +78,7 @@ public class Atr : AbstractBase
|
|||||||
/// The calculated ATR value for the current bar.
|
/// The calculated ATR value for the current bar.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This method calculates the true range for the current bar and then uses an EMA
|
/// This method calculates the true range for the current bar and then uses an RMA
|
||||||
/// to smooth the true range values. For the first bar, it uses the high-low range
|
/// to smooth the true range values. For the first bar, it uses the high-low range
|
||||||
/// as the true range.
|
/// as the true range.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
@@ -84,22 +86,25 @@ public class Atr : AbstractBase
|
|||||||
{
|
{
|
||||||
ManageState(BarInput.IsNew);
|
ManageState(BarInput.IsNew);
|
||||||
|
|
||||||
double trueRange = Math.Max(
|
if (_index == 1)
|
||||||
Math.Max(
|
|
||||||
BarInput.High - BarInput.Low,
|
|
||||||
Math.Abs(BarInput.High - _prevClose)
|
|
||||||
),
|
|
||||||
Math.Abs(BarInput.Low - _prevClose)
|
|
||||||
);
|
|
||||||
if (_index < 2)
|
|
||||||
{
|
{
|
||||||
trueRange = BarInput.High - BarInput.Low;
|
Tr = BarInput.High - BarInput.Low;
|
||||||
|
_prevClose = BarInput.Close;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Tr = Math.Max(
|
||||||
|
BarInput.High - BarInput.Low,
|
||||||
|
Math.Max(
|
||||||
|
Math.Abs(BarInput.High - _prevClose),
|
||||||
|
Math.Abs(BarInput.Low - _prevClose)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ma.Calc(new TValue(Input.Time, Tr, BarInput.IsNew));
|
||||||
|
|
||||||
TValue emaTrueRange = _ma.Calc(new TValue(Input.Time, trueRange, Input.IsNew));
|
|
||||||
IsHot = _ma.IsHot;
|
IsHot = _ma.IsHot;
|
||||||
_prevClose = BarInput.Close;
|
_prevClose = BarInput.Close;
|
||||||
|
return _ma.Value;
|
||||||
return emaTrueRange.Value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-99
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#!csharp
|
#!csharp
|
||||||
|
|
||||||
#r "..\src\obj\Debug\QuanTAlib.dll"
|
#r "..\lib\obj\Debug\QuanTAlib.dll"
|
||||||
|
|
||||||
#r "nuget:Skender.Stock.Indicators"
|
#r "nuget:Skender.Stock.Indicators"
|
||||||
using Skender.Stock.Indicators;
|
using Skender.Stock.Indicators;
|
||||||
@@ -13,113 +13,56 @@ QuanTAlib.Formatters.Initialize();
|
|||||||
|
|
||||||
#!csharp
|
#!csharp
|
||||||
|
|
||||||
|
Atr ma = new(10);
|
||||||
GbmFeed gbm = new();
|
GbmFeed gbm = new();
|
||||||
EmaCalc ema1 = new(gbm.Close, 10, useSma: false);
|
gbm.Add(30);
|
||||||
EmaCalc ema2 = new(gbm.Close, 10, useSma: true);
|
IEnumerable<Quote> quotes = gbm.Select(item => new Quote { Date = item.Time, Open = (decimal)item.Open, High = (decimal)item.High, Low = (decimal)item.Low, Close = (decimal)item.Close, Volume = (decimal)item.Volume });
|
||||||
TValSeries res1 = new(ema1);
|
var SkResults = quotes.GetAtr(10).Select(i => i.Atr.Null2NaN()!);
|
||||||
TValSeries res2 = new(ema2);
|
|
||||||
gbm.Add(50);
|
|
||||||
List<double> mse1 = new();
|
|
||||||
List<double> mse2 = new();
|
|
||||||
|
|
||||||
|
|
||||||
for (int i=0; i< gbm.Length; i++) {
|
for (int i=0; i< gbm.Length; i++) {
|
||||||
double v= gbm.Close[i].Value;
|
ma.Calc(gbm[i]);
|
||||||
double e1 = res1[i].Value;
|
Console.WriteLine($"{i,3} {ma.Value,10:F3} \t {SkResults.ElementAt(i):F3}");
|
||||||
mse1.Add((e1-v)*(e1-v));
|
|
||||||
double e2 = res2[i].Value;
|
|
||||||
mse2.Add((e2-v)*(e2-v));
|
|
||||||
|
|
||||||
//Console.WriteLine($"{i,3} {mse1.Average(),10:F4} {mse2.Average(),10:F4}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine($"{mse2.Average()-mse1.Average(),10:F8}");
|
|
||||||
|
|
||||||
#!csharp
|
|
||||||
|
|
||||||
display(res1);
|
|
||||||
|
|
||||||
#!csharp
|
#!csharp
|
||||||
|
|
||||||
|
Atr ma = new(10);
|
||||||
GbmFeed gbm = new();
|
GbmFeed gbm = new();
|
||||||
EmaCalc ema1 = new(gbm.Close, 10, useSma: false);
|
gbm.Add(30);
|
||||||
EmaCalc ema2 = new(gbm.Close, 10, useSma: true);
|
IEnumerable<Quote> quotes = gbm.Select(item => new Quote { Date = item.Time, Open = (decimal)item.Open, High = (decimal)item.High, Low = (decimal)item.Low, Close = (decimal)item.Close, Volume = (decimal)item.Volume });
|
||||||
TValSeries res1 = new(ema1);
|
var SkResults = quotes.GetTr().Select(i => i.Tr.Null2NaN()!);
|
||||||
TValSeries res2 = new(ema2);
|
for (int i=0; i< gbm.Length; i++) {
|
||||||
|
ma.Calc(new TBar(gbm[i]));
|
||||||
|
|
||||||
|
Console.WriteLine($"{gbm.High[i].Value,6:F4} \t{gbm.Low[i].Value,6:F4} \t{gbm.Close[i].Value,6:F4} \t{ma.Tr,10:F4} \t{SkResults.ElementAt(i),10:F4}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#!csharp
|
||||||
|
|
||||||
|
//ATR test
|
||||||
|
GbmFeed gbm = new();
|
||||||
|
TBarSeries feed = new(gbm);
|
||||||
|
|
||||||
|
Atr ma1 = new(gbm, 10);
|
||||||
|
TSeries res1 = new(ma1);
|
||||||
|
gbm.Add(30);
|
||||||
|
IEnumerable<Quote> quotes = gbm.Select(item => new Quote { Date = item.Time, Open = (decimal)item.Open, High = (decimal)item.High, Low = (decimal)item.Low, Close = (decimal)item.Close, Volume = (decimal)item.Volume });
|
||||||
|
var SkResults = quotes.GetAtr(10).Select(i => i.Atr.Null2NaN()!);
|
||||||
|
for (int i=0; i< gbm.Length; i++) {
|
||||||
|
double delta = Math.Round(res1[i].Value, 10) - Math.Round(SkResults.ElementAt(i), 10);
|
||||||
|
//Console.WriteLine($"{i,3} {gbm.High[i].Value,6:F2} {gbm.Low[i].Value,6:F2} {gbm.Close[i].Value,6:F2} {res1[i].Value,10:F4} {SkResults.ElementAt(i),10:F4}\t{delta}");
|
||||||
|
Console.WriteLine($"{i,3} h:{gbm.High[i].Value,6:F2} l:{gbm.Low[i].Value,6:F2} c:{gbm.Close[i].Value,6:F2} {res1[i].Atr,10:F4} {SkResults.ElementAt(i),10:F4}\t{delta}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#!csharp
|
||||||
|
|
||||||
|
//EMA test
|
||||||
|
GbmFeed gbm = new();
|
||||||
|
Ema ema1 = new(gbm.Close, 10, useSma: true);
|
||||||
|
TSeries res1 = new(ema1);
|
||||||
gbm.Add(30);
|
gbm.Add(30);
|
||||||
IEnumerable<Quote> quotes = gbm.Close.Select(item => new Quote { Date = item.Time, Close = (decimal)item.Value });
|
IEnumerable<Quote> quotes = gbm.Close.Select(item => new Quote { Date = item.Time, Close = (decimal)item.Value });
|
||||||
var SkResults = quotes.GetEma(10).Select(i => i.Ema.Null2NaN()!);
|
var SkResults = quotes.GetEma(10).Select(i => i.Ema.Null2NaN()!);
|
||||||
for (int i=0; i< gbm.Length; i++) {
|
for (int i=0; i< gbm.Length; i++) {
|
||||||
Console.WriteLine($"{i,3} {gbm.Close[i].Value,6:F2} {res1[i].Value,10:F4} {res2[i].Value,10:F4} {SkResults.ElementAt(i),10:F4}");
|
double delta = Math.Round(res1[i].Value, 10) - Math.Round(SkResults.ElementAt(i), 10);
|
||||||
|
Console.WriteLine($"{i,3} {gbm.Close[i].Value,6:F2} {res1[i].Value,10:F4} {SkResults.ElementAt(i),10:F4}\t{delta}");
|
||||||
}
|
}
|
||||||
|
|
||||||
#!csharp
|
|
||||||
|
|
||||||
TValSeries test = new();
|
|
||||||
|
|
||||||
EmaCalc ma1 = new(test, 7, true);
|
|
||||||
TValSeries res1 = new(ma1);
|
|
||||||
|
|
||||||
EmaCalc ma2 = new(test, 7, false);
|
|
||||||
TValSeries res2 = new(ma2);
|
|
||||||
|
|
||||||
test.Add(new[]{1.0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0});
|
|
||||||
|
|
||||||
for (int i=0; i<res1.Count; i++) {
|
|
||||||
Console.WriteLine($"{i,2} {test[i].Value,7:F4} {res1[i].Value,7:F4} {res2[i].Value,7:F4}");
|
|
||||||
}
|
|
||||||
|
|
||||||
#!csharp
|
|
||||||
|
|
||||||
TValSeries test = new();
|
|
||||||
EmaCalc ma = new(test,3);
|
|
||||||
TValSeries result = new();
|
|
||||||
|
|
||||||
for (int i=1; i<10; i++) {
|
|
||||||
test.Add(new TValue(DateTime.Now, (double)i, true, true));
|
|
||||||
result.Add(ma.Tick);
|
|
||||||
}
|
|
||||||
display(result);
|
|
||||||
|
|
||||||
#!csharp
|
|
||||||
|
|
||||||
TValSeries test = new();
|
|
||||||
SmaCalc ma = new(test,7);
|
|
||||||
TValSeries result = new(ma);
|
|
||||||
test.Add(new[]{81.59, 81.06, 82.87, 83.00, 83.61, 83.15, 82.84, 83.99, 84.55, 84.36, 85.53, 86.54, 86.89, 87.77, 87.29});
|
|
||||||
//test.Add(new[]{1.0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0});
|
|
||||||
|
|
||||||
display(result);
|
|
||||||
|
|
||||||
#!csharp
|
|
||||||
|
|
||||||
TValue test = new(DateTime.Today, 100, IsHot: false);
|
|
||||||
TValSeries pub = new();
|
|
||||||
TValSeries sub = new(pub);
|
|
||||||
pub.Add(test);
|
|
||||||
pub.Add(test);
|
|
||||||
pub.Add(2, true);
|
|
||||||
pub.Add(DateTime.Today, 123.1234214234, IsHot: true);
|
|
||||||
|
|
||||||
|
|
||||||
display(sub);
|
|
||||||
display(test);
|
|
||||||
|
|
||||||
#!csharp
|
|
||||||
|
|
||||||
TBar test = new(DateTime.Now, double.NaN,1,2,3,400.1234);
|
|
||||||
|
|
||||||
TBarSeries source = new();
|
|
||||||
TValSeries target = new(source.Close);
|
|
||||||
|
|
||||||
source.Add(new TBar(DateTime.Now,1,2,3,4,125, true));
|
|
||||||
source.Add(new TBar(DateTime.Now,2,1,5,2,1312, true));
|
|
||||||
source.Add(test);
|
|
||||||
source.Name = "MSFT";
|
|
||||||
display(source);
|
|
||||||
display(test)
|
|
||||||
|
|
||||||
#!csharp
|
|
||||||
|
|
||||||
#r "nuget:Skender.Stock.Indicators"
|
|
||||||
using Skender.Stock.Indicators;
|
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ public class JmaIndicator : Indicator, IWatchlistIndicator
|
|||||||
[InputParameter("Phase", sortIndex: 2, -100, 100, 1, 0)]
|
[InputParameter("Phase", sortIndex: 2, -100, 100, 1, 0)]
|
||||||
public int Phase { get; set; } = 0;
|
public int Phase { get; set; } = 0;
|
||||||
|
|
||||||
|
[InputParameter("Beta factor", sortIndex: 3, minimum: 0, maximum:5 , increment: 0.01, decimalPlaces: 2)]
|
||||||
|
public double Factor { get; set; } = 0.45;
|
||||||
|
|
||||||
[InputParameter("Data source", sortIndex: 4, variants: [
|
[InputParameter("Data source", sortIndex: 4, variants: [
|
||||||
"Open", SourceType.Open,
|
"Open", SourceType.Open,
|
||||||
"High", SourceType.High,
|
"High", SourceType.High,
|
||||||
@@ -34,7 +37,7 @@ public class JmaIndicator : Indicator, IWatchlistIndicator
|
|||||||
public int MinHistoryDepths => Math.Max(65,Periods * 2);
|
public int MinHistoryDepths => Math.Max(65,Periods * 2);
|
||||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||||
|
|
||||||
public override string ShortName => $"JMA {Periods}:{Phase}:{SourceName}";
|
public override string ShortName => $"JMA {Periods}:{Phase}:{Factor:F2}:{SourceName}";
|
||||||
|
|
||||||
public JmaIndicator()
|
public JmaIndicator()
|
||||||
{
|
{
|
||||||
@@ -49,7 +52,7 @@ public class JmaIndicator : Indicator, IWatchlistIndicator
|
|||||||
|
|
||||||
protected override void OnInit()
|
protected override void OnInit()
|
||||||
{
|
{
|
||||||
ma = new Jma(Periods, Phase);
|
ma = new Jma(period: Periods, phase: Phase, factor: Factor);
|
||||||
SourceName = Source.ToString();
|
SourceName = Source.ToString();
|
||||||
base.OnInit();
|
base.OnInit();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,14 +59,35 @@ public static class IndicatorExtensions
|
|||||||
|
|
||||||
#pragma warning disable CA1416 // Validate platform compatibility
|
#pragma warning disable CA1416 // Validate platform compatibility
|
||||||
|
|
||||||
|
public static void PaintHLine(this Indicator indicator, PaintChartEventArgs args, double value, Pen pen)
|
||||||
|
{
|
||||||
|
if (indicator.CurrentChart == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Graphics gr = args.Graphics;
|
||||||
|
var mainWindow = indicator.CurrentChart.Windows[args.WindowIndex];
|
||||||
|
var converter = mainWindow.CoordinatesConverter;
|
||||||
|
var clientRect = mainWindow.ClientRectangle;
|
||||||
|
gr.SetClip(clientRect);
|
||||||
|
int leftX = clientRect.Left;
|
||||||
|
int rightX = clientRect.Right;
|
||||||
|
int Y = (int)converter.GetChartY(value);
|
||||||
|
using (pen)
|
||||||
|
{
|
||||||
|
gr.DrawLine(pen, new Point(leftX, Y), new Point(rightX, Y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void PaintSmoothCurve(this Indicator indicator, PaintChartEventArgs args, LineSeries series, int warmupPeriod, bool showColdValues = true, double tension = 0.2)
|
public static void PaintSmoothCurve(this Indicator indicator, PaintChartEventArgs args, LineSeries series, int warmupPeriod, bool showColdValues = true, double tension = 0.2)
|
||||||
{
|
{
|
||||||
if (!series.Visible || indicator.CurrentChart == null)
|
if (!series.Visible || indicator.CurrentChart == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Graphics gr = args.Graphics;
|
Graphics gr = args.Graphics;
|
||||||
var mainWindow = indicator.CurrentChart.MainWindow;
|
gr.SmoothingMode = SmoothingMode.AntiAlias;
|
||||||
|
var mainWindow = indicator.CurrentChart.Windows[args.WindowIndex];
|
||||||
var converter = mainWindow.CoordinatesConverter;
|
var converter = mainWindow.CoordinatesConverter;
|
||||||
|
|
||||||
var clientRect = mainWindow.ClientRectangle;
|
var clientRect = mainWindow.ClientRectangle;
|
||||||
|
|
||||||
gr.SetClip(clientRect);
|
gr.SetClip(clientRect);
|
||||||
|
|||||||
@@ -8,9 +8,12 @@ public class AtrIndicator : Indicator, IWatchlistIndicator
|
|||||||
[InputParameter("Periods", sortIndex: 1, 1, 2000, 1, 0)]
|
[InputParameter("Periods", sortIndex: 1, 1, 2000, 1, 0)]
|
||||||
public int Periods { get; set; } = 20;
|
public int Periods { get; set; } = 20;
|
||||||
|
|
||||||
|
[InputParameter("Show cold values", sortIndex: 21)]
|
||||||
|
public bool ShowColdValues { get; set; } = true;
|
||||||
|
|
||||||
private Atr? atr;
|
private Atr? atr;
|
||||||
protected LineSeries? AtrSeries;
|
protected LineSeries? AtrSeries;
|
||||||
public static int MinHistoryDepths => 2;
|
public int MinHistoryDepths => Math.Max(5, Periods * 2);
|
||||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||||
|
|
||||||
public AtrIndicator()
|
public AtrIndicator()
|
||||||
@@ -19,7 +22,7 @@ public class AtrIndicator : Indicator, IWatchlistIndicator
|
|||||||
Description = "Measures market volatility by calculating the average range between high and low prices.";
|
Description = "Measures market volatility by calculating the average range between high and low prices.";
|
||||||
SeparateWindow = true;
|
SeparateWindow = true;
|
||||||
|
|
||||||
AtrSeries = new("ATR", Color.Blue, 2, LineStyle.Solid);
|
AtrSeries = new($"ATR {Periods}", Color.Blue, 2, LineStyle.Solid);
|
||||||
AddLineSeries(AtrSeries);
|
AddLineSeries(AtrSeries);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +38,16 @@ public class AtrIndicator : Indicator, IWatchlistIndicator
|
|||||||
TValue result = atr!.Calc(input);
|
TValue result = atr!.Calc(input);
|
||||||
|
|
||||||
AtrSeries!.SetValue(result.Value);
|
AtrSeries!.SetValue(result.Value);
|
||||||
|
AtrSeries!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ShortName => $"ATR ({Periods})";
|
public override string ShortName => $"ATR ({Periods})";
|
||||||
|
|
||||||
|
public override void OnPaintChart(PaintChartEventArgs args)
|
||||||
|
{
|
||||||
|
base.OnPaintChart(args);
|
||||||
|
this.PaintHLine(args, 0.05, new Pen(Color.DarkRed, width: 2));
|
||||||
|
this.PaintSmoothCurve(args, AtrSeries!, atr!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Drawing2D;
|
||||||
|
using TradingPlatform.BusinessLayer;
|
||||||
|
|
||||||
|
namespace QuanTAlib;
|
||||||
|
|
||||||
|
public class FlowIndicator : Indicator, IWatchlistIndicator
|
||||||
|
{
|
||||||
|
protected string? SourceName;
|
||||||
|
public static int MinHistoryDepths => 2;
|
||||||
|
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||||
|
|
||||||
|
public FlowIndicator()
|
||||||
|
{
|
||||||
|
Name = "Flow Visualization";
|
||||||
|
SeparateWindow = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
// placeholder
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnUpdate(UpdateArgs args)
|
||||||
|
{
|
||||||
|
// placeholder
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning disable CA1416 // Validate platform compatibility
|
||||||
|
|
||||||
|
public override void OnPaintChart(PaintChartEventArgs args)
|
||||||
|
{
|
||||||
|
base.OnPaintChart(args);
|
||||||
|
Graphics gr = args.Graphics;
|
||||||
|
gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
|
||||||
|
var mainWindow = this.CurrentChart.Windows[args.WindowIndex];
|
||||||
|
var converter = mainWindow.CoordinatesConverter;
|
||||||
|
var clientRect = mainWindow.ClientRectangle;
|
||||||
|
gr.SetClip(clientRect);
|
||||||
|
DateTime leftTime = new[] { converter.GetTime(clientRect.Left), this.HistoricalData.Time(this!.Count - 1) }.Max();
|
||||||
|
DateTime rightTime = new[] { converter.GetTime(clientRect.Right), this.HistoricalData.Time(0) }.Min();
|
||||||
|
|
||||||
|
int leftIndex = (int)this.HistoricalData.GetIndexByTime(leftTime.Ticks) + 1;
|
||||||
|
int rightIndex = (int)this.HistoricalData.GetIndexByTime(rightTime.Ticks);
|
||||||
|
int width = this.CurrentChart.BarsWidth;
|
||||||
|
|
||||||
|
for (int i = rightIndex; i < leftIndex; i++)
|
||||||
|
{
|
||||||
|
int barX1 = (int)converter.GetChartX(this.HistoricalData.Time(i));
|
||||||
|
int barY1 = (int)converter.GetChartY(this.HistoricalData.Open(i));
|
||||||
|
int barYHigh = (int)converter.GetChartY(this.HistoricalData.High(i));
|
||||||
|
int barYLow = (int)converter.GetChartY(this.HistoricalData.Low(i));
|
||||||
|
int barX2 = barX1 + width;
|
||||||
|
int barY2 = (int)converter.GetChartY(this.HistoricalData.Close(i));
|
||||||
|
using (Brush transparentBrush = new SolidBrush(Color.FromArgb(250, 70, 70, 70)))
|
||||||
|
gr.FillRectangle(transparentBrush, barX1, barYHigh - 1, CurrentChart.BarsWidth, Math.Abs(barYLow - barYHigh) + 2);
|
||||||
|
using (Pen defaultPen = new(Color.Yellow, 3))
|
||||||
|
{
|
||||||
|
defaultPen.StartCap = LineCap.Round;
|
||||||
|
defaultPen.EndCap = LineCap.Round;
|
||||||
|
gr.DrawLine(defaultPen, barX1, barY1, barX2, barY2);
|
||||||
|
}
|
||||||
|
if (i > 0)
|
||||||
|
{
|
||||||
|
int barX0 = (int)converter.GetChartX(this.HistoricalData.Time(i - 1));
|
||||||
|
int barY0 = (int)converter.GetChartY(this.HistoricalData.Open(i - 1));
|
||||||
|
using (Pen dottedPen = new(Color.Yellow, 1))
|
||||||
|
{
|
||||||
|
dottedPen.DashStyle = DashStyle.Dot;
|
||||||
|
gr.DrawLine(dottedPen, barX2, barY2, barX0, barY0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -25,7 +25,8 @@ public class JbandsIndicator : Indicator, IWatchlistIndicator
|
|||||||
[InputParameter("vShort", sortIndex: 6, -100, 100, 1, 0)]
|
[InputParameter("vShort", sortIndex: 6, -100, 100, 1, 0)]
|
||||||
public int Phase { get; set; } = 10;
|
public int Phase { get; set; } = 10;
|
||||||
|
|
||||||
private Jma? jma;
|
private Jma? jmaUp;
|
||||||
|
private Jma? jmaLo;
|
||||||
protected LineSeries? UbSeries;
|
protected LineSeries? UbSeries;
|
||||||
protected LineSeries? LbSeries;
|
protected LineSeries? LbSeries;
|
||||||
protected string? SourceName;
|
protected string? SourceName;
|
||||||
@@ -46,18 +47,20 @@ public class JbandsIndicator : Indicator, IWatchlistIndicator
|
|||||||
|
|
||||||
protected override void OnInit()
|
protected override void OnInit()
|
||||||
{
|
{
|
||||||
jma = new(Periods, phase: Phase);
|
jmaUp = new(Periods, phase: Phase);
|
||||||
|
jmaLo = new(Periods, phase: Phase);
|
||||||
SourceName = Source.ToString();
|
SourceName = Source.ToString();
|
||||||
base.OnInit();
|
base.OnInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnUpdate(UpdateArgs args)
|
protected override void OnUpdate(UpdateArgs args)
|
||||||
{
|
{
|
||||||
TValue input = this.GetInputValue(args, Source);
|
TBar input = IndicatorExtensions.GetInputBar(this, args);
|
||||||
jma!.Calc(input);
|
jmaUp!.Calc(input.High);
|
||||||
|
jmaLo!.Calc(input.Low);
|
||||||
|
|
||||||
UbSeries!.SetValue(jma.UpperBand);
|
UbSeries!.SetValue(jmaUp.UpperBand);
|
||||||
LbSeries!.SetValue(jma.LowerBand);
|
LbSeries!.SetValue(jmaLo.LowerBand);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ShortName => $"JBands ({Periods}:{Phase})";
|
public override string ShortName => $"JBands ({Periods}:{Phase})";
|
||||||
|
|||||||
Reference in New Issue
Block a user