Files

11 KiB
Raw Permalink Blame History

BOP: Balance of Power

The market is a tug of war between buyers and sellers. BOP tells you who's pulling harder.

Property Value
Category Momentum
Inputs OHLCV bar (TBar)
Parameters None
Outputs Single series (BOP)
Output range Varies (see docs)
Warmup 0 bars (always hot)
PineScript bop.pine
  • The Balance of Power measures buying versus selling pressure by comparing the body (Close minus Open) to the range (High minus Low).
  • No configurable parameters; computation is stateless per bar.
  • Validated against TA-Lib, Skender, and Tulip reference implementations where available.

The Balance of Power measures buying versus selling pressure by comparing the body (Close minus Open) to the range (High minus Low). Created by Igor Livshin in 2001, this ratio oscillates between -1 and +1, providing instantaneous momentum readings with zero lag. A stateless indicator: each bar evaluated independently, no memory of previous values required.

Historical Context

Igor Livshin introduced BOP in the August 2001 issue of Stocks & Commodities magazine. The concept emerged from a simple observation: the relationship between open and close within a bar's range reveals buyer-seller dynamics more directly than price movement alone.

The brilliance lies in normalization. A large-bodied candle on a narrow-range day might represent significant conviction. A similar-sized body on a wide-range day might indicate indecision. BOP captures this nuance by measuring body size relative to range, not absolute body size.

Unlike momentum oscillators built on smoothed averages, BOP provides raw, unfiltered readings. This makes it noisy but responsive: ideal for detecting immediate shifts in market sentiment that lagging indicators miss.

The indicator found adoption in high-frequency and scalping strategies where milliseconds matter. Zero warmup, zero lag, zero state: BOP computes the same value regardless of historical context.

Architecture & Physics

BOP operates as a pure function: input bar, output ratio. No internal state, no memory, no recursion.

1. Body Calculation

The body measures directional movement within the bar:


Body = Close - Open

Positive body indicates closing above opening (buyers prevailed). Negative body indicates closing below opening (sellers prevailed). Zero body (doji) indicates equilibrium.

2. Range Calculation

The range measures total price movement:


Range = High - Low

Range provides the denominator for normalization. Larger ranges indicate higher volatility; smaller ranges indicate consolidation.

3. Ratio Computation

The core BOP formula:


BOP = \frac{Close - Open}{High - Low}

This ratio is bounded:

  • Maximum +1: Close equals High, Open equals Low (perfect bullish bar)
  • Minimum -1: Close equals Low, Open equals High (perfect bearish bar)
  • Zero: Close equals Open (doji or no movement)

4. Zero-Range Handling

When High equals Low (no range), division by zero would occur. QuanTAlib returns zero:


BOP = \begin{cases}
\frac{Close - Open}{High - Low} & \text{if } High > Low \\
0 & \text{if } High = Low
\end{cases}

The epsilon threshold is double.Epsilon (~5×10⁻³²⁴), handling floating-point edge cases.

5. Interpretation Matrix

BOP Value Interpretation Market State
+1.0 Maximum bullish Close=High, Open=Low
+0.5 to +1.0 Strong buying pressure Buyers dominant
0 to +0.5 Mild buying pressure Slight buyer edge
0 Equilibrium Balanced or doji
-0.5 to 0 Mild selling pressure Slight seller edge
-1.0 to -0.5 Strong selling pressure Sellers dominant
-1.0 Maximum bearish Close=Low, Open=High

6. Noise Characteristics

BOP produces high-frequency signals with no smoothing. Signal-to-noise ratio depends entirely on timeframe and instrument volatility. Common practice: smooth with SMA(14) or EMA(10) for trend identification while preserving raw readings for entry timing.

Mathematical Foundation

Closed-Form Expression

BOP admits no transfer function: it is a ratio, not a filter. No z-domain representation applies because there is no temporal dependency.

Statistical Properties

For random price movements (no trend):


E[BOP] = 0

The expected value is zero when buyers and sellers have equal strength over time.

Variance

Variance depends on the distribution of body sizes relative to ranges:


Var(BOP) = E\left[\left(\frac{Close - Open}{High - Low}\right)^2\right] - E[BOP]^2

Empirically, BOP variance increases during trending periods (bodies consistently directional) and decreases during consolidation (bodies randomly directional).

Correlation with Returns

BOP correlates with bar-level returns but captures different information:


Return_t = \frac{Close_t - Close_{t-1}}{Close_{t-1}}

Returns measure change from previous close; BOP measures efficiency of movement within current bar. A gap-up bar with a bearish body produces positive return but negative BOP.

Performance Profile

Operation Count (Streaming Mode, Scalar)

Per-bar update is minimal:

Operation Count Cost (cycles) Subtotal
SUB (body) 1 1 1
SUB (range) 1 1 1
CMP (range > ε) 1 1 1
DIV (body/range) 1 15 15
Total 4 ~18 cycles

Division dominates (83%). Predictable branch (range > 0 almost always true) avoids misprediction penalties.

Batch Mode (SIMD Analysis)

The Calculate method is fully vectorized:

Operation Scalar Ops SIMD Ops (AVX2) Speedup
Body calculation N N/4 4×
Range calculation N N/4 4×
Division N N/4 4×
Conditional select N N/4 4×

Full 4× speedup with AVX2 (double precision). With AVX-512, achieves 8× speedup. No recursive dependencies: entire calculation parallelizes.

Benchmark Results

Test environment: AMD Ryzen 9 7950X, 128 GB DDR5-6000, .NET 10.0, Windows 11 24H2

Operation Time (μs) Throughput Allocations
Streaming 100K bars 89 1.12B bars/s 0 bytes
Batch 100K bars 23 4.35B bars/s 0 bytes

The batch mode approaches memory bandwidth limits rather than compute limits.

Comparative Performance

Indicator Cycles/bar Relative
BOP 18 1.0×
EMA 21 1.17×
RSI 73 4.06×
ATR 26 1.44×
Bollinger 156 8.67×

BOP is among the fastest indicators: no state, no memory, no warmup.

Quality Metrics

Metric Score Notes
Accuracy 10/10 Exact mathematical formula
Timeliness 10/10 Zero lag (instantaneous)
Overshoot N/A Bounded [-1, +1] by construction
Smoothness 2/10 Raw signal, very noisy
Responsiveness 10/10 Immediate reaction to price action
State Complexity 1/10 No state required

Validation

Validated against four external libraries across all operating modes.

Library Batch Streaming Span Notes
TA-Lib Exact match with TA_BOP
Skender Exact match with GetBop
Tulip Exact match with ti.bop
Ooples Exact match (batch only)

Tolerance: exact match (ratio of integers produces identical floating-point results).

Common Pitfalls

  1. Noise Sensitivity: Raw BOP is extremely volatile. Single-bar spikes or dips rarely indicate trend changes. Smooth with SMA(14) or similar before making trend judgments. Use raw BOP for entry timing within an established trend direction.

  2. Gap Handling: Gaps create disconnect between bars. A gap-up followed by bearish bar produces negative BOP despite overall bullish day. Consider combining with gap analysis for complete picture.

  3. Doji Interpretation: BOP equals zero when Close equals Open. This indicates indecision, not necessarily equilibrium. High-range dojis and low-range dojis both produce BOP = 0 but carry different implications.

  4. TValue Input Limitation: The Update(TValue) method returns zero because BOP requires OHLC data. Using TValue input produces meaningless results. Always use Update(TBar) for proper calculation.

  5. Period Selection for Smoothing: When smoothing BOP, period choice affects signal timing. Shorter periods (7-10) preserve responsiveness; longer periods (14-21) reduce whipsaws. Match to trading timeframe.

  6. Extreme Value Rarity: BOP reaching ±1.0 is rare: requires Close at High/Low and Open at Low/High. Values beyond ±0.8 typically indicate strong conviction. Use these for confirmation, not entry signals.

  7. Volume Ignorance: BOP ignores volume entirely. A high-BOP bar on low volume carries different weight than same BOP on high volume. Consider pairing with volume analysis.

Usage Examples

Streaming Mode

var bop = new Bop();

foreach (var bar in liveStream)
{
    TValue result = bop.Update(bar);
    
    if (result.Value > 0.6)
        Console.WriteLine($"{bar.Time}: Strong buying pressure ({result.Value:F2})");
    else if (result.Value < -0.6)
        Console.WriteLine($"{bar.Time}: Strong selling pressure ({result.Value:F2})");
}

Batch Mode

var bars = new TBarSeries();
// ... populate with historical data ...

TSeries bopSeries = Bop.Batch(bars);

// Calculate average BOP over lookback
double avgBop = bopSeries.Values.TakeLast(14).Average();

Span-Based Calculate

ReadOnlySpan<double> open = openPrices;
ReadOnlySpan<double> high = highPrices;
ReadOnlySpan<double> low = lowPrices;
ReadOnlySpan<double> close = closePrices;

Span<double> bopValues = stackalloc double[open.Length];
Bop.Calculate(open, high, low, close, bopValues);

Smoothed BOP Chain

var bop = new Bop();
var sma = new Sma(14);

foreach (var bar in liveStream)
{
    TValue rawBop = bop.Update(bar);
    TValue smoothBop = sma.Update(rawBop);
    
    // Use raw for timing, smooth for direction
    bool trendBullish = smoothBop.Value > 0;
    bool strongEntry = rawBop.Value > 0.5;
    
    if (trendBullish && strongEntry)
        Console.WriteLine("Bullish entry signal");
}

References

  • Livshin, I. (2001). "Balance of Power." Technical Analysis of Stocks & Commodities, August 2001.
  • Investopedia. "Balance of Power (BOP) Indicator." https://www.investopedia.com/terms/b/bop.asp
  • Achelis, S. (2000). Technical Analysis from A to Z. McGraw-Hill. (General indicator theory)