CMF (Chaikin Money Flow)¶
Note: CMF (Chaikin Money Flow) is not a standard TA-Lib indicator and is not currently available in SQA::TAI. This page is provided for reference purposes as CMF is commonly used alongside other volume indicators. However, similar functionality is available through ADOSC (Chaikin A/D Oscillator).
Overview¶
Chaikin Money Flow (CMF) is a volume-weighted average of accumulation and distribution over a specified period. Created by Marc Chaikin, it measures the amount of Money Flow Volume over a specific period, helping traders identify buying and selling pressure.
What is Chaikin Money Flow?¶
CMF oscillates between -1 and +1, with: - Positive values: Buying pressure (accumulation) - Negative values: Selling pressure (distribution) - Zero line: Equilibrium between buyers and sellers
Unlike the unbounded Accumulation/Distribution Line (AD), CMF is normalized and bounded, making it easier to compare across different securities and time periods.
Formula¶
Money Flow Multiplier = ((Close - Low) - (High - Close)) / (High - Low)
Money Flow Volume = Money Flow Multiplier × Volume
CMF = Sum(Money Flow Volume, N periods) / Sum(Volume, N periods)
Typical Period: 20 or 21 days
Typical Usage¶
Note: Array elements should be ordered from oldest to newest (chronological order)
If CMF were available, it would be used like this:
# Hypothetical usage (NOT currently available in SQA::TAI)
require 'sqa/tai'
# Note: This functionality is not implemented
# cmf = SQA::TAI.cmf(high, low, close, volume, period: 20)
# Manual calculation approach:
def calculate_cmf(high, low, close, volume, period = 20)
# Calculate money flow multiplier
mf_multiplier = high.zip(low, close).map do |h, l, c|
next 0 if h == l # Avoid division by zero
((c - l) - (h - c)) / (h - l)
end
# Calculate money flow volume
mf_volume = mf_multiplier.zip(volume).map { |m, v| m * v }
# Calculate CMF using rolling window
result = Array.new(close.length, nil)
(period - 1...close.length).each do |i|
start_idx = i - period + 1
period_mf_volume = mf_volume[start_idx..i].sum
period_volume = volume[start_idx..i].sum
result[i] = period_volume.zero? ? 0 : period_mf_volume / period_volume
end
result
end
# Usage
high = [...]
low = [...]
close = [...]
volume = [...]
cmf = calculate_cmf(high, low, close, volume, period: 20)
puts "CMF(20): #{cmf.last.round(4)}"
if cmf.last > 0
puts "Buying pressure dominant"
elsif cmf.last < 0
puts "Selling pressure dominant"
end
Interpretation¶
Signal Ranges¶
| CMF Value | Interpretation | Action |
|---|---|---|
| +0.25 to +1.0 | Strong buying pressure | Confirm uptrend, hold longs |
| +0.05 to +0.25 | Moderate buying pressure | Cautiously bullish |
| -0.05 to +0.05 | Neutral | No clear edge |
| -0.25 to -0.05 | Moderate selling pressure | Cautiously bearish |
| -1.0 to -0.25 | Strong selling pressure | Confirm downtrend, hold shorts |
Key Signals¶
1. Zero Line Crosses
# CMF crosses above zero = Bullish
# CMF crosses below zero = Bearish
if cmf.last > 0 && cmf[-2] <= 0
puts "CMF Cross Above Zero - BULLISH"
elsif cmf.last < 0 && cmf[-2] >= 0
puts "CMF Cross Below Zero - BEARISH"
end
2. Divergences
# Price makes new high, CMF makes lower high = Bearish divergence
# Price makes new low, CMF makes higher low = Bullish divergence
if close.last > close[-10..-1].max && cmf.last < cmf[-10..-1].max
puts "Bearish Divergence - Weakening uptrend"
end
3. Extreme Readings
# Sustained CMF > +0.25 = Very strong accumulation
# Sustained CMF < -0.25 = Very strong distribution
if cmf.last > 0.25
puts "Strong accumulation phase"
elsif cmf.last < -0.25
puts "Strong distribution phase"
end
Comparison with Related Indicators¶
CMF vs AD vs ADOSC¶
| Feature | CMF | AD | ADOSC |
|---|---|---|---|
| Bounded | Yes (-1 to +1) | No | No |
| Calculation | Fixed period average | Cumulative | MACD of AD |
| Best For | Comparing securities | Long-term trends | Momentum shifts |
| Availability | Not in TA-Lib | Available | Available |
Alternative Indicators in SQA::TAI¶
Since CMF is not available, use these similar indicators:
1. Chaikin A/D Oscillator (ADOSC)¶
Most similar to CMF:
require 'sqa/tai'
# ADOSC is available and similar to CMF
adosc = SQA::TAI.adosc(high, low, close, volume,
fast_period: 3,
slow_period: 10)
puts "ADOSC: #{adosc.last.round(2)}"
if adosc.last > 0
puts "Accumulation (similar to positive CMF)"
elsif adosc.last < 0
puts "Distribution (similar to negative CMF)"
end
2. Accumulation/Distribution Line (AD)¶
Underlying indicator:
require 'sqa/tai'
ad = SQA::TAI.ad(high, low, close, volume)
# Look at AD trend
ad_trend = ad.last > ad[-5] ? "Rising" : "Falling"
puts "A/D Line: #{ad_trend}"
3. Money Flow Index (MFI)¶
Volume-weighted RSI:
require 'sqa/tai'
mfi = SQA::TAI.mfi(high, low, close, volume, period: 14)
puts "MFI: #{mfi.last.round(2)}"
if mfi.last > 80
puts "Overbought with volume"
elsif mfi.last < 20
puts "Oversold with volume"
end
4. On Balance Volume (OBV)¶
Simple volume indicator:
require 'sqa/tai'
obv = SQA::TAI.obv(close, volume)
# OBV trend
if obv.last > obv[-5]
puts "Volume supporting price (like positive CMF)"
else
puts "Volume not supporting price (like negative CMF)"
end
Trading Strategies with CMF¶
Strategy 1: Trend Confirmation¶
# Confirm trends with CMF
ema_20 = SQA::TAI.ema(close, period: 20)
cmf = calculate_cmf(high, low, close, volume, 20)
# Uptrend confirmation
if close.last > ema_20.last && cmf.last > 0.1
puts "CONFIRMED UPTREND"
puts "Price above EMA and CMF positive"
# Downtrend confirmation
elsif close.last < ema_20.last && cmf.last < -0.1
puts "CONFIRMED DOWNTREND"
puts "Price below EMA and CMF negative"
end
Strategy 2: Divergence Trading¶
# Look for divergences between price and CMF
cmf = calculate_cmf(high, low, close, volume, 20)
# Bearish divergence
recent_high_price = close[-20..-1].max
recent_high_cmf = cmf[-20..-1].max
if close.last >= recent_high_price && cmf.last < recent_high_cmf
puts "BEARISH DIVERGENCE"
puts "Price new high, CMF lower high"
puts "Consider taking profits or going short"
end
# Bullish divergence
recent_low_price = close[-20..-1].min
recent_low_cmf = cmf[-20..-1].min
if close.last <= recent_low_price && cmf.last > recent_low_cmf
puts "BULLISH DIVERGENCE"
puts "Price new low, CMF higher low"
puts "Consider buying opportunity"
end
Strategy 3: Breakout Validation¶
# Validate breakouts with CMF
resistance = 250.0 # Key level
cmf = calculate_cmf(high, low, close, volume, 20)
if close.last > resistance
if cmf.last > 0.15
puts "VALID BREAKOUT"
puts "Price above resistance with strong CMF"
puts "High probability continuation"
else
puts "WEAK BREAKOUT"
puts "Price above resistance but CMF weak"
puts "Potential false breakout"
end
end
Complete CMF Calculator Class¶
require 'sqa/tai'
class ChaikinMoneyFlow
attr_reader :values
def initialize(high, low, close, volume, period: 20)
@high = high
@low = low
@close = close
@volume = volume
@period = period
calculate
end
def calculate
# Calculate money flow multiplier
mf_multiplier = @high.zip(@low, @close).map do |h, l, c|
next 0 if (h - l).zero?
((c - l) - (h - c)) / (h - l)
end
# Calculate money flow volume
mf_volume = mf_multiplier.zip(@volume).map { |m, v| m * v }
# Calculate CMF for each period
@values = Array.new(@close.length, nil)
(@period - 1...@close.length).each do |i|
start_idx = i - @period + 1
sum_mf_volume = mf_volume[start_idx..i].sum
sum_volume = @volume[start_idx..i].sum
@values[i] = sum_volume.zero? ? 0 : sum_mf_volume / sum_volume
end
end
def last
@values.last
end
def signal
return :neutral if last.nil?
case last
when 0.25..Float::INFINITY
:strong_buying
when 0.05..0.25
:moderate_buying
when -0.05..0.05
:neutral
when -0.25..-0.05
:moderate_selling
when -Float::INFINITY..-0.25
:strong_selling
end
end
def trend
return :unknown if @values.compact.length < 5
recent = @values.compact.last(5)
avg = recent.sum / recent.length
if avg > 0.1
:accumulation
elsif avg < -0.1
:distribution
else
:neutral
end
end
def divergence?(price_data)
return nil if @values.compact.length < 10
# Check last 10 periods for divergence
recent_prices = price_data.last(10)
recent_cmf = @values.compact.last(10)
price_trend = recent_prices.last > recent_prices.first ? :up : :down
cmf_trend = recent_cmf.last > recent_cmf.first ? :up : :down
if price_trend == :up && cmf_trend == :down
:bearish_divergence
elsif price_trend == :down && cmf_trend == :up
:bullish_divergence
else
:no_divergence
end
end
def trading_signal(price)
sig = signal
div = divergence?(price)
case sig
when :strong_buying
if div == :bearish_divergence
{ action: :caution, reason: "Strong buying but bearish divergence" }
else
{ action: :buy, reason: "Strong accumulation" }
end
when :strong_selling
if div == :bullish_divergence
{ action: :caution, reason: "Strong selling but bullish divergence" }
else
{ action: :sell, reason: "Strong distribution" }
end
when :moderate_buying
{ action: :hold_long, reason: "Moderate accumulation" }
when :moderate_selling
{ action: :hold_short, reason: "Moderate distribution" }
else
{ action: :wait, reason: "Neutral money flow" }
end
end
def to_s
<<~INFO
Chaikin Money Flow (#{@period})
Current Value: #{last.nil? ? 'N/A' : last.round(4)}
Signal: #{signal}
Trend: #{trend}
Divergence: #{divergence?(@close)}
INFO
end
end
# Usage Example
high = [100, 102, 105, 104, 106, 108, 107, 109, 111, 110]
low = [98, 100, 103, 102, 104, 106, 105, 107, 109, 108]
close = [99, 101, 104, 103, 105, 107, 106, 108, 110, 109]
volume = [1000, 1500, 2000, 1200, 1800, 2200, 1400, 1900, 2500, 1600]
cmf = ChaikinMoneyFlow.new(high, low, close, volume, period: 5)
puts cmf.to_s
signal = cmf.trading_signal(close)
puts "\nTrading Signal:"
puts " Action: #{signal[:action]}"
puts " Reason: #{signal[:reason]}"
Best Practices¶
1. Use with Trend Indicators¶
# Combine CMF with moving averages
cmf = ChaikinMoneyFlow.new(high, low, close, volume, period: 20)
ema_50 = SQA::TAI.ema(close, period: 50)
# Only take signals aligned with trend
if close.last > ema_50.last && cmf.last > 0
puts "Aligned bullish signals"
end
2. Multiple Timeframe Confirmation¶
# Use CMF on different timeframes
daily_cmf = ChaikinMoneyFlow.new(daily_high, daily_low, daily_close,
daily_volume, period: 20)
hourly_cmf = ChaikinMoneyFlow.new(hourly_high, hourly_low, hourly_close,
hourly_volume, period: 20)
# Both timeframes aligned
if daily_cmf.last > 0 && hourly_cmf.last > 0
puts "Multi-timeframe accumulation confirmed"
end
3. Combine with Other Volume Indicators¶
# Use multiple volume indicators for confirmation
cmf = ChaikinMoneyFlow.new(high, low, close, volume, period: 20)
obv = SQA::TAI.obv(close, volume)
mfi = SQA::TAI.mfi(high, low, close, volume, period: 14)
# All volume indicators aligned
if cmf.last > 0 && obv.last > obv[-5] && mfi.last > 50
puts "Strong volume confirmation of uptrend"
end
Limitations¶
- Lagging Indicator: CMF uses historical data and may lag price action
- False Signals: Can give false signals in choppy, low-volume markets
- Period Sensitivity: Different periods can give conflicting signals
- Volume Quality: Accuracy depends on reliable volume data
See Also¶
Related Volume Indicators¶
- Accumulation/Distribution (AD) - Cumulative volume indicator
- Chaikin A/D Oscillator (ADOSC) - Similar momentum indicator
- On Balance Volume (OBV) - Simple volume flow indicator
- Money Flow Index (MFI) - Volume-weighted RSI
Recommended Combinations¶
- EMA - Trend direction
- RSI - Momentum confirmation
- MACD - Trend strength
- Bollinger Bands - Volatility context
Resources¶
Implementation Status: Not available in current TA-Lib/SQA::TAI version. Use manual calculation or ADOSC as alternative. The provided Ruby implementation can be used for custom CMF calculations.