0bps

Strategy vault / Trend following

OBV EMA Crossover Trend Following Strategy

This strategy uses the crossover of dual EMA lines of OBV indicator to determine the trend of OBV, and takes long/short positions according to the trend direction. OBV indicator can reflect the relationship between…

Author
ChaoZhang
Complexity
Intermediate · 89 lines
Parameters
12
Backtest bar
1h · intraday
Tested on
BTC/USDT · futures

IMG

Overview

This strategy uses the crossover of dual EMA lines of OBV indicator to determine the trend of OBV, and takes long/short positions according to the trend direction. OBV indicator can reflect the relationship between price and volume more clearly and judge the intention of market participants, so it can be used to capture market trend. This strategy combines the indicator smoothing of moving averages, which can effectively filter out market noise and capture the main trend.

Strategy Principle

This strategy mainly uses whether OBV is in an uptrend to determine the long entry timing. Specifically, it calculates the 6-day EMA and 24-day EMA of OBV. When the 6-day EMA crosses above the 24-day EMA, a long signal is generated. Similarly, when the 6-day EMA crosses below the 24-day EMA, a short signal is generated. In addition, the strategy also sets a 3% stop loss.

The key to the strategy's trend judgment lies in the OBV indicator. OBV indicator reflects the collective intention of big money and can effectively reflect the attitude of market participants. Combined with the moving average line processing, some noise can be filtered out to make the signal clearer and more reliable. The strategy uses fast EMA lines and slow EMA lines to construct trading signals, which can smooth price data and also capture trend changes more sensitively.

Advantage Analysis

The strategy has the following advantages:

  1. OBV indicator based on trading volume can clearly judge the intention of market participants, and the signal is more reliable.

  2. The dual EMA line processing can filter out some noise to make the signal clearer.

  3. The combination of fast and slow EMA lines can take into account both price smoothing and capturing trend changes.

  4. The strategy operation is simple and easy to implement.

Risk Analysis

The strategy also has some risks:

  1. OBV indicator may issue wrong signals at some point, at which the strategy may suffer losses.

  2. In violent trading, EMA lines have lagging effect, which may miss the optimal entry point.

  3. The fixed stop loss setting may be too rigid to adapt to market changes.

Countermeasures:

  1. Confirm with other indicators to avoid wrong signals.

  2. Optimize parameter settings to make EMA lines more sensitive.

  3. Set dynamic stop loss.

Optimization Direction

The strategy can be optimized in the following aspects:

  1. Optimize EMA parameter combination to find better matched moving average parameters.

  2. Increase other indicators for signal confirmation, such as MACD, RSI, etc., to improve signal accuracy.

  3. Set dynamic stop loss, which can adjust stop loss point in real time according to market fluctuations.

  4. Parameter combination optimization to find the best parameter combination.

Conclusion

In general, this strategy is a relatively simple and reliable trend following strategy. It combines OBV indicator and dual EMA lines to judge the trend. The advantages are simple operation, clear signals, and ability to effectively track trends. The disadvantages are possible wrong signals and lagging of EMA line processing. Optimization with other indicators can achieve better results.

Parameters

NameDefaultWhat it controls
v_input_12017Backtest Start Year
v_input_2trueBacktest Start Month
v_input_3trueBacktest Start Day
v_input_42019Backtest Stop Year
v_input_512Backtest Stop Month
v_input_631Backtest Stop Day
v_input_73ATR Period
v_input_8trueATR Mult
v_input_924v_input_9
v_input_106v_input_10
v_input_113Stop Loss %
v_input_125000Take Profit %

Source Pine Script

/*backtest
start: 2024-01-01 00:00:00
end: 2024-01-31 23:59:59
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

//@version=4
strategy("OBV EMA X BF ?", overlay=false, initial_capital=10000, default_qty_type=strategy.percent_of_equity, default_qty_value=100, commission_type=strategy.commission.percent, commission_value=0.0)

/////////////// Time Frame ///////////////
testStartYear = input(2017, "Backtest Start Year") 
testStartMonth = input(1, "Backtest Start Month")
testStartDay = input(1, "Backtest Start Day")
testPeriodStart = timestamp(testStartYear,testStartMonth,testStartDay, 0, 0)

testStopYear = input(2019, "Backtest Stop Year")
testStopMonth = input(12, "Backtest Stop Month")
testStopDay = input(31, "Backtest Stop Day")
testPeriodStop = timestamp(testStopYear,testStopMonth,testStopDay, 0, 0)

testPeriod() => true

/////////////// OBV /////////////// 
src = close
atr = atr(input(title="ATR Period", defval=3, minval=1))
atrmult = input(title="ATR Mult", defval=1, minval=0)
obv = cum(change(src) > 0 ? volume * (volume / atr) : change(src) < 0 ? -volume * (volume / atr) : 0 * volume / atr)
e1 = ema(obv, input(24))
e2 = ema(obv, input(6))

///////////////  Strategy  /////////////// 
long = crossover(e2, e1)
short = crossunder(e2, e1)

last_long = 0.0
last_short = 0.0
last_long := long ? time : nz(last_long[1])
last_short := short ? time : nz(last_short[1])

long_signal = crossover(last_long, last_short)
short_signal = crossover(last_short, last_long)

last_open_long_signal = 0.0
last_open_short_signal = 0.0
last_open_long_signal := long_signal ? open : nz(last_open_long_signal[1])
last_open_short_signal := short_signal ? open : nz(last_open_short_signal[1])

last_long_signal = 0.0
last_short_signal = 0.0
last_long_signal := long_signal ? time : nz(last_long_signal[1])
last_short_signal := short_signal ? time : nz(last_short_signal[1])

in_long_signal = last_long_signal > last_short_signal
in_short_signal = last_short_signal > last_long_signal

last_high = 0.0
last_low = 0.0
last_high := not in_long_signal ? na : in_long_signal and (na(last_high[1]) or high > nz(last_high[1])) ? high : nz(last_high[1])
last_low := not in_short_signal ? na : in_short_signal and (na(last_low[1]) or low < nz(last_low[1])) ? low : nz(last_low[1])

since_longEntry = barssince(last_open_long_signal != last_open_long_signal[1]) 
since_shortEntry = barssince(last_open_short_signal != last_open_short_signal[1]) 

//////////////// Stop loss /////////////// 
sl_inp = input(3.0, title='Stop Loss %') / 100
tp_inp = input(5000.0, title='Take Profit %') / 100
 
take_level_l = strategy.position_avg_price * (1 + tp_inp)
take_level_s = strategy.position_avg_price * (1 - tp_inp)

slLong = in_long_signal ? strategy.position_avg_price * (1 - sl_inp) : na
slShort = strategy.position_avg_price * (1 + sl_inp)
long_sl = in_long_signal ? slLong : na
short_sl = in_short_signal ? slShort : na

/////////////// Execution /////////////// 
if testPeriod()
    strategy.entry("L", strategy.long, when=long)
    strategy.entry("S", strategy.short, when=short)
    strategy.exit("L SL", "L", stop=long_sl, when=since_longEntry > 0)
    strategy.exit("S SL", "S", stop=short_sl, when=since_shortEntry > 0)

/////////////// Plotting /////////////// 
plot(e1, color = e1 > e1[1] ? color.lime : e1 < e1[1] ? color.red : color.white, linewidth = 2, offset = 0)
plot(e2, color = e2 > e2[1] ? color.lime : e2 < e2[1] ? color.red : color.white, linewidth = 1)
bgcolor(strategy.position_size > 0 ? color.lime : strategy.position_size < 0 ? color.red : color.white, transp=90)
bgcolor(long_signal ? color.lime : short_signal ? color.red : na, transp=60)

Published by ChaoZhang on the FMZ strategy square · last modified 2024-02-20. Collected in the Quant Trading Vault. Unvalidated research: re-test with your own data and costs before trusting it.

Build this with AgenKit

A spec is not a trading system. Paste this into your coding agent and AgenKit runs a specialist team — architecture, test-first build, risk gates, review — with paper trading on by default.

in Claude Code, Cursor, Codex & 4 more
/agenkit build a production-grade trading bot with a backtest engine, risk controls and paper trading by default from strategies/OBV-EMA-Crossover-Trend-Following-Strategy.md
  1. npx agenkit install engineering-kit in your project
  2. Paste the command into your agent and approve each phase
  3. Get tests, backtest, risk controls and a paper-trading switch

Understand it first

Before you run this, read the lesson behind the idea and how to backtest it honestly. Specs like this one are the survivors of an unknown number of trials; deflate their results accordingly.

Related specs

esc

Try , , or .

↑↓ move↵ open