mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-16 01:28:05 +00:00
Adl, Adosc, Aobv, Cmf
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
# Momentum indicators
|
||||
|
||||
✔️ ADX - Average Directional Movement Index
|
||||
✔️ ADXR - Average Directional Movement Index Rating
|
||||
✔️ APO - Absolute Price Oscillator
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# Oscillators indicators
|
||||
|
||||
AC - Acceleration Oscillator
|
||||
AO - Awesome Oscillator
|
||||
AROON - Aroon oscillator
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# Statistics indicators
|
||||
|
||||
BETA - Beta coefficient
|
||||
CORR - Correlation Coefficient
|
||||
✔️ CURVATURE - Rate of Change in Direction or Slope
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# Volatility indicators
|
||||
|
||||
ADR - Average Daily Range
|
||||
AP - Andrew's Pitchfork
|
||||
✔️ ATR - Average True Range
|
||||
@@ -22,7 +24,7 @@ PSAR - Parabolic Stop and Reverse
|
||||
PV - Parkinson Volatility
|
||||
RSV - Rogers-Satchell Volatility
|
||||
✔️ RV - Realized Volatility
|
||||
RVI - Relative Volatility Index
|
||||
✔️ RVI - Relative Volatility Index
|
||||
STARC - Starc Bands
|
||||
SV - Stochastic Volatility
|
||||
TR - True Range
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
+8
-6
@@ -1,9 +1,11 @@
|
||||
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
|
||||
# Volume indicators
|
||||
|
||||
✔️ 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
|
||||
|
||||
Reference in New Issue
Block a user