feat: Dpo, Tsi, Vortex, Bpp, Cci, Cfo, Tr, Ui, Vc, Vov, Vr, Vs, Mfi, Nvi, Obv, Pvi, Pvo, Pvol, Pvr, Pvt, Tvi

This commit is contained in:
Miha
2024-10-30 13:45:36 -07:00
parent 06c6875970
commit 6231bab9e5
34 changed files with 3151 additions and 254 deletions
+141
View File
@@ -0,0 +1,141 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// MFI: Money Flow Index
/// A volume-weighted momentum indicator that measures the inflow and outflow of money into an asset
/// over a specific period of time. It's sometimes referred to as volume-weighted RSI.
/// </summary>
/// <remarks>
/// The MFI calculation process:
/// 1. Calculate Typical Price:
/// TP = (High + Low + Close) / 3
/// 2. Calculate Raw Money Flow:
/// RMF = TP * Volume
/// 3. Determine Positive/Negative Money Flow:
/// If TP > Previous TP: Positive Money Flow
/// If TP < Previous TP: Negative Money Flow
/// 4. Calculate Money Flow Ratio:
/// MFR = (14-period Positive Money Flow Sum) / (14-period Negative Money Flow Sum)
/// 5. Calculate Money Flow Index:
/// MFI = 100 - (100 / (1 + MFR))
///
/// Key characteristics:
/// - Oscillates between 0 and 100
/// - Default period is 14 days
/// - Overbought level typically at 80
/// - Oversold level typically at 20
/// - Volume-weighted measure
///
/// Formula:
/// TP = (High + Low + Close) / 3
/// RMF = TP * Volume
/// MFR = ΣPositive Money Flow / ΣNegative Money Flow
/// MFI = 100 - (100 / (1 + MFR))
///
/// Market Applications:
/// - Overbought/Oversold conditions
/// - Divergence analysis
/// - Trend confirmation
/// - Price reversals
/// - Volume flow analysis
///
/// Sources:
/// Gene Quong and Avrum Soudack - Original development
/// https://www.investopedia.com/terms/m/mfi.asp
///
/// Note: Values above 80 indicate overbought conditions, while values below 20 indicate oversold conditions
/// </remarks>
[SkipLocalsInit]
public sealed class Mfi : AbstractBase
{
private readonly CircularBuffer _posMf;
private readonly CircularBuffer _negMf;
private double _prevTp;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Mfi(int period = 14)
{
WarmupPeriod = period + 1; // Need one extra period for previous TP
Name = $"MFI({period})";
_posMf = new CircularBuffer(period);
_negMf = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Mfi(object source, int period = 14) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevTp = 0;
_posMf.Clear();
_negMf.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 Typical Price
double tp = (BarInput.High + BarInput.Low + BarInput.Close) / 3;
// Skip first period to establish previous TP
if (_index == 1)
{
_prevTp = tp;
return 0;
}
// Calculate Raw Money Flow
double rmf = tp * BarInput.Volume;
// Determine Positive/Negative Money Flow
if (tp > _prevTp)
{
_posMf.Add(rmf);
_negMf.Add(0);
}
else if (tp < _prevTp)
{
_posMf.Add(0);
_negMf.Add(rmf);
}
else
{
_posMf.Add(0);
_negMf.Add(0);
}
// Store current TP for next calculation
_prevTp = tp;
// Calculate Money Flow Ratio and Index
double posMfSum = _posMf.Sum();
double negMfSum = _negMf.Sum();
double mfi = Math.Abs(negMfSum) < double.Epsilon ? 100 : 100 - (100 / (1 + (posMfSum / negMfSum)));
IsHot = _index >= WarmupPeriod;
return mfi;
}
}
+114
View File
@@ -0,0 +1,114 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// NVI: Negative Volume Index
/// A cumulative indicator that focuses on days when volume decreases from the previous day.
/// It is based on the premise that smart money is active on days with lower volume.
/// </summary>
/// <remarks>
/// The NVI calculation process:
/// 1. Compare current volume with previous volume
/// 2. If current volume is less than previous volume:
/// NVI = Previous NVI + (((Close - Previous Close) / Previous Close) * Previous NVI)
/// 3. If current volume is greater than or equal to previous volume:
/// NVI = Previous NVI
///
/// Key characteristics:
/// - Cumulative indicator
/// - Only updates on lower volume days
/// - Starts at base value of 1000
/// - Focuses on smart money activity
/// - Volume-driven measure
///
/// Formula:
/// If Volume < Previous Volume:
/// NVI = Previous NVI + (Price % Change * Previous NVI)
/// Else:
/// NVI = Previous NVI
///
/// Market Applications:
/// - Smart money tracking
/// - Trend identification
/// - Market timing
/// - Volume analysis
/// - Price confirmation
///
/// Sources:
/// Paul Dysart - Original development (1930s)
/// Norman Fosback - Further development
/// https://www.investopedia.com/terms/n/nvi.asp
///
/// Note: Rising NVI suggests smart money is buying, while falling NVI suggests smart money is selling
/// </remarks>
[SkipLocalsInit]
public sealed class Nvi : AbstractBase
{
private double _prevClose;
private double _prevVolume;
private double _prevNvi;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Nvi()
{
WarmupPeriod = 2; // Need previous volume and close
Name = "NVI";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Nvi(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_prevVolume = 0;
_prevNvi = 1000; // Standard starting value
}
[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 values
if (_index == 1)
{
_prevClose = BarInput.Close;
_prevVolume = BarInput.Volume;
return _prevNvi;
}
// Calculate NVI
if (BarInput.Volume < _prevVolume)
{
double priceChange = ((BarInput.Close - _prevClose) / _prevClose);
_prevNvi += priceChange * _prevNvi;
}
// Store current values for next calculation
_prevClose = BarInput.Close;
_prevVolume = BarInput.Volume;
IsHot = _index >= WarmupPeriod;
return _prevNvi;
}
}
+117
View File
@@ -0,0 +1,117 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// OBV: On-Balance Volume
/// A momentum indicator that uses volume flow to predict changes in stock price.
/// It accumulates volume on up days and subtracts volume on down days.
/// </summary>
/// <remarks>
/// The OBV calculation process:
/// 1. Compare current close with previous close
/// 2. If current close is higher:
/// OBV = Previous OBV + Current Volume
/// 3. If current close is lower:
/// OBV = Previous OBV - Current Volume
/// 4. If current close equals previous close:
/// OBV = Previous OBV
///
/// Key characteristics:
/// - Cumulative indicator
/// - Volume-based momentum measure
/// - Leading indicator
/// - No upper or lower bounds
/// - Focuses on volume flow
///
/// Formula:
/// If Close > Previous Close:
/// OBV = Previous OBV + Volume
/// If Close < Previous Close:
/// OBV = Previous OBV - Volume
/// If Close = Previous Close:
/// OBV = Previous OBV
///
/// Market Applications:
/// - Trend confirmation
/// - Potential breakouts
/// - Divergence analysis
/// - Volume flow analysis
/// - Price movement prediction
///
/// Sources:
/// Joe Granville - Original development (1963)
/// https://www.investopedia.com/terms/o/onbalancevolume.asp
///
/// Note: Rising OBV suggests buying pressure, while falling OBV suggests selling pressure
/// </remarks>
[SkipLocalsInit]
public sealed class Obv : AbstractBase
{
private double _prevClose;
private double _prevObv;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Obv()
{
WarmupPeriod = 2; // Need previous close
Name = "OBV";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Obv(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_prevObv = 0;
}
[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 OBV
if (BarInput.Close > _prevClose)
{
_prevObv += BarInput.Volume;
}
else if (BarInput.Close < _prevClose)
{
_prevObv -= BarInput.Volume;
}
// If prices equal, OBV remains the same
// Store current close for next calculation
_prevClose = BarInput.Close;
IsHot = _index >= WarmupPeriod;
return _prevObv;
}
}
+113
View File
@@ -0,0 +1,113 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// PVI: Positive Volume Index
/// A cumulative indicator that focuses on days when volume increases from the previous day.
/// It is based on the premise that the public is active on days with higher volume.
/// </summary>
/// <remarks>
/// The PVI calculation process:
/// 1. Compare current volume with previous volume
/// 2. If current volume is greater than previous volume:
/// PVI = Previous PVI + (((Close - Previous Close) / Previous Close) * Previous PVI)
/// 3. If current volume is less than or equal to previous volume:
/// PVI = Previous PVI
///
/// Key characteristics:
/// - Cumulative indicator
/// - Only updates on higher volume days
/// - Starts at base value of 1000
/// - Focuses on public activity
/// - Volume-driven measure
///
/// Formula:
/// If Volume > Previous Volume:
/// PVI = Previous PVI + (Price % Change * Previous PVI)
/// Else:
/// PVI = Previous PVI
///
/// Market Applications:
/// - Public participation tracking
/// - Trend identification
/// - Market timing
/// - Volume analysis
/// - Price confirmation
///
/// Sources:
/// Norman Fosback - Original development
/// https://www.investopedia.com/terms/p/pvi.asp
///
/// Note: Rising PVI suggests public buying pressure, while falling PVI suggests public selling pressure
/// </remarks>
[SkipLocalsInit]
public sealed class Pvi : AbstractBase
{
private double _prevClose;
private double _prevVolume;
private double _prevPvi;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvi()
{
WarmupPeriod = 2; // Need previous volume and close
Name = "PVI";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvi(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_prevVolume = 0;
_prevPvi = 1000; // Standard starting value
}
[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 values
if (_index == 1)
{
_prevClose = BarInput.Close;
_prevVolume = BarInput.Volume;
return _prevPvi;
}
// Calculate PVI
if (BarInput.Volume > _prevVolume)
{
double priceChange = ((BarInput.Close - _prevClose) / _prevClose);
_prevPvi += priceChange * _prevPvi;
}
// Store current values for next calculation
_prevClose = BarInput.Close;
_prevVolume = BarInput.Volume;
IsHot = _index >= WarmupPeriod;
return _prevPvi;
}
}
+111
View File
@@ -0,0 +1,111 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// PVO: Percentage Volume Oscillator
/// A momentum indicator for volume that shows the relationship between two volume moving averages
/// as a percentage. Similar to the Price Oscillator but uses volume instead of price.
/// </summary>
/// <remarks>
/// The PVO calculation process:
/// 1. Calculate short-term EMA of volume
/// 2. Calculate long-term EMA of volume
/// 3. Calculate PVO:
/// PVO = ((Short EMA - Long EMA) / Long EMA) * 100
///
/// Key characteristics:
/// - Volume-based momentum indicator
/// - Oscillates around zero
/// - Shows volume trends
/// - Default periods are 12 and 26 days
/// - Percentage-based measure
///
/// Formula:
/// Short EMA = EMA(Volume, shortPeriod)
/// Long EMA = EMA(Volume, longPeriod)
/// PVO = ((Short EMA - Long EMA) / Long EMA) * 100
///
/// Market Applications:
/// - Volume trend analysis
/// - Divergence identification
/// - Volume momentum measurement
/// - Market tops and bottoms
/// - Trading volume patterns
///
/// Sources:
/// https://www.investopedia.com/terms/p/pvo.asp
///
/// Note: Positive values indicate higher short-term volume, while negative values indicate higher long-term volume
/// </remarks>
[SkipLocalsInit]
public sealed class Pvo : AbstractBase
{
private readonly int _longPeriod;
private double _shortEma;
private double _longEma;
private readonly double _shortAlpha;
private readonly double _longAlpha;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvo(int shortPeriod = 12, int longPeriod = 26)
{
_longPeriod = longPeriod;
WarmupPeriod = longPeriod;
Name = $"PVO({shortPeriod},{_longPeriod})";
_shortAlpha = 2.0 / (shortPeriod + 1);
_longAlpha = 2.0 / (longPeriod + 1);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvo(object source, int shortPeriod = 12, int longPeriod = 26) : this(shortPeriod, longPeriod)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_shortEma = 0;
_longEma = 0;
}
[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);
// Initialize or update EMAs
if (_index <= _longPeriod)
{
_shortEma = BarInput.Volume;
_longEma = BarInput.Volume;
return 0;
}
// Update EMAs
_shortEma = (_shortAlpha * BarInput.Volume) + ((1 - _shortAlpha) * _shortEma);
_longEma = (_longAlpha * BarInput.Volume) + ((1 - _longAlpha) * _longEma);
// Calculate PVO
double pvo = Math.Abs(_longEma) >= double.Epsilon ? ((_shortEma - _longEma) / _longEma) * 100 : 0;
IsHot = _index >= WarmupPeriod;
return pvo;
}
}
+108
View File
@@ -0,0 +1,108 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// PVOL: Price-Volume
/// A technical indicator that measures the relationship between price and volume changes,
/// helping to identify the strength of price movements.
/// </summary>
/// <remarks>
/// The PVOL calculation process:
/// 1. Calculate price change:
/// Price Change = (Close - Previous Close) / Previous Close
/// 2. Calculate volume change:
/// Volume Change = (Volume - Previous Volume) / Previous Volume
/// 3. Calculate PVOL:
/// PVOL = Price Change * Volume Change * 100
///
/// Key characteristics:
/// - Measures price-volume relationship
/// - Oscillates around zero
/// - Shows momentum strength
/// - Identifies volume-supported moves
/// - No specific boundaries
///
/// Formula:
/// Price Change = (Close - Previous Close) / Previous Close
/// Volume Change = (Volume - Previous Volume) / Previous Volume
/// PVOL = Price Change * Volume Change * 100
///
/// Market Applications:
/// - Price movement confirmation
/// - Volume analysis
/// - Trend strength assessment
/// - Divergence identification
/// - Market momentum analysis
///
/// Note: High positive values indicate strong upward momentum with volume support,
/// while high negative values indicate strong downward momentum with volume support
/// </remarks>
[SkipLocalsInit]
public sealed class Pvol : AbstractBase
{
private double _prevClose;
private double _prevVolume;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvol()
{
WarmupPeriod = 2; // Need previous close and volume
Name = "PVOL";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvol(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_prevVolume = 0;
}
[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 values
if (_index == 1)
{
_prevClose = BarInput.Close;
_prevVolume = BarInput.Volume;
return 0;
}
// Calculate price and volume changes
double priceChange = (Math.Abs(_prevClose) >= double.Epsilon) ? (BarInput.Close - _prevClose) / _prevClose : 0;
double volumeChange = (Math.Abs(_prevVolume) >= double.Epsilon) ? (BarInput.Volume - _prevVolume) / _prevVolume : 0;
// Store current values for next calculation
_prevClose = BarInput.Close;
_prevVolume = BarInput.Volume;
// Calculate PVOL
double pvol = priceChange * volumeChange * 100;
IsHot = _index >= WarmupPeriod;
return pvol;
}
}
+109
View File
@@ -0,0 +1,109 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// PVR: Price Volume Rank
/// A technical indicator that ranks price and volume movements to identify
/// significant market moves based on their combined strength.
/// </summary>
/// <remarks>
/// The PVR calculation process:
/// 1. Calculate price change percentage:
/// Price Change = ((Close - Previous Close) / Previous Close) * 100
/// 2. Calculate volume ratio:
/// Volume Ratio = Current Volume / Previous Volume
/// 3. Calculate PVR:
/// PVR = Price Change * Volume Ratio
///
/// Key characteristics:
/// - Combines price and volume analysis
/// - No specific boundaries
/// - Measures movement significance
/// - Volume-weighted price change
/// - Identifies strong moves
///
/// Formula:
/// Price Change = ((Close - Previous Close) / Previous Close) * 100
/// Volume Ratio = Volume / Previous Volume
/// PVR = Price Change * Volume Ratio
///
/// Market Applications:
/// - Significant move identification
/// - Volume-supported moves
/// - Trend strength analysis
/// - Breakout confirmation
/// - Market momentum measurement
///
/// Note: Higher absolute values indicate more significant price moves with volume support
/// </remarks>
[SkipLocalsInit]
public sealed class Pvr : AbstractBase
{
private double _prevClose;
private double _prevVolume;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvr()
{
WarmupPeriod = 2; // Need previous close and volume
Name = "PVR";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvr(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_prevVolume = 0;
}
[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 values
if (_index == 1)
{
_prevClose = BarInput.Close;
_prevVolume = BarInput.Volume;
return 0;
}
// Calculate price change percentage
double priceChange = (Math.Abs(_prevClose) > double.Epsilon) ? ((BarInput.Close - _prevClose) / _prevClose) * 100 : 0;
// Calculate volume ratio
double volumeRatio = (Math.Abs(_prevVolume) > double.Epsilon) ? BarInput.Volume / _prevVolume : 1;
// Store current values for next calculation
_prevClose = BarInput.Close;
_prevVolume = BarInput.Volume;
// Calculate PVR
double pvr = priceChange * volumeRatio;
IsHot = _index >= WarmupPeriod;
return pvr;
}
}
+105
View File
@@ -0,0 +1,105 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// PVT: Price Volume Trend
/// A momentum indicator that combines price and volume to determine the strength of a trend.
/// Similar to OBV but uses percentage price changes in its calculation.
/// </summary>
/// <remarks>
/// The PVT calculation process:
/// 1. Calculate price change percentage:
/// Price Change = (Close - Previous Close) / Previous Close
/// 2. Calculate PVT:
/// PVT = Previous PVT + (Price Change * Volume)
///
/// Key characteristics:
/// - Cumulative indicator
/// - Volume-weighted price changes
/// - No upper or lower bounds
/// - Trend strength measure
/// - More sensitive than OBV
///
/// Formula:
/// Price Change = (Close - Previous Close) / Previous Close
/// PVT = Previous PVT + (Price Change * Volume)
///
/// Market Applications:
/// - Trend confirmation
/// - Divergence analysis
/// - Volume-price relationships
/// - Support/resistance levels
/// - Market momentum
///
/// Sources:
/// Norman Fosback - Original development
/// https://www.investopedia.com/terms/p/pvt.asp
///
/// Note: Rising PVT suggests buying pressure, while falling PVT suggests selling pressure
/// </remarks>
[SkipLocalsInit]
public sealed class Pvt : AbstractBase
{
private double _prevClose;
private double _prevPvt;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvt()
{
WarmupPeriod = 2; // Need previous close
Name = "PVT";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pvt(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_prevPvt = 0;
}
[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 price change percentage
double priceChange = (Math.Abs(_prevClose) > double.Epsilon) ? (BarInput.Close - _prevClose) / _prevClose : 0;
// Calculate PVT
_prevPvt += priceChange * BarInput.Volume;
// Store current close for next calculation
_prevClose = BarInput.Close;
IsHot = _index >= WarmupPeriod;
return _prevPvt;
}
}
+112
View File
@@ -0,0 +1,112 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// TVI: Trade Volume Index
/// A technical indicator that determines whether a security is being accumulated or distributed
/// based on price changes relative to a minimum tick value.
/// </summary>
/// <remarks>
/// The TVI calculation process:
/// 1. Calculate price change:
/// Price Change = Close - Previous Close
/// 2. Compare price change to minimum tick value:
/// If |Price Change| >= Minimum Tick:
/// Add/Subtract volume based on price direction
///
/// Key characteristics:
/// - Volume-based trend indicator
/// - Uses minimum tick value
/// - Cumulative measure
/// - No upper or lower bounds
/// - Focuses on significant moves
///
/// Formula:
/// If |Close - Previous Close| >= Minimum Tick:
/// If Close > Previous Close:
/// TVI = Previous TVI + Volume
/// If Close < Previous Close:
/// TVI = Previous TVI - Volume
/// Else:
/// TVI = Previous TVI
///
/// Market Applications:
/// - Trend identification
/// - Volume analysis
/// - Accumulation/distribution
/// - Price movement significance
/// - Trading signal generation
///
/// Note: Rising TVI suggests accumulation, while falling TVI suggests distribution
/// </remarks>
[SkipLocalsInit]
public sealed class Tvi : AbstractBase
{
private readonly double _minTick;
private double _prevClose;
private double _prevTvi;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Tvi(double minTick = 0.5)
{
_minTick = minTick;
WarmupPeriod = 2; // Need previous close
Name = $"TVI({_minTick})";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Tvi(object source, double minTick = 0.5) : this(minTick)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_prevTvi = 0;
}
[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 price change
double priceChange = BarInput.Close - _prevClose;
// Update TVI if price change exceeds minimum tick
if (Math.Abs(priceChange) >= _minTick)
{
_prevTvi += priceChange > 0 ? BarInput.Volume : -BarInput.Volume;
}
// Store current close for next calculation
_prevClose = BarInput.Close;
IsHot = _index >= WarmupPeriod;
return _prevTvi;
}
}
+11 -10
View File
@@ -1,5 +1,5 @@
# Volume indicators
Done: 6, Todo: 12
Done: 15, Todo: 3
✔️ ADL - Chaikin Accumulation Distribution Line
✔️ ADOSC - Chaikin Accumulation Distribution Oscillator
@@ -7,15 +7,16 @@ Done: 6, Todo: 12
✔️ CMF - Chaikin Money Flow
✔️ EOM - Ease of Movement
✔️ KVO - Klinger Volume Oscillator
MFI - Money Flow Index
NVI - Negative Volume Index
OBV - On-Balance Volume
PVI - Positive Volume Index
PVOL - Price-Volume
PVO - Percentage Volume Oscillator
PVR - Price Volume Rank
PVT - Price Volume Trend
TVI - Trade Volume Index
✔️ MFI - Money Flow Index
✔️ NVI - Negative Volume Index
✔️ OBV - On-Balance Volume
✔️ PVI - Positive Volume Index
✔️ PVOL - Price-Volume
✔️ PVO - Percentage Volume Oscillator
✔️ PVR - Price Volume Rank
✔️ PVT - Price Volume Trend
✔️ TVI - Trade Volume Index
VF - Volume Force
VP - Volume Profile
VWAP - Volume Weighted Average Price
VWMA - Volume Weighted Moving Average