Bband, Ccv, Ce, Cv, Cvi, Ewma, Fcb, Gkv, Hlv

This commit is contained in:
Miha Kralj
2024-11-03 10:47:10 -08:00
parent 4b5724d5bc
commit caa0d31dbe
22 changed files with 1595 additions and 129 deletions
+4 -1
View File
@@ -8,7 +8,6 @@
<Deterministic>true</Deterministic>
<NeutralLanguage>en-US</NeutralLanguage>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<DisableImplicitNamespaceImports>true</DisableImplicitNamespaceImports>
<PlatformTarget>AnyCPU</PlatformTarget>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
@@ -21,6 +20,9 @@
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<PlatformTarget>AnyCPU</PlatformTarget>
<IsLocalBuild Condition="'$(GITHUB_ACTIONS)' == ''">true</IsLocalBuild>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<UpdateAssemblyInfo>true</UpdateAssemblyInfo>
<GenerateGitVersionInformation>true</GenerateGitVersionInformation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
@@ -49,6 +51,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All"/>
<PackageReference Include="Microsoft.DotNet.Interactive.Formatting" Version="1.0.0-beta.21459.1" />
<PackageReference Include="GitVersion.MsBuild" Version="6.0.0-beta.7" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup Condition="'$(IsLocalBuild)' == 'true' AND $([MSBuild]::IsOSPlatform('Windows'))">
+25
View File
@@ -17,6 +17,15 @@ public class StatisticsUpdateTests
return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200 - 100; // Range: -100 to 100
}
private TBar GetRandomBar(bool IsNew)
{
double open = GetRandomDouble();
double high = open + Math.Abs(GetRandomDouble());
double low = open - Math.Abs(GetRandomDouble());
double close = low + (high - low) * GetRandomDouble();
return new TBar(DateTime.Now, open, high, low, close, 1000, IsNew);
}
[Fact]
public void Curvature_Update()
{
@@ -47,6 +56,22 @@ public class StatisticsUpdateTests
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Hurst_Update()
{
var indicator = new Hurst(period: 100, minLength: 10);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Kurtosis_Update()
{
+144 -16
View File
@@ -90,6 +90,134 @@ public class VolatilityUpdateTests
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Bband_Update()
{
var indicator = new Bband(period: 20, multiplier: 2.0);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Ccv_Update()
{
var indicator = new Ccv(period: 20);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Ce_Update()
{
var indicator = new Ce(period: 22, multiplier: 3.0);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Cv_Update()
{
var indicator = new Cv(period: 20);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Cvi_Update()
{
var indicator = new Cvi(period: 10, smoothPeriod: 10);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Ewma_Update()
{
var indicator = new Ewma(period: 20, lambda: 0.94);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Fcb_Update()
{
var indicator = new Fcb(period: 20, smoothing: 0.5);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Gkv_Update()
{
var indicator = new Gkv(period: 20);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Historical_Update()
{
@@ -105,6 +233,22 @@ public class VolatilityUpdateTests
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Hlv_Update()
{
var indicator = new Hlv(period: 20);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Jvolty_Update()
{
@@ -150,22 +294,6 @@ public class VolatilityUpdateTests
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Cvi_Update()
{
var indicator = new Cvi(period: 14);
TBar r = GetRandomBar(true);
double initialValue = indicator.Calc(r);
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Tr_Update()
{
+16 -14
View File
@@ -1,16 +1,18 @@
# Indicators in QuanTAlib
| **Category** | **Status** | **Completion** |
|--------------|:----------:|:--------------:|
| Basic Transforms | 6 of 6 | 100% |
| Averages & Trends | 33 of 33 | 100% |
| Momentum | 17 of 17 | 100% |
| Oscillators | 12 of 29 | 41% |
| Volatility | 15 of 35 | 43% |
| Volume | 19 of 19 | 100% |
| Numerical Analysis | 13 of 20 | 65% |
| Oscillators | 11 of 29 | 38% |
| Volatility | 24 of 35 | 69% |
| Volume | 15 of 19 | 79% |
| Numerical Analysis | 13 of 19 | 68% |
| Errors | 16 of 16 | 100% |
| **Total** | **131 of 175** | **75%** |
| **Total** | **135 of 174** | **78%** |
|Technical Indicator Name| Class Name|
|-----------|:----------:|
@@ -109,16 +111,16 @@
|ATR - Average True Range|`Atr`|
|ATRP - Average True Range Percent|`Atrp`|
|ATRS - ATR Trailing Stop|`Atrs`|
|🚧 BB* - Bollinger Bands® (Upper, Middle, Lower)|`Bb`|
|🚧 CCV - Close-to-Close Volatility|`Ccv`|
|🚧 CE - Chandelier Exit|`Ce`|
|🚧 CV - Conditional Volatility (ARCH/GARCH)|`Cv`|
|🚧 CVI - Chaikin's Volatility|`Cvi`|
|BB* - Bollinger Bands® (Upper, Middle, Lower)|`Bb`|
|CCV - Close-to-Close Volatility|`Ccv`|
|CE - Chandelier Exit|`Ce`|
|CV - Conditional Volatility (ARCH/GARCH)|`Cv`|
|CVI - Chaikin's Volatility|`Cvi`|
|🚧 DC* - Donchian Channels (Upper, Middle, Lower)|`Dc`|
|🚧 EWMA - Exponential Weighted Moving Average Volatility|`Ewma`|
|🚧 FCB - Fractal Chaos Bands|`Fcb`|
|🚧 GKV - Garman-Klass Volatility|`Gkv`|
|🚧 HLV - High-Low Volatility|`Hlv`|
|EWMA - Exponential Weighted Moving Average Volatility|`Ewma`|
|FCB - Fractal Chaos Bands|`Fcb`|
|GKV - Garman-Klass Volatility|`Gkv`|
|HLV - High-Low Volatility|`Hlv`|
|HV - Historical Volatility|`Hv`|
|🚧 ICH* - Ichimoku Cloud (Conversion, Base, Leading Span A, Leading Span B, Lagging Span)|`Ich`|
|JVOLTY - Jurik Volatility|`Jvolty`|
+1 -10
View File
@@ -1,36 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Title>QuanTAlib</Title>
<Product>Library of TA Calculations, Charts and Strategies for Quantower</Product>
<Description>Quantitative Technical Analysis Library in C# for Quantower</Description>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/mihakralj/QuanTAlib</RepositoryUrl>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<Authors>Miha Kralj</Authors>
<Copyright>Miha Kralj</Copyright>
<PackageReadmeFile>readme.md</PackageReadmeFile>
<RootNamespace>QuanTAlib</RootNamespace>
<AssemblyName>QuanTAlib</AssemblyName>
<AssemblyVersion>0.0.0.1</AssemblyVersion>
<IsPublishable>True</IsPublishable>
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>full</DebugType>
<ProduceReferenceAssembly>True</ProduceReferenceAssembly>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageIcon>QuanTAlib2.png</PackageIcon>
<PackageReadmeFile>readme.md</PackageReadmeFile>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageTags>
Indicators;Stock;Market;Technical;Analysis;Algorithmic;Trading;Trade;Trend;Momentum;Finance;Algorithm;Algo;
AlgoTrading;Financial;Strategy;Chart;Charting;Oscillator;Overlay;Equity;Bitcoin;Crypto;Cryptocurrency;Forex;
Quantitative;Historical;Quotes;
</PackageTags>
<PackageIcon>QuanTAlib2.png</PackageIcon>
<PackageIconUrl>https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png</PackageIconUrl>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<NoWarn>$(NoWarn);NU1903;NU5104</NoWarn>
</PropertyGroup>
<ItemGroup>
@@ -51,4 +42,4 @@
</None>
</ItemGroup>
</Project>
</Project>
+194
View File
@@ -0,0 +1,194 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// HURST: Hurst Exponent
/// A measure of long-term memory of time series that relates to the
/// autocorrelations of the time series, and the rate at which these
/// decrease as the lag between pairs of values increases.
/// </summary>
/// <remarks>
/// The Hurst Exponent calculation process:
/// 1. Calculate log returns of the series
/// 2. Create subsequences of different lengths
/// 3. For each length:
/// - Calculate range (max-min) of cumulative deviations
/// - Calculate standard deviation
/// - Calculate R/S ratio
/// 4. Fit log(R/S) vs log(length) to find H
///
/// Key characteristics:
/// - H = 0.5: Random walk (Brownian motion)
/// - 0.5 < H ≤ 1.0: Trending (persistent) series
/// - 0 ≤ H < 0.5: Mean-reverting (anti-persistent) series
/// - Default minimum length is 10
/// - Default maximum length is period/2
///
/// Formula:
/// R(n)/S(n) = c * n^H
/// where:
/// R(n) = range of cumulative deviations
/// S(n) = standard deviation
/// n = subsequence length
/// H = Hurst exponent
///
/// Market Applications:
/// - Market efficiency analysis
/// - Trend strength measurement
/// - Trading strategy development
/// - Risk assessment
/// - Market regime identification
///
/// Sources:
/// H.E. Hurst (1951)
/// "Long-term Storage Capacity of Reservoirs"
/// Transactions of the American Society of Civil Engineers, 116, 770-799
///
/// Note: Returns a value between 0 and 1
/// </remarks>
[SkipLocalsInit]
public sealed class Hurst : AbstractBase
{
private readonly int _period;
private readonly int _minLength;
private readonly CircularBuffer _prices;
private readonly CircularBuffer _logReturns;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Hurst(int period = 100, int minLength = 10)
{
if (minLength < 10)
{
throw new ArgumentOutOfRangeException(nameof(minLength), "Minimum length must be at least 10.");
}
if (period <= minLength * 2)
{
throw new ArgumentOutOfRangeException(nameof(period), "Period must be at least twice the minimum length.");
}
_period = period;
_minLength = minLength;
WarmupPeriod = period + 1; // Need one extra period for returns
Name = $"HURST({_period})";
_prices = new CircularBuffer(period);
_logReturns = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Hurst(object source, int period = 100, int minLength = 10) : this(period, minLength)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prices.Clear();
_logReturns.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static (double range, double stdDev) CalculateRangeAndStdDev(ReadOnlySpan<double> data)
{
int n = data.Length;
if (n == 0) return (0, 0);
// Calculate mean
double mean = 0;
for (int i = 0; i < n; i++)
{
mean += data[i];
}
mean /= n;
// Calculate cumulative deviations and std dev
double max = double.MinValue;
double min = double.MaxValue;
double sumSquaredDev = 0;
double cumDev = 0;
for (int i = 0; i < n; i++)
{
double dev = data[i] - mean;
cumDev += dev;
max = Math.Max(max, cumDev);
min = Math.Min(min, cumDev);
sumSquaredDev += dev * dev;
}
double range = max - min;
double stdDev = Math.Sqrt(sumSquaredDev / n);
return (range, stdDev);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Add price and calculate log return
_prices.Add(BarInput.Close);
if (_index > 1)
{
double logReturn = Math.Log(BarInput.Close / _prices[1]);
_logReturns.Add(logReturn);
}
// Need enough values for calculation
if (_index <= _period)
{
return 0.5; // Return random walk value until we have enough data
}
// Calculate R/S values for different lengths
int maxLength = _period / 2;
int numPoints = 0;
double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
for (int length = _minLength; length <= maxLength; length *= 2)
{
var (range, stdDev) = CalculateRangeAndStdDev(_logReturns.GetSpan()[..length]);
if (stdDev > 0)
{
double rs = range / stdDev;
if (rs > 0)
{
double x = Math.Log(length);
double y = Math.Log(rs);
sumX += x;
sumY += y;
sumXY += x * y;
sumX2 += x * x;
numPoints++;
}
}
}
// Calculate Hurst exponent using linear regression
double hurst = 0.5; // Default to random walk
if (numPoints > 1)
{
double slope = (numPoints * sumXY - sumX * sumY) / (numPoints * sumX2 - sumX * sumX);
hurst = Math.Max(0, Math.Min(1, slope)); // Clamp between 0 and 1
}
IsHot = _index >= WarmupPeriod;
return hurst;
}
}
+143
View File
@@ -0,0 +1,143 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// BBAND: Bollinger Bands®
/// A technical analysis tool that creates a band of three lines:
/// - Middle Band: n-period simple moving average (SMA)
/// - Upper Band: Middle Band + (standard deviation * multiplier)
/// - Lower Band: Middle Band - (standard deviation * multiplier)
/// </summary>
/// <remarks>
/// The Bollinger Bands calculation process:
/// 1. Calculate the middle band (SMA of closing prices)
/// 2. Calculate the standard deviation of prices
/// 3. Upper and lower bands are the middle band +/- standard deviation * multiplier
///
/// Key characteristics:
/// - Adapts to volatility
/// - Default period is 20 days
/// - Default multiplier is 2.0
/// - Returns three bands (upper, middle, lower)
/// - Wider bands indicate higher volatility
/// - Narrower bands indicate lower volatility
///
/// Formula:
/// Middle Band = SMA(Close, period)
/// Standard Deviation = SQRT(SUM((Close - Middle Band)^2) / period)
/// Upper Band = Middle Band + (multiplier * Standard Deviation)
/// Lower Band = Middle Band - (multiplier * Standard Deviation)
///
/// Market Applications:
/// - Volatility measurement
/// - Overbought/oversold identification
/// - Price breakout detection
/// - Trend strength analysis
/// - Dynamic support/resistance levels
///
/// Sources:
/// John Bollinger (1980s)
/// https://www.bollingerbands.com
///
/// Note: Returns three values: upper, middle, and lower bands
/// </remarks>
[SkipLocalsInit]
public sealed class Bband : AbstractBase
{
private readonly int _period;
private readonly double _multiplier;
private readonly CircularBuffer _prices;
private double _middleBand;
private double _upperBand;
private double _lowerBand;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Bband(int period = 20, double multiplier = 2.0)
{
_period = period;
_multiplier = multiplier;
WarmupPeriod = period;
Name = $"BBAND({_period},{_multiplier})";
_prices = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Bband(object source, int period = 20, double multiplier = 2.0) : this(period, multiplier)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_middleBand = 0;
_upperBand = 0;
_lowerBand = 0;
_prices.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Add current price to buffer
_prices.Add(BarInput.Close);
// Need enough values for calculation
if (_index <= _period)
{
return 0;
}
// Calculate middle band (SMA)
_middleBand = _prices.Average();
// Calculate standard deviation
double sumSquaredDeviations = 0;
for (int i = 0; i < _period; i++)
{
double deviation = _prices[i] - _middleBand;
sumSquaredDeviations += deviation * deviation;
}
double standardDeviation = Math.Sqrt(sumSquaredDeviations / _period);
// Calculate bands
double bandWidth = _multiplier * standardDeviation;
_upperBand = _middleBand + bandWidth;
_lowerBand = _middleBand - bandWidth;
IsHot = _index >= WarmupPeriod;
return _middleBand; // Return middle band as primary value
}
/// <summary>
/// Gets the upper band value
/// </summary>
public double UpperBand => _upperBand;
/// <summary>
/// Gets the middle band value (SMA)
/// </summary>
public double MiddleBand => _middleBand;
/// <summary>
/// Gets the lower band value
/// </summary>
public double LowerBand => _lowerBand;
}
+130
View File
@@ -0,0 +1,130 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// CCV: Close-to-Close Volatility
/// A measure of price volatility that uses only closing prices,
/// calculated as the standard deviation of logarithmic returns.
/// </summary>
/// <remarks>
/// The CCV calculation process:
/// 1. Calculate logarithmic returns: ln(Close[t]/Close[t-1])
/// 2. Calculate standard deviation of returns over the period
/// 3. Annualize by multiplying by sqrt(trading days per year)
///
/// Key characteristics:
/// - Uses only closing prices
/// - Based on logarithmic returns
/// - Default period is 20 days
/// - Annualized by default (multiply by sqrt(252))
/// - Expressed as a percentage
///
/// Formula:
/// Returns = ln(Close[t]/Close[t-1])
/// CCV = StdDev(Returns, period) * sqrt(252) * 100
///
/// Market Applications:
/// - Volatility measurement
/// - Risk assessment
/// - Option pricing
/// - Trading strategy development
/// - Portfolio management
///
/// Sources:
/// Close-to-Close Volatility concept
/// https://www.investopedia.com/terms/v/volatility.asp
///
/// Note: Returns annualized volatility as a percentage
/// </remarks>
[SkipLocalsInit]
public sealed class Ccv : AbstractBase
{
private readonly int _period;
private readonly bool _annualize;
private readonly CircularBuffer _returns;
private double _prevClose;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ccv(int period = 20, bool annualize = true)
{
_period = period;
_annualize = annualize;
WarmupPeriod = period + 1; // Need one extra period for returns calculation
Name = $"CCV({_period})";
_returns = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ccv(object source, int period = 20, bool annualize = true) : this(period, annualize)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_returns.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate logarithmic return
double logReturn = Math.Log(BarInput.Close / _prevClose);
_returns.Add(logReturn);
_prevClose = BarInput.Close;
// Need enough values for calculation
if (_index <= _period)
{
return 0;
}
// Calculate standard deviation
double mean = _returns.Average();
double sumSquaredDeviations = 0;
for (int i = 0; i < _period; i++)
{
double deviation = _returns[i] - mean;
sumSquaredDeviations += deviation * deviation;
}
double stdDev = Math.Sqrt(sumSquaredDeviations / _period);
// Annualize if requested (sqrt(252) for trading days in a year)
if (_annualize)
{
stdDev *= Math.Sqrt(252);
}
// Convert to percentage
double volatility = stdDev * 100;
IsHot = _index >= WarmupPeriod;
return volatility;
}
}
+157
View File
@@ -0,0 +1,157 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// CE: Chandelier Exit
/// A volatility-based stop-loss indicator that adapts to market conditions,
/// using ATR to set stop levels above/below recent price extremes.
/// </summary>
/// <remarks>
/// The CE calculation process:
/// 1. Calculate highest high and lowest low over the period
/// 2. Calculate ATR over the period
/// 3. Long Exit = Highest High - (ATR * multiplier)
/// 4. Short Exit = Lowest Low + (ATR * multiplier)
///
/// Key characteristics:
/// - Adapts to market volatility
/// - Default period is 22 days
/// - Default multiplier is 3.0
/// - Returns both long and short exit levels
/// - Based on ATR and price extremes
///
/// Formula:
/// ATR = Average(TR, period)
/// Long Exit = Highest High[period] - (multiplier * ATR)
/// Short Exit = Lowest Low[period] + (multiplier * ATR)
///
/// Market Applications:
/// - Stop loss placement
/// - Position management
/// - Trend following
/// - Risk control
/// - Exit strategy
///
/// Sources:
/// Chuck LeBeau
/// https://www.investopedia.com/terms/c/chandelier-exit.asp
///
/// Note: Returns two values: long exit and short exit levels
/// </remarks>
[SkipLocalsInit]
public sealed class Ce : AbstractBase
{
private readonly int _period;
private readonly double _multiplier;
private readonly CircularBuffer _tr;
private readonly CircularBuffer _highs;
private readonly CircularBuffer _lows;
private double _prevClose;
private double _longExit;
private double _shortExit;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ce(int period = 22, double multiplier = 3.0)
{
_period = period;
_multiplier = multiplier;
WarmupPeriod = period + 1; // Need one extra period for TR
Name = $"CE({_period},{_multiplier})";
_tr = new CircularBuffer(period);
_highs = new CircularBuffer(period);
_lows = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ce(object source, int period = 22, double multiplier = 3.0) : this(period, multiplier)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_longExit = 0;
_shortExit = 0;
_tr.Clear();
_highs.Clear();
_lows.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate True Range
double tr = Math.Max(BarInput.High - BarInput.Low,
Math.Max(Math.Abs(BarInput.High - _prevClose),
Math.Abs(BarInput.Low - _prevClose)));
// Add values to buffers
_tr.Add(tr);
_highs.Add(BarInput.High);
_lows.Add(BarInput.Low);
// Store current close for next calculation
_prevClose = BarInput.Close;
// Need enough values for calculation
if (_index <= _period)
{
return 0;
}
// Calculate ATR
double atr = _tr.Average();
// Find highest high and lowest low
double highestHigh = double.MinValue;
double lowestLow = double.MaxValue;
for (int i = 0; i < _period; i++)
{
highestHigh = Math.Max(highestHigh, _highs[i]);
lowestLow = Math.Min(lowestLow, _lows[i]);
}
// Calculate exit levels
_longExit = highestHigh - (_multiplier * atr);
_shortExit = lowestLow + (_multiplier * atr);
IsHot = _index >= WarmupPeriod;
return _longExit; // Return long exit as primary value
}
/// <summary>
/// Gets the long exit level
/// </summary>
public double LongExit => _longExit;
/// <summary>
/// Gets the short exit level
/// </summary>
public double ShortExit => _shortExit;
}
+140
View File
@@ -0,0 +1,140 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// CV: Conditional Volatility (GARCH)
/// Implements the GARCH(1,1) model for estimating conditional volatility,
/// which captures volatility clustering and mean reversion in financial markets.
/// </summary>
/// <remarks>
/// The CV (GARCH) calculation process:
/// 1. Calculate returns: (Close[t] - Close[t-1])/Close[t-1]
/// 2. Update variance estimate using GARCH(1,1) formula:
/// σ²[t] = ω + α*r²[t-1] + β*σ²[t-1]
/// 3. Take square root to get volatility
///
/// Key characteristics:
/// - Captures volatility clustering
/// - Mean-reverting behavior
/// - Responds to market shocks
/// - Default period is 20 days
/// - Returns annualized volatility
///
/// Formula:
/// Returns[t] = (Close[t] - Close[t-1])/Close[t-1]
/// σ²[t] = ω + α*Returns²[t-1] + β*σ²[t-1]
/// CV[t] = sqrt(σ²[t]) * sqrt(252) * 100
///
/// Where:
/// ω (omega) = long-term variance * (1 - α - β)
/// α (alpha) = weight of recent squared return
/// β (beta) = weight of previous variance
///
/// Market Applications:
/// - Risk measurement
/// - Option pricing
/// - Value at Risk (VaR)
/// - Portfolio optimization
/// - Volatility forecasting
///
/// Sources:
/// Bollerslev (1986)
/// https://en.wikipedia.org/wiki/GARCH
///
/// Note: Returns annualized volatility as a percentage
/// </remarks>
[SkipLocalsInit]
public sealed class Cv : AbstractBase
{
private readonly int _period;
private readonly double _alpha;
private readonly double _beta;
private readonly double _omega;
private double _prevClose;
private double _prevVariance;
private double _longTermVariance;
private bool _isInitialized;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cv(int period = 20, double alpha = 0.1, double beta = 0.8)
{
_period = period;
_alpha = alpha;
_beta = beta;
_omega = 0.001 * (1 - alpha - beta); // Initial estimate, will be updated with actual data
WarmupPeriod = period + 1; // Need one extra period for returns
Name = $"CV({_period})";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cv(object source, int period = 20, double alpha = 0.1, double beta = 0.8) : this(period, alpha, beta)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_prevVariance = 0;
_longTermVariance = 0;
_isInitialized = false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate return
double return_ = (BarInput.Close - _prevClose) / _prevClose;
double squaredReturn = return_ * return_;
_prevClose = BarInput.Close;
// Initialize with first available data if not done
if (!_isInitialized && _index > _period)
{
_longTermVariance = squaredReturn; // Use current squared return as initial estimate
_prevVariance = _longTermVariance;
_isInitialized = true;
}
// Need enough values for calculation
if (_index <= _period)
{
return 0;
}
// Update variance estimate using GARCH(1,1)
double variance = _omega + _alpha * squaredReturn + _beta * _prevVariance;
_prevVariance = variance;
// Calculate annualized volatility as percentage
double volatility = Math.Sqrt(variance) * Math.Sqrt(252) * 100;
IsHot = _index >= WarmupPeriod;
return volatility;
}
}
+59 -47
View File
@@ -2,66 +2,66 @@ using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// CVI: Chaikin's Volatility
/// A technical indicator developed by Marc Chaikin that measures the volatility of a financial instrument by comparing the spread between the high and low prices.
/// CVI: Chaikin's Volatility Index
/// Measures the rate of change of a moving average of the difference
/// between high and low prices, indicating volatility expansion/contraction.
/// </summary>
/// <remarks>
/// The CVI calculation process:
/// 1. Calculates the difference between the high and low prices.
/// 2. Applies an exponential moving average (EMA) to the differences.
/// 3. Computes the percentage change in the EMA over a specified period.
/// 1. Calculate High-Low difference
/// 2. Take EMA of High-Low difference
/// 3. Calculate ROC of the EMA over specified period
///
/// Key characteristics:
/// - Measures volatility
/// - Uses high and low prices
/// - Percentage-based
/// - EMA smoothing
/// - Measures volatility expansion/contraction
/// - Default period is 10 days
/// - Default smoothing period is 10 days
/// - Positive values indicate expanding volatility
/// - Negative values indicate contracting volatility
///
/// Formula:
/// CVI = (EMA(high - low, period) - EMA(high - low, period, offset)) / EMA(high - low, period, offset) * 100
/// HL = High - Low
/// Smoothed = EMA(HL, smoothPeriod)
/// CVI = ((Smoothed - Smoothed[period]) / Smoothed[period]) * 100
///
/// Market Applications:
/// - Volatility assessment
/// - Trend confirmation
/// - Risk management
/// - Entry/exit timing
/// - Volatility measurement
/// - Trend strength analysis
/// - Market regime identification
/// - Trading range analysis
/// - Breakout confirmation
///
/// Sources:
/// Marc Chaikin - Original development
/// https://www.investopedia.com/terms/c/chaikins-volatility.asp
/// Marc Chaikin
/// https://www.investopedia.com/terms/c/chaikinvolatility.asp
///
/// Note: Higher CVI values indicate higher volatility
/// Note: Returns percentage change in volatility
/// </remarks>
[SkipLocalsInit]
public sealed class Cvi : AbstractBase
{
private readonly int _period;
private readonly Ema _ema;
private readonly CircularBuffer _buffer;
private double _prevEma;
private readonly int _smoothPeriod;
private readonly CircularBuffer _smoothed;
private readonly double _alpha;
private double _ema;
/// <param name="period">The number of periods for CVI calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cvi(int period)
public Cvi(int period = 10, int smoothPeriod = 10)
{
if (period < 1)
{
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 1.");
}
_period = period;
_ema = new Ema(period);
_buffer = new CircularBuffer(period);
WarmupPeriod = period;
Name = $"CVI({period})";
_smoothPeriod = smoothPeriod;
_alpha = 2.0 / (_smoothPeriod + 1);
WarmupPeriod = _period + _smoothPeriod;
Name = $"CVI({_period},{_smoothPeriod})";
_smoothed = new CircularBuffer(_period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for CVI calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cvi(object source, int period) : this(period)
public Cvi(object source, int period = 10, int smoothPeriod = 10) : this(period, smoothPeriod)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
@@ -71,9 +71,8 @@ public sealed class Cvi : AbstractBase
public override void Init()
{
base.Init();
_ema.Init();
_buffer.Clear();
_prevEma = 0;
_ema = 0;
_smoothed.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -81,6 +80,7 @@ public sealed class Cvi : AbstractBase
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
@@ -90,20 +90,32 @@ public sealed class Cvi : AbstractBase
{
ManageState(BarInput.IsNew);
double highLowDiff = BarInput.High - BarInput.Low;
_buffer.Add(highLowDiff, BarInput.IsNew);
// Calculate High-Low difference
double hl = BarInput.High - BarInput.Low;
double ema = _ema.Calc(new TValue(Input.Time, highLowDiff, BarInput.IsNew)).Value;
double cvi = 0;
if (_index >= _period)
// Calculate EMA of High-Low difference
if (_index == 1)
{
double prevEma = _buffer[_buffer.Count - _period];
cvi = (ema - prevEma) / prevEma * 100;
_ema = hl;
}
else
{
_ema = (_alpha * hl) + ((1 - _alpha) * _ema);
}
_prevEma = ema;
// Add smoothed value to buffer
_smoothed.Add(_ema);
// Need enough values for calculation
if (_index <= _period)
{
return 0;
}
// Calculate rate of change
double roc = ((_ema - _smoothed[_period - 1]) / _smoothed[_period - 1]) * 100;
IsHot = _index >= WarmupPeriod;
return cvi;
return roc;
}
}
+141
View File
@@ -0,0 +1,141 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// EWMA: Exponential Weighted Moving Average Volatility
/// A volatility measure that gives more weight to recent observations,
/// calculated using squared returns and exponential weighting.
/// </summary>
/// <remarks>
/// The EWMA calculation process:
/// 1. Calculate returns: (Close[t] - Close[t-1])/Close[t-1]
/// 2. Square returns
/// 3. Apply exponential weighting to squared returns
/// 4. Take square root and annualize
///
/// Key characteristics:
/// - More responsive to recent volatility changes
/// - Default decay factor (lambda) is 0.94
/// - Default period is 20 days
/// - Annualized by default (multiply by sqrt(252))
/// - Expressed as a percentage
///
/// Formula:
/// Returns[t] = (Close[t] - Close[t-1])/Close[t-1]
/// EWMA[t] = λ * EWMA[t-1] + (1-λ) * Returns[t]²
/// Volatility = sqrt(EWMA) * sqrt(252) * 100
///
/// Where:
/// λ (lambda) = decay factor (typically 0.94)
///
/// Market Applications:
/// - Risk measurement
/// - Option pricing
/// - Value at Risk (VaR)
/// - Portfolio optimization
/// - Volatility forecasting
///
/// Sources:
/// RiskMetrics™ Technical Document (1996)
/// https://www.msci.com/documents/10199/5915b101-4206-4ba0-aee2-3449d5c7e95a
///
/// Note: Returns annualized volatility as a percentage
/// </remarks>
[SkipLocalsInit]
public sealed class Ewma : AbstractBase
{
private readonly int _period;
private readonly double _lambda;
private readonly bool _annualize;
private double _prevClose;
private double _ewma;
private bool _isInitialized;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ewma(int period = 20, double lambda = 0.94, bool annualize = true)
{
_period = period;
_lambda = lambda;
_annualize = annualize;
WarmupPeriod = period + 1; // Need one extra period for returns
Name = $"EWMA({_period},{_lambda})";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ewma(object source, int period = 20, double lambda = 0.94, bool annualize = true) : this(period, lambda, annualize)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_ewma = 0;
_isInitialized = false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate return
double return_ = (BarInput.Close - _prevClose) / _prevClose;
double squaredReturn = return_ * return_;
_prevClose = BarInput.Close;
// Initialize EWMA if not done
if (!_isInitialized && _index > _period)
{
_ewma = squaredReturn;
_isInitialized = true;
}
// Need enough values for calculation
if (_index <= _period)
{
return 0;
}
// Update EWMA
_ewma = _lambda * _ewma + (1 - _lambda) * squaredReturn;
// Calculate volatility
double volatility = Math.Sqrt(_ewma);
// Annualize if requested
if (_annualize)
{
volatility *= Math.Sqrt(252);
}
// Convert to percentage
volatility *= 100;
IsHot = _index >= WarmupPeriod;
return volatility;
}
}
+163
View File
@@ -0,0 +1,163 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// FCB: Fractal Chaos Bands
/// Adaptive price bands based on fractal geometry concepts,
/// identifying potential support and resistance levels.
/// </summary>
/// <remarks>
/// The FCB calculation process:
/// 1. Identify fractal highs and lows over the period
/// 2. Calculate high and low bands using fractal points
/// 3. Smooth bands using exponential moving average
///
/// Key characteristics:
/// - Adapts to market structure
/// - Default period is 20 days
/// - Default smoothing factor is 0.5
/// - Returns upper and lower bands
/// - Based on fractal geometry concepts
///
/// Formula:
/// Fractal High = High[t] where High[t] > High[t±1,2]
/// Fractal Low = Low[t] where Low[t] < Low[t±1,2]
/// Upper Band = EMA(Fractal Highs, smoothing)
/// Lower Band = EMA(Fractal Lows, smoothing)
///
/// Market Applications:
/// - Support/resistance identification
/// - Trend analysis
/// - Volatility measurement
/// - Breakout detection
/// - Trading range analysis
///
/// Sources:
/// Bill Williams' Chaos Theory
/// Trading Chaos (2nd Edition) by Bill Williams
///
/// Note: Returns three values: upper, middle, and lower bands
/// </remarks>
[SkipLocalsInit]
public sealed class Fcb : AbstractBase
{
private readonly int _period;
private readonly double _smoothing;
private readonly CircularBuffer _highs;
private readonly CircularBuffer _lows;
private double _upperBand;
private double _middleBand;
private double _lowerBand;
private double _upperEma;
private double _lowerEma;
private readonly double _alpha;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Fcb(int period = 20, double smoothing = 0.5)
{
_period = period;
_smoothing = smoothing;
_alpha = 2.0 / (_period + 1);
WarmupPeriod = period + 4; // Need extra periods for fractal identification
Name = $"FCB({_period},{_smoothing})";
_highs = new CircularBuffer(period);
_lows = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Fcb(object source, int period = 20, double smoothing = 0.5) : this(period, smoothing)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_upperBand = 0;
_middleBand = 0;
_lowerBand = 0;
_upperEma = 0;
_lowerEma = 0;
_highs.Clear();
_lows.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Add current high/low to buffers
_highs.Add(BarInput.High);
_lows.Add(BarInput.Low);
// Need enough values for calculation
if (_index <= 4)
{
return 0;
}
// Check for fractal patterns
bool isFractalHigh = false;
bool isFractalLow = false;
if (_index >= 5)
{
// Fractal high: current high is higher than 2 bars before and after
isFractalHigh = _highs[2] > _highs[0] && _highs[2] > _highs[1] &&
_highs[2] > _highs[3] && _highs[2] > _highs[4];
// Fractal low: current low is lower than 2 bars before and after
isFractalLow = _lows[2] < _lows[0] && _lows[2] < _lows[1] &&
_lows[2] < _lows[3] && _lows[2] < _lows[4];
}
// Update EMAs with fractal points
if (isFractalHigh)
{
_upperEma = (_alpha * _highs[2]) + ((1 - _alpha) * _upperEma);
}
if (isFractalLow)
{
_lowerEma = (_alpha * _lows[2]) + ((1 - _alpha) * _lowerEma);
}
// Apply smoothing to bands
_upperBand = _smoothing * _upperEma + (1 - _smoothing) * BarInput.High;
_lowerBand = _smoothing * _lowerEma + (1 - _smoothing) * BarInput.Low;
_middleBand = (_upperBand + _lowerBand) / 2;
IsHot = _index >= WarmupPeriod;
return _middleBand; // Return middle band as primary value
}
/// <summary>
/// Gets the upper band value
/// </summary>
public double UpperBand => _upperBand;
/// <summary>
/// Gets the middle band value
/// </summary>
public double MiddleBand => _middleBand;
/// <summary>
/// Gets the lower band value
/// </summary>
public double LowerBand => _lowerBand;
}
+127
View File
@@ -0,0 +1,127 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// GKV: Garman-Klass Volatility
/// An efficient estimator of volatility that uses open, high, low,
/// and close prices to capture intraday price movements.
/// </summary>
/// <remarks>
/// The GKV calculation process:
/// 1. Calculate components using OHLC prices
/// 2. Combine components using optimal weights
/// 3. Take rolling average over period
/// 4. Annualize and convert to percentage
///
/// Key characteristics:
/// - More efficient than close-to-close volatility
/// - Uses full OHLC price information
/// - Default period is 20 days
/// - Annualized by default
/// - Expressed as a percentage
///
/// Formula:
/// u = ln(High/Low)²/2
/// c = ln(Close/Open)²
/// GKV = sqrt(sum((0.5*u - (2*ln(2)-1)*c) / period) * 252) * 100
///
/// Market Applications:
/// - Volatility estimation
/// - Risk measurement
/// - Option pricing
/// - Trading strategy development
/// - Market analysis
///
/// Sources:
/// Garman and Klass (1980)
/// Journal of Business 53(1): 67-78
///
/// Note: Returns annualized volatility as a percentage
/// </remarks>
[SkipLocalsInit]
public sealed class Gkv : AbstractBase
{
private readonly int _period;
private readonly bool _annualize;
private readonly CircularBuffer _components;
private readonly double _ln2;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Gkv(int period = 20, bool annualize = true)
{
_period = period;
_annualize = annualize;
WarmupPeriod = period;
Name = $"GKV({_period})";
_components = new CircularBuffer(period);
_ln2 = Math.Log(2);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Gkv(object source, int period = 20, bool annualize = true) : this(period, annualize)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_components.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Calculate components
double u = Math.Log(BarInput.High / BarInput.Low);
u = u * u / 2;
double c = Math.Log(BarInput.Close / BarInput.Open);
c = c * c;
// Combine components with optimal weights
double component = 0.5 * u - (2 * _ln2 - 1) * c;
_components.Add(component);
// Need enough values for calculation
if (_index <= _period)
{
return 0;
}
// Calculate average component
double avgComponent = _components.Average();
// Calculate volatility
double volatility = Math.Sqrt(avgComponent);
// Annualize if requested
if (_annualize)
{
volatility *= Math.Sqrt(252);
}
// Convert to percentage
volatility *= 100;
IsHot = _index >= WarmupPeriod;
return volatility;
}
}
+130
View File
@@ -0,0 +1,130 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// HLV: High-Low Volatility
/// A volatility measure based on the high-low range relative
/// to the previous close, capturing intraday price movements.
/// </summary>
/// <remarks>
/// The HLV calculation process:
/// 1. Calculate normalized high-low range
/// 2. Take rolling average over period
/// 3. Convert to annualized volatility
///
/// Key characteristics:
/// - Captures intraday price movements
/// - Uses high, low, and previous close
/// - Default period is 20 days
/// - Annualized by default
/// - Expressed as a percentage
///
/// Formula:
/// Range = (High - Low) / PrevClose
/// HLV = sqrt(sum(Range² / period) * 252) * 100
///
/// Market Applications:
/// - Volatility measurement
/// - Risk assessment
/// - Trading range analysis
/// - Market regime identification
/// - Position sizing
///
/// Sources:
/// Parkinson (1980) modified
/// The Extreme Value Method for Estimating the Variance of the Rate of Return
/// Journal of Business 53(1): 61-65
///
/// Note: Returns annualized volatility as a percentage
/// </remarks>
[SkipLocalsInit]
public sealed class Hlv : AbstractBase
{
private readonly int _period;
private readonly bool _annualize;
private readonly CircularBuffer _ranges;
private double _prevClose;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Hlv(int period = 20, bool annualize = true)
{
_period = period;
_annualize = annualize;
WarmupPeriod = period + 1; // Need one extra period for previous close
Name = $"HLV({_period})";
_ranges = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Hlv(object source, int period = 20, bool annualize = true) : this(period, annualize)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_ranges.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate normalized range
double range = (BarInput.High - BarInput.Low) / _prevClose;
double squaredRange = range * range;
_ranges.Add(squaredRange);
// Store current close for next calculation
_prevClose = BarInput.Close;
// Need enough values for calculation
if (_index <= _period)
{
return 0;
}
// Calculate average squared range
double avgSquaredRange = _ranges.Average();
// Calculate volatility
double volatility = Math.Sqrt(avgSquaredRange);
// Annualize if requested
if (_annualize)
{
volatility *= Math.Sqrt(252);
}
// Convert to percentage
volatility *= 100;
IsHot = _index >= WarmupPeriod;
return volatility;
}
}
+10 -10
View File
@@ -1,21 +1,21 @@
# Volatility indicators
Done: 15, Todo: 20
Done: 24, Todo: 11
✔️ ADR - Average Daily Range
✔️ AP - Andrew's Pitchfork
✔️ ATR - Average True Range
✔️ ATRP - Average True Range Percent
✔️ ATRS - ATR Trailing Stop
*BB - Bollinger Bands® (Upper, Middle, Lower)
CCV - Close-to-Close Volatility
CE - Chandelier Exit
CV - Conditional Volatility (ARCH/GARCH)
CVI - Chaikin's Volatility
✔️ BBAND - Bollinger Bands® (Upper, Middle, Lower)
✔️ CCV - Close-to-Close Volatility
✔️ CE - Chandelier Exit
✔️ CV - Conditional Volatility (ARCH/GARCH)
✔️ CVI - Chaikin's Volatility
*DC - Donchian Channels (Upper, Middle, Lower)
EWMA - Exponential Weighted Moving Average Volatility
FCB - Fractal Chaos Bands
GKV - Garman-Klass Volatility
HLV - High-Low Volatility
✔️ EWMA - Exponential Weighted Moving Average Volatility
✔️ FCB - Fractal Chaos Bands
✔️ GKV - Garman-Klass Volatility
✔️ HLV - High-Low Volatility
✔️ HV - Historical Volatility
*ICH - Ichimoku Cloud (Conversion, Base, Leading Span A, Leading Span B, Lagging Span)
✔️ JVOLTY - Jurik Volatility
+2 -6
View File
@@ -2,13 +2,10 @@
<PropertyGroup>
<AssemblyName>Averages</AssemblyName>
<AlgoType>Indicator</AlgoType>
<AssemblyVersion>0.0.0.1</AssemblyVersion>
<OutputPath>bin\$(Configuration)\</OutputPath>
<IsLocalBuild Condition="'$(GITHUB_ACTIONS)' == ''">true</IsLocalBuild>
<UpdateAssemblyInfo>true</UpdateAssemblyInfo>
<GenerateGitVersionInformation>true</GenerateGitVersionInformation>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
</ItemGroup>
@@ -16,7 +13,6 @@
<ItemGroup>
<Compile Include="..\*.cs" />
<Compile Include="*.cs" />
<Compile Include="..\..\lib\**\*.cs" Exclude="..\..\lib\bin\**;..\..\lib\obj\**" />
<Reference Include="TradingPlatform.BusinessLayer">
<HintPath>..\..\.github\TradingPlatform.BusinessLayer.dll</HintPath>
@@ -32,4 +28,4 @@
DestinationFolder="$(QuantowerRoot)\Settings\Scripts\Indicators\QuanTAlib\Averages" />
</Target>
</Project>
</Project>
+2 -5
View File
@@ -2,13 +2,10 @@
<PropertyGroup>
<AssemblyName>Momentum</AssemblyName>
<AlgoType>Indicator</AlgoType>
<AssemblyVersion>0.0.0.1</AssemblyVersion>
<OutputPath>bin\$(Configuration)\</OutputPath>
<IsLocalBuild Condition="'$(GITHUB_ACTIONS)' == ''">true</IsLocalBuild>
<UpdateAssemblyInfo>true</UpdateAssemblyInfo>
<GenerateGitVersionInformation>true</GenerateGitVersionInformation>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
</ItemGroup>
@@ -30,4 +27,4 @@
<Copy SourceFiles="$(OutputPath)\Momentum.dll" DestinationFolder="$(QuantowerRoot)\Settings\Scripts\Indicators\QuanTAlib\Momentum" />
</Target>
</Project>
</Project>
+2 -5
View File
@@ -2,13 +2,10 @@
<PropertyGroup>
<AssemblyName>Oscillators</AssemblyName>
<AlgoType>Indicator</AlgoType>
<AssemblyVersion>0.0.0.1</AssemblyVersion>
<OutputPath>bin\$(Configuration)\</OutputPath>
<IsLocalBuild Condition="'$(GITHUB_ACTIONS)' == ''">true</IsLocalBuild>
<UpdateAssemblyInfo>true</UpdateAssemblyInfo>
<GenerateGitVersionInformation>true</GenerateGitVersionInformation>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
</ItemGroup>
@@ -30,4 +27,4 @@
<Copy SourceFiles="$(OutputPath)\Oscillators.dll" DestinationFolder="$(QuantowerRoot)\Settings\Scripts\Indicators\QuanTAlib\Oscillators" />
</Target>
</Project>
</Project>
+1 -4
View File
@@ -2,13 +2,10 @@
<PropertyGroup>
<AssemblyName>Statistics</AssemblyName>
<AlgoType>Indicator</AlgoType>
<AssemblyVersion>0.0.0.1</AssemblyVersion>
<OutputPath>bin\$(Configuration)\</OutputPath>
<IsLocalBuild Condition="'$(GITHUB_ACTIONS)' == ''">true</IsLocalBuild>
<UpdateAssemblyInfo>true</UpdateAssemblyInfo>
<GenerateGitVersionInformation>true</GenerateGitVersionInformation>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
</ItemGroup>
+2 -6
View File
@@ -2,13 +2,10 @@
<PropertyGroup>
<AssemblyName>Volatility</AssemblyName>
<AlgoType>Indicator</AlgoType>
<AssemblyVersion>0.0.0.1</AssemblyVersion>
<OutputPath>bin\$(Configuration)\</OutputPath>
<IsLocalBuild Condition="'$(GITHUB_ACTIONS)' == ''">true</IsLocalBuild>
<UpdateAssemblyInfo>true</UpdateAssemblyInfo>
<GenerateGitVersionInformation>true</GenerateGitVersionInformation>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
</ItemGroup>
@@ -16,7 +13,6 @@
<ItemGroup>
<Compile Include="..\*.cs" />
<Compile Include="*.cs" />
<Compile Include="..\..\lib\**\*.cs" Exclude="..\..\lib\bin\**;..\..\lib\obj\**" />
<Reference Include="TradingPlatform.BusinessLayer">
<HintPath>..\..\.github\TradingPlatform.BusinessLayer.dll</HintPath>
@@ -31,4 +27,4 @@
<Copy SourceFiles="$(OutputPath)\Volatility.dll" DestinationFolder="$(QuantowerRoot)\Settings\Scripts\Indicators\QuanTAlib\Volatility" />
</Target>
</Project>
</Project>
+2 -5
View File
@@ -2,13 +2,10 @@
<PropertyGroup>
<AssemblyName>Volume</AssemblyName>
<AlgoType>Indicator</AlgoType>
<AssemblyVersion>0.0.0.1</AssemblyVersion>
<OutputPath>bin\$(Configuration)\</OutputPath>
<IsLocalBuild Condition="'$(GITHUB_ACTIONS)' == ''">true</IsLocalBuild>
<UpdateAssemblyInfo>true</UpdateAssemblyInfo>
<GenerateGitVersionInformation>true</GenerateGitVersionInformation>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
</ItemGroup>
@@ -30,4 +27,4 @@
<Copy SourceFiles="$(OutputPath)\Volume.dll" DestinationFolder="$(QuantowerRoot)\Settings\Scripts\Indicators\QuanTAlib\Volume" />
</Target>
</Project>
</Project>