first iteration

This commit is contained in:
Miha Kralj
2025-11-25 20:40:46 -08:00
parent b5881b9bb4
commit 33ffd3a37a
594 changed files with 117007 additions and 80111 deletions
-112
View File
@@ -1,112 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// ADL: Accumulation Distribution Line (Chaikin)
/// A volume-based indicator that measures the cumulative flow of money into and out
/// of a security. It assesses the relationship between price and volume to determine
/// buying/selling pressure.
/// </summary>
/// <remarks>
/// The ADL calculation process:
/// 1. Calculates Money Flow Multiplier (MFM):
/// MFM = ((Close - Low) - (High - Close)) / (High - Low)
/// 2. Calculates Money Flow Volume (MFV):
/// MFV = MFM × Volume
/// 3. ADL is cumulative sum of MFV values
///
/// Key characteristics:
/// - Volume-weighted measure
/// - Cumulative indicator
/// - No upper/lower bounds
/// - Trend confirmation tool
/// - Divergence indicator
///
/// Formula:
/// MFM = ((Close - Low) - (High - Close)) / (High - Low)
/// MFV = MFM × Volume
/// ADL = Previous ADL + MFV
///
/// Market Applications:
/// - Trend confirmation
/// - Volume analysis
/// - Price/volume divergence
/// - Support/resistance levels
/// - Market participation
///
/// Sources:
/// Marc Chaikin - Original development
/// https://www.investopedia.com/terms/a/accumulationdistribution.asp
///
/// Note: Focuses on the relationship between price and volume
/// </remarks>
[SkipLocalsInit]
public sealed class Adl : AbstractBase
{
private double _cumulativeAdl;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Adl()
{
WarmupPeriod = 1;
Name = "ADL";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Adl(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_cumulativeAdl = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateMoneyFlowMultiplier(double close, double high, double low)
{
double range = high - low;
if (range > 0)
{
return ((close - low) - (high - close)) / range;
}
return 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Calculate Money Flow Multiplier
double mfm = CalculateMoneyFlowMultiplier(BarInput.Close, BarInput.High, BarInput.Low);
// Calculate Money Flow Volume
double mfv = mfm * BarInput.Volume;
// Update cumulative ADL only for new bars
if (BarInput.IsNew)
{
_cumulativeAdl += mfv;
}
IsHot = _index >= WarmupPeriod;
return _cumulativeAdl;
}
}
-137
View File
@@ -1,137 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// ADOSC: Chaikin Accumulation/Distribution Oscillator
/// A momentum indicator that measures the strength of accumulation/distribution by combining
/// price and volume with moving averages. It helps identify potential trend reversals and
/// buying/selling pressure.
/// </summary>
/// <remarks>
/// The ADOSC calculation process:
/// 1. Calculate ADL (Accumulation/Distribution Line)
/// a. Money Flow Multiplier = ((Close - Low) - (High - Close)) / (High - Low)
/// b. Money Flow Volume = MFM × Volume
/// c. ADL = Previous ADL + MFV
/// 2. Calculate two EMAs of ADL values
/// 3. Subtract longer EMA from shorter EMA
///
/// Key characteristics:
/// - Volume-weighted measure
/// - Oscillates around zero
/// - Uses two different time periods
/// - Default periods are 3 and 10 days
/// - Shows momentum of money flow
///
/// Formula:
/// MFM = ((Close - Low) - (High - Close)) / (High - Low)
/// MFV = MFM × Volume
/// ADL = Previous ADL + MFV
/// ADOSC = EMA(ADL, shortPeriod) - EMA(ADL, longPeriod)
///
/// Market Applications:
/// - Trend confirmation
/// - Divergence analysis
/// - Volume/price relationship
/// - Support/resistance levels
/// - Market reversals
///
/// Sources:
/// Marc Chaikin - Original development
/// https://www.investopedia.com/terms/c/chaikinoscillator.asp
///
/// Note: Positive values indicate buying pressure, while negative values indicate selling pressure
/// </remarks>
[SkipLocalsInit]
public sealed class Adosc : AbstractBase
{
private readonly int _longPeriod;
private double _cumulativeAdl;
private double _shortEma;
private double _longEma;
private readonly double _shortAlpha;
private readonly double _longAlpha;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Adosc(int shortPeriod = 3, int longPeriod = 10)
{
_longPeriod = longPeriod;
WarmupPeriod = longPeriod; // Need longer period for EMA calculation
Name = $"ADOSC({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 Adosc(object source, int shortPeriod = 3, int longPeriod = 10) : this(shortPeriod, longPeriod)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_cumulativeAdl = 0;
_shortEma = 0;
_longEma = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateMoneyFlowMultiplier(double close, double high, double low)
{
double range = high - low;
if (range > 0)
{
return ((close - low) - (high - close)) / range;
}
return 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Calculate Money Flow Multiplier
double mfm = CalculateMoneyFlowMultiplier(BarInput.Close, BarInput.High, BarInput.Low);
// Calculate Money Flow Volume
double mfv = mfm * BarInput.Volume;
// Update cumulative ADL
_cumulativeAdl += mfv;
// Calculate EMAs
if (_index <= _longPeriod)
{
// Initialize EMAs
_shortEma = _cumulativeAdl;
_longEma = _cumulativeAdl;
return 0;
}
// Update EMAs
_shortEma = (_shortAlpha * _cumulativeAdl) + ((1 - _shortAlpha) * _shortEma);
_longEma = (_longAlpha * _cumulativeAdl) + ((1 - _longAlpha) * _longEma);
// Calculate ADOSC
double adosc = _shortEma - _longEma;
IsHot = _index >= WarmupPeriod;
return adosc;
}
}
-132
View File
@@ -1,132 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// AOBV: Archer On-Balance Volume
/// A modified version of the traditional On-Balance Volume (OBV) indicator that uses a more
/// sophisticated method to determine buying and selling pressure. It considers both the
/// closing price and the price range to provide a more nuanced view of volume flow.
/// </summary>
/// <remarks>
/// The AOBV calculation process:
/// 1. Determine price position within the day's range
/// 2. Apply volume based on price position:
/// - If close is in upper 1/3 of range: Add full volume
/// - If close is in middle 1/3 of range: Add/subtract half volume
/// - If close is in lower 1/3 of range: Subtract full volume
///
/// Key characteristics:
/// - Volume-weighted measure
/// - Cumulative indicator
/// - No upper/lower bounds
/// - More nuanced than traditional OBV
/// - Considers price position in range
///
/// Formula:
/// Range = High - Low
/// UpperThird = High - (Range / 3)
/// LowerThird = Low + (Range / 3)
/// If Close >= UpperThird:
/// AOBV = Previous AOBV + Volume
/// Else if Close <= LowerThird:
/// AOBV = Previous AOBV - Volume
/// Else:
/// If Close > Previous Close:
/// AOBV = Previous AOBV + (Volume / 2)
/// Else:
/// AOBV = Previous AOBV - (Volume / 2)
///
/// Market Applications:
/// - Trend confirmation
/// - Volume analysis
/// - Price/volume divergence
/// - Support/resistance levels
/// - Market participation
///
/// Sources:
/// Steve Archer - Original development
/// Technical Analysis of Stock Trends (Edwards, Magee)
///
/// Note: Provides a more detailed analysis of volume flow than traditional OBV
/// </remarks>
[SkipLocalsInit]
public sealed class Aobv : AbstractBase
{
private double _cumulativeAobv;
private double _prevClose;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Aobv()
{
WarmupPeriod = 1;
Name = "AOBV";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Aobv(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_cumulativeAobv = 0;
_prevClose = 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;
}
double range = BarInput.High - BarInput.Low;
if (range > 0)
{
double upperThird = BarInput.High - (range / 3);
double lowerThird = BarInput.Low + (range / 3);
// Determine volume flow based on price position
if (BarInput.Close >= upperThird)
{
_cumulativeAobv += BarInput.Volume;
}
else if (BarInput.Close <= lowerThird)
{
_cumulativeAobv -= BarInput.Volume;
}
else
{
// In middle third, use half volume based on close comparison
_cumulativeAobv += (BarInput.Close > _prevClose) ?
(BarInput.Volume / 2) : -(BarInput.Volume / 2);
}
}
_prevClose = BarInput.Close;
IsHot = _index >= WarmupPeriod;
return _cumulativeAobv;
}
}
-128
View File
@@ -1,128 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// CMF: Chaikin Money Flow
/// A volume-weighted technical indicator that measures the amount of Money Flow Volume (MFV)
/// over a specific period. Unlike ADL which is cumulative, CMF averages the Money Flow
/// Volume over a specified period.
/// </summary>
/// <remarks>
/// The CMF calculation process:
/// 1. Calculates Money Flow Multiplier (MFM):
/// MFM = ((Close - Low) - (High - Close)) / (High - Low)
/// 2. Calculates Money Flow Volume (MFV):
/// MFV = MFM × Volume
/// 3. CMF = Sum(MFV) / Sum(Volume) over N periods
///
/// Key characteristics:
/// - Oscillator between -1 and +1
/// - Volume-weighted measure
/// - Non-cumulative indicator
/// - Default period is 20 days
///
/// Formula:
/// MFM = ((Close - Low) - (High - Close)) / (High - Low)
/// MFV = MFM × Volume
/// CMF = Sum(MFV over N periods) / Sum(Volume over N periods)
///
/// Market Applications:
/// - Trend confirmation
/// - Volume analysis
/// - Price/volume divergence
/// - Support/resistance levels
/// - Market participation
///
/// Sources:
/// Marc Chaikin - Original development
/// https://www.investopedia.com/terms/c/chaikinmoneyflow.asp
///
/// Note: Values above zero indicate buying pressure, while values below zero indicate selling pressure
/// </remarks>
[SkipLocalsInit]
public sealed class Cmf : AbstractBase
{
private readonly int _period;
private readonly double[] _mfv;
private readonly double[] _volume;
private int _position;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cmf(int period = 20)
{
_period = period;
WarmupPeriod = period;
Name = $"CMF({_period})";
_mfv = new double[period];
_volume = new double[period];
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cmf(object source, int period = 20) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_position = 0;
Array.Clear(_mfv, 0, _mfv.Length);
Array.Clear(_volume, 0, _volume.Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateMoneyFlowMultiplier(double close, double high, double low)
{
double range = high - low;
if (range > 0)
{
return ((close - low) - (high - close)) / range;
}
return 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Calculate Money Flow Multiplier
double mfm = CalculateMoneyFlowMultiplier(BarInput.Close, BarInput.High, BarInput.Low);
// Calculate Money Flow Volume
double currentMfv = mfm * BarInput.Volume;
// Update circular buffers
_mfv[_position] = currentMfv;
_volume[_position] = BarInput.Volume;
_position = (_position + 1) % _period;
// Calculate CMF
double sumMfv = 0;
double sumVolume = 0;
for (int i = 0; i < _period; i++)
{
sumMfv += _mfv[i];
sumVolume += _volume[i];
}
double cmf = Math.Abs(sumVolume) > double.Epsilon ? sumMfv / sumVolume : 0;
IsHot = _index >= WarmupPeriod;
return cmf;
}
}
-131
View File
@@ -1,131 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// EOM: Ease of Movement
/// A volume-based technical indicator that relates price change to volume, showing the
/// relationship between price change and volume. It emphasizes days where price changes
/// are accomplished with minimal volume and minimizes days where large volume generates
/// small price changes.
/// </summary>
/// <remarks>
/// The EOM calculation process:
/// 1. Calculate the distance moved:
/// Distance = ((High + Low)/2 - (Prior High + Prior Low)/2)
/// 2. Calculate the Box Ratio:
/// BoxRatio = Volume / (High - Low)
/// 3. Calculate single-period EMV:
/// EMV = Distance / BoxRatio
/// 4. Smooth EMV using simple moving average (optional)
///
/// Key characteristics:
/// - Volume-weighted measure
/// - Oscillates around zero
/// - Shows ease of price movement
/// - Default period is 14 days
///
/// Formula:
/// Distance = ((H + L)/2 - (pH + pL)/2)
/// BoxRatio = Volume / (High - Low)
/// EMV = Distance / BoxRatio
/// EOM = SMA(EMV, period)
///
/// Market Applications:
/// - Trend strength analysis
/// - Volume/price relationship
/// - Support/resistance breakouts
/// - Market momentum
/// - Divergence identification
///
/// Sources:
/// Richard W. Arms Jr. - Original development
/// https://www.investopedia.com/terms/e/easeofmovement.asp
///
/// Note: Positive values suggest prices are rising with light volume (bullish),
/// while negative values suggest prices are falling with light volume (bearish)
/// </remarks>
[SkipLocalsInit]
public sealed class Eom : AbstractBase
{
private readonly int _period;
private readonly double[] _emv;
private int _position;
private double _prevMidpoint;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Eom(int period = 14)
{
_period = period;
WarmupPeriod = period + 1; // Need one extra period for previous midpoint
Name = $"EOM({_period})";
_emv = new double[period];
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Eom(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();
_position = 0;
_prevMidpoint = 0;
Array.Clear(_emv, 0, _emv.Length);
}
[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);
double midpoint = (BarInput.High + BarInput.Low) / 2;
double boxRatio = BarInput.Volume / (BarInput.High - BarInput.Low + double.Epsilon); // Avoid division by zero
// Skip first period to establish previous midpoint
if (_index == 1)
{
_prevMidpoint = midpoint;
return 0;
}
// Calculate distance moved
double distance = midpoint - _prevMidpoint;
// Calculate EMV for this period
double emv = distance / boxRatio * 10000; // Multiply by 10000 to make values more readable
// Store in circular buffer
_emv[_position] = emv;
_position = (_position + 1) % _period;
// Calculate EOM (simple moving average of EMV)
double sum = 0;
for (int i = 0; i < _period; i++)
{
sum += _emv[i];
}
double eom = sum / _period;
// Store current midpoint for next calculation
_prevMidpoint = midpoint;
IsHot = _index >= WarmupPeriod;
return eom;
}
}
-140
View File
@@ -1,140 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// KVO: Klinger Volume Oscillator
/// A volume-based technical indicator that compares volume to price movement to identify
/// long-term trends and potential reversals. It helps determine the long-term money flow
/// while remaining sensitive to short-term fluctuations.
/// </summary>
/// <remarks>
/// The KVO calculation process:
/// 1. Calculate Trend:
/// Trend = Current DM > Previous DM ? +1 : -1
/// 2. Calculate Volume Force (VF):
/// VF = Volume * abs(ROC) * Trend * 100
/// 3. Calculate two EMAs of VF and their difference:
/// Signal = EMA(VF, shortPeriod) - EMA(VF, longPeriod)
///
/// Key characteristics:
/// - Volume-weighted measure
/// - Oscillates around zero
/// - Uses two different time periods
/// - Default periods are 34 and 55 days
/// - Shows volume force and price direction
///
/// Formula:
/// DM = (H + L + C) / 3
/// Trend = DM > Previous DM ? +1 : -1
/// VF = Volume * abs(ROC) * Trend * 100
/// KVO = EMA(VF, shortPeriod) - EMA(VF, longPeriod)
///
/// Market Applications:
/// - Trend confirmation
/// - Divergence analysis
/// - Volume/price relationship
/// - Support/resistance levels
/// - Market reversals
///
/// Sources:
/// Stephen Klinger - Original development
/// https://www.investopedia.com/terms/k/klingeroscillator.asp
///
/// Note: Positive values indicate buying pressure, while negative values indicate selling pressure
/// </remarks>
[SkipLocalsInit]
public sealed class Kvo : AbstractBase
{
private readonly int _longPeriod;
private double _prevDm;
private double _shortEma;
private double _longEma;
private readonly double _shortAlpha;
private readonly double _longAlpha;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Kvo(int shortPeriod = 34, int longPeriod = 55)
{
_longPeriod = longPeriod;
WarmupPeriod = longPeriod + 1; // Need one extra period for previous DM
Name = $"KVO({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 Kvo(object source, int shortPeriod = 34, int longPeriod = 55) : this(shortPeriod, longPeriod)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevDm = 0;
_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);
// Calculate Daily Mean
double dm = (BarInput.High + BarInput.Low + BarInput.Close) / 3;
// Skip first period to establish previous DM
if (_index == 1)
{
_prevDm = dm;
return 0;
}
// Calculate Trend
int trend = dm > _prevDm ? 1 : -1;
// Calculate Rate of Change
double roc = Math.Abs(dm - _prevDm) / _prevDm;
// Calculate Volume Force
double vf = BarInput.Volume * roc * trend * 100;
// Calculate EMAs
if (_index <= _longPeriod)
{
// Initialize EMAs
_shortEma = vf;
_longEma = vf;
}
else
{
// Update EMAs
_shortEma = (_shortAlpha * vf) + ((1 - _shortAlpha) * _shortEma);
_longEma = (_longAlpha * vf) + ((1 - _longAlpha) * _longEma);
}
// Store current DM for next calculation
_prevDm = dm;
// Calculate KVO
double kvo = _shortEma - _longEma;
IsHot = _index >= WarmupPeriod;
return kvo;
}
}
-140
View File
@@ -1,140 +0,0 @@
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;
}
}
-113
View File
@@ -1,113 +0,0 @@
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;
}
}
-116
View File
@@ -1,116 +0,0 @@
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;
}
}
-112
View File
@@ -1,112 +0,0 @@
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;
}
}
-110
View File
@@ -1,110 +0,0 @@
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;
}
}
-107
View File
@@ -1,107 +0,0 @@
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;
}
}
-108
View File
@@ -1,108 +0,0 @@
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;
}
}
-104
View File
@@ -1,104 +0,0 @@
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;
}
}
-111
View File
@@ -1,111 +0,0 @@
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;
}
}
-109
View File
@@ -1,109 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// VF: Volume Force
/// A volume-based indicator that measures the strength of volume relative to price
/// movement. It helps identify whether volume is supporting or contradicting the
/// current price trend.
/// </summary>
/// <remarks>
/// The VF calculation process:
/// 1. Calculate price change
/// 2. Calculate volume force as volume * price change
/// 3. Optionally smooth the result with EMA
///
/// Key characteristics:
/// - Volume-weighted measure
/// - Trend strength indicator
/// - No upper/lower bounds
/// - Raw and smoothed versions
/// - Divergence indicator
///
/// Formula:
/// VF = Volume * (Close - Close[1])
/// Smoothed VF = EMA(VF, period)
///
/// Market Applications:
/// - Volume analysis
/// - Trend confirmation
/// - Price/volume divergence
/// - Market participation
/// - Momentum confirmation
///
/// Note: Higher values indicate stronger volume force
/// </remarks>
[SkipLocalsInit]
public sealed class Vf : AbstractBase
{
private readonly Ema _ema;
private double _prevClose;
private double _p_prevClose;
private const int DefaultPeriod = 13;
/// <param name="period">The smoothing period for EMA calculation (default 13).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vf(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
_ema = new(period);
WarmupPeriod = period + 1;
Name = $"VF({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The smoothing period for EMA calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vf(object source, int period = DefaultPeriod) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_ema.Init();
_prevClose = double.NaN;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_index++;
_p_prevClose = _prevClose;
}
else
{
_prevClose = _p_prevClose;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate raw volume force
double priceChange = BarInput.Close - _prevClose;
double volumeForce = BarInput.Volume * priceChange;
// Update previous close
_prevClose = BarInput.Close;
// Apply EMA smoothing
return _ema.Calc(volumeForce, BarInput.IsNew);
}
}
-104
View File
@@ -1,104 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// VP: Volume Profile
/// A volume-based indicator that analyzes volume distribution across price levels.
/// It helps identify significant price levels where most trading activity occurs.
/// </summary>
/// <remarks>
/// The VP calculation process:
/// 1. Track volume at each price level within a period
/// 2. Calculate Point of Control (POC) - price with highest volume
/// 3. Calculate Value Area (70% of total volume)
///
/// Key characteristics:
/// - Price level analysis
/// - Volume distribution
/// - Support/resistance identification
/// - Trading activity concentration
/// - Market structure analysis
///
/// Formula:
/// VP = Σ Volume at each price level
/// POC = Price level with max volume
/// Value Area = Price range containing 70% of volume
///
/// Market Applications:
/// - Support/resistance levels
/// - Market structure analysis
/// - Trading activity patterns
/// - Price level significance
/// - Volume concentration
///
/// Note: Returns Point of Control (price level with highest volume)
/// </remarks>
[SkipLocalsInit]
public sealed class Vp : AbstractBase
{
private readonly CircularBuffer _volumes;
private readonly CircularBuffer _prices;
private const int DefaultPeriod = 14;
/// <param name="period">The number of periods to analyze volume distribution (default 14).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vp(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
_volumes = new(period);
_prices = new(period);
WarmupPeriod = period;
Name = $"VP({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods to analyze volume distribution.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vp(object source, int period = DefaultPeriod) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
_index++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static int FindMaxVolumeIndex(CircularBuffer volumes)
{
int maxIndex = 0;
double maxVolume = volumes[0];
for (int i = 1; i < volumes.Count; i++)
{
if (volumes[i] > maxVolume)
{
maxVolume = volumes[i];
maxIndex = i;
}
}
return maxIndex;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Store volume and price
_volumes.Add(BarInput.Volume, BarInput.IsNew);
_prices.Add(BarInput.Close, BarInput.IsNew);
// Find price level with highest volume (Point of Control)
int pocIndex = FindMaxVolumeIndex(_volumes);
return _prices[pocIndex];
}
}
-92
View File
@@ -1,92 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// VWAP: Volume Weighted Average Price
/// A trading benchmark that shows the ratio of the value traded to total volume
/// traded over a specific period. VWAP equals the dollar value of all trading
/// periods divided by the total trading volume for the current day.
/// </summary>
/// <remarks>
/// The VWAP calculation process:
/// 1. Calculate typical price for each period
/// 2. Multiply typical price by volume
/// 3. Calculate cumulative values
/// 4. Divide cumulative (price * volume) by cumulative volume
///
/// Key characteristics:
/// - Intraday trading benchmark
/// - Volume-weighted measure
/// - Institutional trading reference
/// - Price momentum indicator
/// - Trading efficiency measure
///
/// Formula:
/// VWAP = Σ(Price * Volume) / ΣVolume
/// where Price = (High + Low + Close)/3
///
/// Market Applications:
/// - Best execution analysis
/// - Trading algorithms
/// - Price momentum
/// - Market impact analysis
/// - Order timing
///
/// Sources:
/// https://www.investopedia.com/terms/v/vwap.asp
///
/// Note: Commonly used by institutional traders
/// </remarks>
[SkipLocalsInit]
public sealed class Vwap : AbstractBase
{
private double _cumulativeTPV; // Cumulative (Typical Price * Volume)
private double _cumulativeVolume;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vwap()
{
WarmupPeriod = 1;
Name = "VWAP";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vwap(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_cumulativeTPV = 0;
_cumulativeVolume = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
_index++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Update cumulative values only for new bars
if (BarInput.IsNew)
{
_cumulativeTPV += BarInput.HLC3 * BarInput.Volume;
_cumulativeVolume += BarInput.Volume;
}
// Calculate VWAP
return _cumulativeVolume > 0 ? _cumulativeTPV / _cumulativeVolume : BarInput.HLC3;
}
}
-91
View File
@@ -1,91 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// VWMA: Volume Weighted Moving Average
/// A technical indicator that combines price and volume to show the average price
/// weighted by volume over a period. It gives more weight to prices with higher
/// volume, making it more responsive to high-volume price movements.
/// </summary>
/// <remarks>
/// The VWMA calculation process:
/// 1. Multiply price by volume for each period
/// 2. Sum (price * volume) over the period
/// 3. Sum volume over the period
/// 4. Divide sums to get weighted average
///
/// Key characteristics:
/// - Volume-sensitive average
/// - Trend indicator
/// - Support/resistance levels
/// - Price momentum
/// - Volume emphasis
///
/// Formula:
/// VWMA = Σ(Price * Volume) / ΣVolume
/// where sums are taken over the specified period
///
/// Market Applications:
/// - Trend identification
/// - Support/resistance levels
/// - Volume analysis
/// - Price momentum
/// - Trading signals
///
/// Note: More responsive to high-volume price movements
/// </remarks>
[SkipLocalsInit]
public sealed class Vwma : AbstractBase
{
private readonly CircularBuffer _priceVolume;
private readonly CircularBuffer _volume;
private const int DefaultPeriod = 20;
/// <param name="period">The number of periods for VWMA calculation (default 20).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vwma(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
_priceVolume = new(period);
_volume = new(period);
WarmupPeriod = period;
Name = $"VWMA({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for VWMA calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vwma(object source, int period = DefaultPeriod) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
_index++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Calculate and store price * volume
double priceVolume = BarInput.Close * BarInput.Volume;
_priceVolume.Add(priceVolume, BarInput.IsNew);
_volume.Add(BarInput.Volume, BarInput.IsNew);
// Calculate sums
double sumPriceVolume = _priceVolume.Sum();
double sumVolume = _volume.Sum();
// Calculate VWMA
return sumVolume > 0 ? sumPriceVolume / sumVolume : BarInput.Close;
}
}
-22
View File
@@ -1,22 +0,0 @@
# Volume indicators
Done: 19, Todo: 0
✔️ ADL - Chaikin Accumulation Distribution Line
✔️ ADOSC - Chaikin Accumulation Distribution Oscillator
✔️ AOBV - Archer On-Balance Volume
✔️ 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
✔️ VF - Volume Force
✔️ VP - Volume Profile
✔️ VWAP - Volume Weighted Average Price
✔️ VWMA - Volume Weighted Moving Average