mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-05 20:47:43 +00:00
Bollinger Bands® with validation tests
This commit is contained in:
@@ -158,7 +158,7 @@ public abstract class Single_TBars_Indicator : TSeries
|
||||
for (int i = 0; i < data.Count; i++) { base.Add(TValue: data[i], update: false); }
|
||||
}
|
||||
|
||||
public new void Add((System.DateTime t, double o, double h, double l, double c, double v) TBar)
|
||||
public void Add((System.DateTime t, double o, double h, double l, double c, double v) TBar)
|
||||
=> this.Add(TBar: TBar, update: false);
|
||||
public void Add(bool update)
|
||||
=> this.Add(TBar: this._bars[this._bars.Count - 1], update: update);
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
BBANDS: Bollinger Bands®
|
||||
Price channels created by John Bollinger, depict volatility as standard deviation boundary
|
||||
line range from a moving average of price. The bands automatically widen when volatility
|
||||
increases and contract when volatility decreases. Their dynamic nature allows them to be
|
||||
used on different securities with the standard settings.
|
||||
|
||||
Mid Band = simple moving average (SMA)
|
||||
Upper Band = SMA + (standard deviation of price x multiplier)
|
||||
Lower Band = SMA - (standard deviation of price x multiplier)
|
||||
Bandwidth = Width of the channel: (Upper-Lower)/SMA
|
||||
%B = The location of the data point within the channel: (Price-Lower)/(Upper/Lower)
|
||||
Z-Score = number of standard deviations of the data point from SMA
|
||||
|
||||
Sources:
|
||||
https://www.investopedia.com/terms/b/bollingerbands.asp
|
||||
https://school.stockcharts.com/doku.php?id=technical_indicators:bollinger_bands
|
||||
|
||||
Note:
|
||||
Bollinger Bands® is a registered trademark of John A. Bollinger.
|
||||
|
||||
</summary> */
|
||||
|
||||
public class BBANDS_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public SMA_Series Mid { get; }
|
||||
public ADD_Series Upper { get; }
|
||||
public SUB_Series Lower { get; }
|
||||
public DIV_Series PercentB { get; }
|
||||
public DIV_Series Bandwidth { get; }
|
||||
public DIV_Series Zscore { get; }
|
||||
|
||||
private readonly SDEV_Series _sdev;
|
||||
private readonly MUL_Series _mulsdev;
|
||||
private readonly SUB_Series _pbdnd;
|
||||
private readonly SUB_Series _pbdvr;
|
||||
private readonly SUB_Series _zdnd;
|
||||
|
||||
public BBANDS_Series(TSeries source, int period = 26, double multiplier = 2.0, bool useNaN = false)
|
||||
: base(source, period: 0, useNaN)
|
||||
{
|
||||
this.Mid = new(source: source, period: period, useNaN: useNaN);
|
||||
|
||||
_sdev = new(source, period, useNaN: useNaN);
|
||||
_mulsdev = new(_sdev, multiplier);
|
||||
this.Upper = new(Mid, _mulsdev);
|
||||
this.Lower = new(Mid, _mulsdev);
|
||||
|
||||
_pbdnd = new(source, Lower);
|
||||
_pbdvr = new(Upper, Lower);
|
||||
|
||||
this.PercentB = new(_pbdnd, _pbdvr);
|
||||
this.Bandwidth = new(_pbdvr, Mid);
|
||||
|
||||
_zdnd = new(source, Mid);
|
||||
this.Zscore = new(_zdnd, _sdev);
|
||||
|
||||
if (source.Count > 0)
|
||||
{ base.Add(this.Bandwidth); }
|
||||
}
|
||||
public override void Add((System.DateTime t, double v) TValue, bool update)
|
||||
{
|
||||
double _bbandwidth;
|
||||
if (update)
|
||||
{ _sdev.Add(TValue, true); }
|
||||
_bbandwidth = this.Bandwidth[(this.Count < this.Bandwidth.Count) ? this.Count : this.Bandwidth.Count - 1].v;
|
||||
var result = (TValue.t, _bbandwidth);
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Version>0.1.15</Version>
|
||||
<Version>0.1.16</Version>
|
||||
<releaseNotes>
|
||||
</releaseNotes>
|
||||
<Title>QuanTAlib</Title>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
using Xunit;
|
||||
using System;
|
||||
using QuanTAlib;
|
||||
|
||||
namespace MovingAvg;
|
||||
public class BBANDS_Test
|
||||
{
|
||||
[Fact]
|
||||
public void Add_Test()
|
||||
{
|
||||
TSeries a = new() { 0, 1, 2, 3, 4, 5 };
|
||||
BBANDS_Series c = new(a, 4,2.5);
|
||||
Assert.Equal(6, c.Count);
|
||||
a.Add(5);
|
||||
Assert.Equal(a.Count, c.Count);
|
||||
Assert.Equal(a.Count, c.Mid.Count);
|
||||
Assert.Equal(a.Count, c.Upper.Count);
|
||||
Assert.Equal(a.Count, c.Lower.Count);
|
||||
Assert.Equal(a.Count, c.PercentB.Count);
|
||||
Assert.Equal(a.Count, c.Zscore.Count);
|
||||
Assert.Equal(a.Count, c.Bandwidth.Count);
|
||||
|
||||
a.Add(0, update: true);
|
||||
Assert.Equal(a.Count, c.Count);
|
||||
Assert.Equal(a.Count, c.Mid.Count);
|
||||
Assert.Equal(a.Count, c.Upper.Count);
|
||||
Assert.Equal(a.Count, c.Lower.Count);
|
||||
Assert.Equal(a.Count, c.PercentB.Count);
|
||||
Assert.Equal(a.Count, c.Zscore.Count);
|
||||
Assert.Equal(a.Count, c.Bandwidth.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Edge_Test()
|
||||
{
|
||||
TSeries a = new() { double.NaN, double.Epsilon, double.PositiveInfinity, double.MaxValue };
|
||||
BBANDS_Series c = new(a, 4, 2.5);
|
||||
Assert.Equal(a.Count, c.Count);
|
||||
a.Add(double.NaN);
|
||||
Assert.Equal(a.Count, c.Count);
|
||||
Assert.Equal(a.Count, c.Mid.Count);
|
||||
Assert.Equal(a.Count, c.Upper.Count);
|
||||
Assert.Equal(a.Count, c.Lower.Count);
|
||||
Assert.Equal(a.Count, c.PercentB.Count);
|
||||
Assert.Equal(a.Count, c.Zscore.Count);
|
||||
Assert.Equal(a.Count, c.Bandwidth.Count);
|
||||
a.Add(double.PositiveInfinity);
|
||||
Assert.Equal(a.Count, c.Count);
|
||||
Assert.Equal(a.Count, c.Mid.Count);
|
||||
Assert.Equal(a.Count, c.Upper.Count);
|
||||
Assert.Equal(a.Count, c.Lower.Count);
|
||||
Assert.Equal(a.Count, c.PercentB.Count);
|
||||
Assert.Equal(a.Count, c.Zscore.Count);
|
||||
Assert.Equal(a.Count, c.Bandwidth.Count);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -160,9 +160,25 @@ public class Skender_Stock
|
||||
var SK = this.quotes.GetMacd(12,26,9);
|
||||
|
||||
Assert.Equal(Math.Round((double)SK.Last().Macd!, 8), Math.Round(QL.Last().v, 8));
|
||||
Assert.Equal(Math.Round((double)SK.Last().Signal!, 8), Math.Round(QL.Signal.Last().v, 8));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BBANDS()
|
||||
{
|
||||
BBANDS_Series QL = new(this.bars.Close, this.period, 2.0, useNaN: false);
|
||||
var SK = this.quotes.GetBollingerBands(this.period, 2.0);
|
||||
|
||||
Assert.Equal(Math.Round((double)SK.Last().Sma!, 8), Math.Round(QL.Mid.Last().v, 8));
|
||||
Assert.Equal(Math.Round((double)SK.Last().UpperBand!, 8), Math.Round(QL.Upper.Last().v, 8));
|
||||
Assert.Equal(Math.Round((double)SK.Last().LowerBand!, 8), Math.Round(QL.Lower.Last().v, 8));
|
||||
Assert.Equal(Math.Round((double)SK.Last().Width!, 8), Math.Round(QL.Bandwidth.Last().v, 8));
|
||||
Assert.Equal(Math.Round((double)SK.Last().PercentB!, 8), Math.Round(QL.PercentB.Last().v, 8));
|
||||
Assert.Equal(Math.Round((double)SK.Last().ZScore!, 8), Math.Round(QL.Zscore.Last().v, 8));
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void RSI()
|
||||
{
|
||||
RSI_Series QL = new(this.bars.Close, this.period, useNaN: false);
|
||||
|
||||
@@ -164,8 +164,25 @@ public class TA_LIB
|
||||
MACD_Series QL = new(this.bars.Close, slow: 26, fast: 12, signal: 9, false);
|
||||
Core.Macd(this.inclose, 0, this.bars.Count - 1, outMacd: this.TALIB, outMacdSignal: macdSignal, outMacdHist: macdHist, out int outBegIdx, out _);
|
||||
Assert.Equal(Math.Round(this.TALIB[this.TALIB.Length - outBegIdx - 1], 8), Math.Round(QL.Last().v, 8));
|
||||
Assert.Equal(Math.Round(macdSignal[macdSignal.Length - outBegIdx - 1], 8), Math.Round(QL.Signal.Last().v, 8));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BBANDS()
|
||||
{
|
||||
double[] outMiddle = new double[this.bars.Count];
|
||||
double[] outUpper = new double[this.bars.Count];
|
||||
double[] outLower = new double[this.bars.Count];
|
||||
BBANDS_Series QL = new(this.bars.Close, period:26, multiplier:2.0, false);
|
||||
Core.Bbands(this.inclose, 0, this.bars.Count - 1, outRealUpperBand: outUpper, outRealMiddleBand: outMiddle, outRealLowerBand: outLower, out int outBegIdx, out _, optInTimePeriod:26, optInNbDevUp:2.0, optInNbDevDn:2.0);
|
||||
Assert.Equal(Math.Round(outUpper[outUpper.Length - outBegIdx - 1], 8), Math.Round(QL.Upper.Last().v, 8));
|
||||
Assert.Equal(Math.Round(outMiddle[outMiddle.Length - outBegIdx - 1], 8), Math.Round(QL.Mid.Last().v, 8));
|
||||
Assert.Equal(Math.Round(outLower[outLower.Length - outBegIdx - 1], 8), Math.Round(QL.Lower.Last().v, 8));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Fact]
|
||||
public void HL2()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user