Adl, Adosc, Aobv, Cmf

This commit is contained in:
Miha
2024-10-28 15:29:29 -07:00
parent 6b79f8158c
commit 45c6f08e1e
14 changed files with 1266 additions and 23 deletions
+2
View File
@@ -1,3 +1,5 @@
# Momentum indicators
✔️ ADX - Average Directional Movement Index
✔️ ADXR - Average Directional Movement Index Rating
✔️ APO - Absolute Price Oscillator
+2
View File
@@ -1,3 +1,5 @@
# Oscillators indicators
AC - Acceleration Oscillator
AO - Awesome Oscillator
AROON - Aroon oscillator
+2
View File
@@ -1,3 +1,5 @@
# Statistics indicators
BETA - Beta coefficient
CORR - Correlation Coefficient
✔️ CURVATURE - Rate of Change in Direction or Slope
+3 -1
View File
@@ -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
+113
View File
@@ -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;
}
}
+138
View File
@@ -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;
}
}
+133
View File
@@ -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;
}
}
+129
View File
@@ -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;
}
}
+132
View File
@@ -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;
}
}
+141
View File
@@ -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
View File
@@ -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