mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-02 11:37:42 +00:00
199 lines
6.5 KiB
Plaintext
199 lines
6.5 KiB
Plaintext
#!meta
|
|
|
|
{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}}
|
|
|
|
#!csharp
|
|
|
|
#r "..\src\obj\Debug\QuanTAlib.dll"
|
|
|
|
#r "nuget:Skender.Stock.Indicators"
|
|
|
|
using Skender.Stock.Indicators;
|
|
using QuanTAlib;
|
|
|
|
#!csharp
|
|
|
|
public class Htit : AbstractBase
|
|
{
|
|
private readonly int _period;
|
|
private readonly CircularBuffer _pr, _sp, _dt, _pd, _q1, _i1, _q2, _i2, _re, _im, _sd, _it;
|
|
|
|
public Htit(int period = 50) : base()
|
|
{
|
|
_period = period;
|
|
_pr = new CircularBuffer(period);
|
|
_sp = new CircularBuffer(period);
|
|
_dt = new CircularBuffer(period);
|
|
_pd = new CircularBuffer(period);
|
|
_q1 = new CircularBuffer(period);
|
|
_i1 = new CircularBuffer(period);
|
|
_q2 = new CircularBuffer(period);
|
|
_i2 = new CircularBuffer(period);
|
|
_re = new CircularBuffer(period);
|
|
_im = new CircularBuffer(period);
|
|
_sd = new CircularBuffer(period);
|
|
_it = new CircularBuffer(period);
|
|
Name = "Htit";
|
|
WarmupPeriod = 12; // Minimum required data points
|
|
Init();
|
|
}
|
|
|
|
public Htit(object source, int period = 50) : this(period)
|
|
{
|
|
var pubEvent = source.GetType().GetEvent("Pub");
|
|
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
|
}
|
|
|
|
public override void Init()
|
|
{
|
|
base.Init();
|
|
_pr.Clear();
|
|
_sp.Clear();
|
|
_dt.Clear();
|
|
_pd.Clear();
|
|
_q1.Clear();
|
|
_i1.Clear();
|
|
_q2.Clear();
|
|
_i2.Clear();
|
|
_re.Clear();
|
|
_im.Clear();
|
|
_sd.Clear();
|
|
_it.Clear();
|
|
}
|
|
|
|
protected override void ManageState(bool isNew)
|
|
{
|
|
if (isNew)
|
|
{
|
|
_index++;
|
|
}
|
|
}
|
|
|
|
protected override double GetLastValid()
|
|
{
|
|
return _it[^1];
|
|
}
|
|
|
|
protected override double Calculation()
|
|
{
|
|
ManageState(Input.IsNew);
|
|
_pr.Add(Input.Value, Input.IsNew);
|
|
|
|
if (_index > 6)
|
|
{
|
|
double adj = (0.075 * _pd[^2]) + 0.54;
|
|
|
|
// smooth and detrender
|
|
_sp.Add(((4 * _pr[^1]) + (3 * _pr[^2]) + (2 * _pr[^3]) + _pr[^4]) / 10, Input.IsNew);
|
|
_dt.Add(((0.0962 * _sp[^1]) + (0.5769 * _sp[^3]) - (0.5769 * _sp[^5]) - (0.0962 * _sp[^7])) * adj, Input.IsNew);
|
|
|
|
// in-phase and quadrature
|
|
_q1.Add(((0.0962 * _dt[^1]) + (0.5769 * _dt[^3]) - (0.5769 * _dt[^5]) - (0.0962 * _dt[^7])) * adj, Input.IsNew);
|
|
_i1.Add(_dt[^4], Input.IsNew);
|
|
|
|
// advance the phases by 90 degrees
|
|
double jI = ((0.0962 * _i1[^1]) + (0.5769 * _i1[^3]) - (0.5769 * _i1[^5]) - (0.0962 * _i1[^7])) * adj;
|
|
double jQ = ((0.0962 * _q1[^1]) + (0.5769 * _q1[^3]) - (0.5769 * _q1[^5]) - (0.0962 * _q1[^7])) * adj;
|
|
|
|
// phasor addition for 3-bar averaging
|
|
_i2.Add(_i1[^1] - jQ, Input.IsNew);
|
|
_q2.Add(_q1[^1] + jI, Input.IsNew);
|
|
|
|
_i2[^1] = (0.2 * _i2[^1]) + (0.8 * _i2[^2]); // smoothing it
|
|
_q2[^1] = (0.2 * _q2[^1]) + (0.8 * _q2[^2]);
|
|
|
|
// homodyne discriminator
|
|
_re.Add((_i2[^1] * _i2[^2]) + (_q2[^1] * _q2[^2]), Input.IsNew);
|
|
_im.Add((_i2[^1] * _q2[^2]) - (_q2[^1] * _i2[^2]), Input.IsNew);
|
|
|
|
_re[^1] = (0.2 * _re[^1]) + (0.8 * _re[^2]); // smoothing it
|
|
_im[^1] = (0.2 * _im[^1]) + (0.8 * _im[^2]);
|
|
|
|
// calculate period
|
|
_pd.Add(_im[^1] != 0 && _re[^1] != 0
|
|
? 2 * Math.PI / Math.Atan(_im[^1] / _re[^1])
|
|
: 0, Input.IsNew);
|
|
|
|
// adjust period to thresholds
|
|
_pd[^1] = (_pd[^1] > 1.5 * _pd[^2]) ? 1.5 * _pd[^2] : _pd[^1];
|
|
_pd[^1] = (_pd[^1] < 0.67 * _pd[^2]) ? 0.67 * _pd[^2] : _pd[^1];
|
|
_pd[^1] = (_pd[^1] < 6.0) ? 6.0 : _pd[^1];
|
|
_pd[^1] = (_pd[^1] > 50.0) ? 50.0 : _pd[^1];
|
|
|
|
// smooth the period
|
|
_pd[^1] = (0.2 * _pd[^1]) + (0.8 * _pd[^2]);
|
|
_sd.Add((0.33 * _pd[^1]) + (0.67 * _sd[^2]), Input.IsNew);
|
|
|
|
//check this loop
|
|
// smooth dominant cycle period
|
|
int dcPeriods = (int)(_sd[^1] + 0.5);
|
|
double sumPr = 0;
|
|
for (int d = 1; d < dcPeriods+1; d++) //0 -> 5
|
|
{
|
|
sumPr += _pr[^d];
|
|
}
|
|
_it.Add(dcPeriods > 0 ? sumPr / dcPeriods : _pr[^1], Input.IsNew);
|
|
|
|
|
|
Console.WriteLine($"{_index}\t {_it[^1]:F2}");
|
|
|
|
// final indicators
|
|
double Trendline, SmoothPrice;
|
|
|
|
Trendline = _index >= 12 // 12th bar
|
|
? ((4 * _it[^1]) + (3 * _it[^2]) + (2 * _it[^3]) + _it[^4]) / 10.0
|
|
: _pr[^1];
|
|
SmoothPrice = ((4 * _pr[^1]) + (3 * _pr[^2]) + (2 * _pr[^3]) + _pr[^4]) / 10.0;
|
|
|
|
Value = Trendline;
|
|
}
|
|
|
|
else
|
|
{
|
|
Value = _pr[^1];
|
|
_pd.Add(0, Input.IsNew);
|
|
_sp.Add(0, Input.IsNew);
|
|
_dt.Add(0, Input.IsNew);
|
|
_i1.Add(0, Input.IsNew);
|
|
_q1.Add(0, Input.IsNew);
|
|
_i2.Add(0, Input.IsNew);
|
|
_q2.Add(0, Input.IsNew);
|
|
_re.Add(0, Input.IsNew);
|
|
_im.Add(0, Input.IsNew);
|
|
_sd.Add(0, Input.IsNew);
|
|
_it.Add(_pr[^1], Input.IsNew);
|
|
}
|
|
|
|
IsHot = _index >= WarmupPeriod;
|
|
return Value;
|
|
}
|
|
}
|
|
|
|
#!csharp
|
|
|
|
Random rnd = new((int)DateTime.Now.Ticks);
|
|
GbmFeed feed = new(sigma: 0.5, mu: 0.0);
|
|
|
|
TBarSeries bars = new(feed);
|
|
feed.Add(15);
|
|
|
|
IEnumerable<Quote> quotes = feed.Select(q => new Quote {
|
|
Date = q.Time,
|
|
Open = (decimal)q.Open,
|
|
High = (decimal)q.High,
|
|
Low = (decimal)q.Low,
|
|
Close = (decimal)q.Close,
|
|
Volume = (decimal)q.Volume
|
|
});
|
|
|
|
Htit ma = new();
|
|
TSeries QL = new();
|
|
foreach (TBar item in feed) { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); }
|
|
var SK = quotes.Select(q => (q.Date, (double)q.Close)).GetHtTrendline().Select(i => i.Trendline.Null2NaN()!);
|
|
|
|
Console.WriteLine($"Data\tSkend\tQuanTAlib");
|
|
for (int i = 8; i < feed.Length; i++)
|
|
{
|
|
Console.WriteLine($"{i}\t{feed[i].Close,6:F2}\t{SK.ElementAt(i),6:F2}\t{QL[i].Value,6:F2} {Math.Truncate(SK.ElementAt(i)*100)==Math.Truncate(QL[i].Value*100)}");
|
|
}
|