Multi-Dimensional Short-Term Trend Predictor: Technical Analysis and Volatility-Adaptive Trading Strategy
FMZQuant

FMZQuant @fmzquant

Joined:
Apr 25, 2024

Multi-Dimensional Short-Term Trend Predictor: Technical Analysis and Volatility-Adaptive Trading Strategy

Publish Date: Apr 3
1 1

Image description

Image description

Overview
This strategy is a multi-dimensional short-term trend prediction approach focusing on leveraging the synergistic effects of multiple technical indicators to identify and predict short-term trend changes in financial markets. By integrating key technical analysis tools such as Simple Moving Averages (SMA), Relative Strength Index (RSI), Average Directional Index (ADX), Average True Range (ATR), Moving Average Convergence Divergence (MACD), and Stochastic Oscillator, the strategy aims to enhance the accuracy and reliability of trading signals.

Strategy Principles
The core principle of this strategy is based on synergistic technical indicator analysis and trend confirmation mechanisms. Trading signals are generated by comprehensively considering the following key factors:

  1. Short-term and long-term moving average crossovers
  2. RSI overbought/oversold states
  3. MACD line and signal line changes
  4. Stochastic oscillator momentum indicators
  5. ADX trend strength
  6. Overall market trend via 200-period moving average
  7. Recent market volatility

The strategy dynamically calculates potential entry points, stop-loss, and take-profit levels, adjusting these critical parameters based on recent market volatility to achieve risk management and trade execution.

Strategy Advantages

  1. Multi-indicator comprehensive analysis: Integrating multiple technical indicators reduces the risk of misjudgment from single indicators
  2. Dynamic risk management: ATR-based stop-loss and take-profit mechanisms adapt to market volatility
  3. Flexible timeframe: Supports trading cycles from 5 minutes to 4 hours
  4. Adaptive position sizing: Dynamically adjusts position size based on available capital and per-trade risk percentage
  5. Trend strength confirmation: Using ADX to validate trend effectiveness, avoiding frequent trading in ranging markets

Strategy Risks

  1. Complexity of multiple indicators may cause signal generation delays
  2. Potential contradictory signals in highly unstable market environments
  3. Backtesting results may not fully represent actual future trading performance
  4. Leveraged trading can significantly amplify losses
  5. Lack of consideration for fundamental factors and unexpected market events

Strategy Optimization Directions

  1. Introduce machine learning algorithms for dynamic indicator weight adjustment
  2. Incorporate more fundamental and sentiment indicators
  3. Develop more intelligent position management algorithms
  4. Customize personalized parameters for different markets and asset classes
  5. Integrate real-time news and social media sentiment analysis

Conclusion
This is a multi-dimensional, data-driven short-term trend prediction strategy that aims to improve trading decision accuracy and reliability through complex technical indicator combinations and dynamic risk management mechanisms. Despite its theoretical advantages, practical application requires caution and continuous backtesting and optimization.

Strategy source code

/*backtest
start: 2024-03-31 00:00:00
end: 2025-03-29 08:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT"}]
*/

// © HugoMora
//@version=5
strategy("Short-Term Trend Predictor: Call/Put (5min to 4hr)", overlay=true)

// --- 1. Parameters (Inputs) ---
// Info-bulle pour short_length
short_length = input.int(5, title="Short SMA Period", minval=1, tooltip = "Short moving average period. Used to identify short-term trends.")
long_length = input.int(13, title="Long SMA Period", minval=1, tooltip = "Long moving average period. Used to confirm longer-term trends.")
rsi_length = input.int(14, title="RSI period", minval=1, tooltip = "Relative Strength Index (RSI) period. Measures the speed and magnitude of recent price movements to assess overbought or oversold conditions.")
sma200_length = input.int(200, title="SMA 200 period", minval=1, tooltip = "Period of the 200-period moving average. Used to determine the overall market trend (bullish or bearish).")
resistance_length = input.int(20, title="Resistance Period", minval=1, tooltip="Number of periods used to calculate resistance and support levels.")
macd_fast = input.int(12, title="MACD Fast Length", tooltip = "Shorter period used in calculating the MACD line. Represents short-term price movements.")
macd_slow = input.int(26, title="MACD Slow Length", tooltip = "Longer period used in calculating the MACD line. Represents long-term price movements.")
macd_signal = input.int(9, title="MACD Signal Period", tooltip = "SMA period of the MACD line, used as a buy or sell signal.")
stoch_k = input.int(14, title="Length %K Stoch", tooltip = "Time period used to calculate the Stochastic Oscillator %K line, indicating where the closing price is relative to the high-low range over that time period.")
stoch_d = input.int(3, title="Length %D Stoch", tooltip = "Period used to calculate the %D line, which is a moving average of %K. Used to smooth out fluctuations in %K.")
adx_length = input.int(14, title="ADX length", tooltip = "Period used to calculate the Average Directional Index (ADX), which measures trend strength.")
adx_smooth = input.int(14, title="ADX Smoothing", tooltip = "Period used to smooth the ADX line, reducing noise and providing a more stable measure of trend strength.")
atrLength = input.int(14, title="ATR length", tooltip = "Period used to calculate the Average True Range (ATR), which measures market volatility.")
atrMultiplierSL = input.float(2.0, title="ATR Stop Loss Multiplier", minval=0.1, tooltip = "ATR multiplier to determine the Stop Loss level. A higher multiplier means a Stop Loss further from the entry price.")
atrMultiplierTP = input.float(2.0, title="ATR Take Profit Multiplier", minval=0.1, tooltip = "ATR multiplier to determine the Take Profit level. A higher multiplier means a Take Profit further from the entry price.")
takeProfitRiskRatio = input.float(2.0, title="Ratio Take Profit/Risk", minval=0.1, tooltip = "Take Profit to Risk (Stop Loss) Ratio. For example, a ratio of 2 means that the Take Profit is twice as far away as the Stop Loss.")
volatility_lookback = input.int(20, title="Recent Volatility (Periods)", minval=5, tooltip="Number of periods used to calculate recent market volatility, measured by the standard deviation of closing prices.") // Parameter for volatility
initialCapital = input.float(100, title="Capital Initial (€)", minval=1, tooltip = "Initial capital used for backtesting the strategy.") // Initial capital of €100
riskPerTrade = input.float(1.0, title="Risk per Trade (%)", minval=0.01, maxval=1.0, tooltip = "Percentage of initial capital risked per trade. Used to calculate position size. "// Risk is 100% of available capital
leverage = input.int(1, title="Lever", minval=1, tooltip = "Leverage applied to trades. Multiplies profits and losses.")   // Add leverage input

// --- 2. Calculation of Indicators and Resistances ---
sma_short = ta.sma(close, short_length)
sma_long = ta.sma(close, long_length)
rsi = ta.rsi(close, rsi_length)
sma200 = ta.sma(close, sma200_length)
isUptrend = ta.rising(sma200, 5)
isDowntrend = ta.falling(sma200, 5)
resistance_high = ta.highest(high, resistance_length)
resistance_low = ta.lowest(low, resistance_length)
[macdLine, signalLine, hist] = ta.macd(close, macd_fast, macd_slow, macd_signal)
stochK = ta.stoch(close, high, low, stoch_k)
stochD = ta.sma(stochK, stoch_d)
[diPlus, diMinus, adx] = ta.dmi(adx_length, adx_smooth)
atrValue = ta.atr(atrLength)

// Calculation of recent volatility (standard deviation of prices)
recentVolatility = ta.stdev(close, volatility_lookback)

// --- 3. Crossing Detection and Conditions ---
short_above_long = sma_short > sma_long and macdLine > signalLine and stochK > stochD and adx > 25 and diPlus > diMinus
short_below_long = sma_short < sma_long and macdLine < signalLine and stochK < stochD and adx > 25 and diMinus > diPlus // Modified stochK < stochD
long_enter = ta.crossover(sma_short,sma_long)
short_enter = ta.crossunder(sma_short,sma_long)

// --- 4. Trend Analysis and Recommendations and Potential Entry/Exit Points ---
var string tendance = ""
var color tendance_couleur = color.gray //Neutral color by default
var float potentialEntry = na
var float potentialStopLoss = na
var float potentialTakeProfit = na

if (isUptrend and short_above_long)
    tendance := "Haussier Fort (Call)"
    tendance_couleur := color.green
    potentialEntry := high
    // Using Recent Volatility for Stop Loss and Take Profit
    potentialStopLoss := potentialEntry - (recentVolatility * atrMultiplierSL)
    potentialTakeProfit := potentialEntry + (recentVolatility * atrMultiplierTP)

else if (isUptrend and not short_above_long and not short_below_long) // Added check to prevent both conditions being true
    tendance := "Moderate Bullish (Call)"
    tendance_couleur := color.lime
    potentialEntry := high
    potentialStopLoss := potentialEntry - (recentVolatility * atrMultiplierSL)
    potentialTakeProfit := potentialEntry + (recentVolatility * atrMultiplierTP)

else if (isDowntrend and short_below_long)
    tendance := "Strong Bearish (Put)"
    tendance_couleur := color.red
    potentialEntry := low
    potentialStopLoss := potentialEntry + (recentVolatility * atrMultiplierSL)
    potentialTakeProfit := potentialEntry - (recentVolatility * atrMultiplierTP)

else if (isDowntrend and not short_below_long and not short_above_long) // Added check to prevent both conditions being true
    tendance := "Moderate Bearish (Put)"
    tendance_couleur := color.orange
    potentialEntry := low
    potentialStopLoss := potentialEntry + (recentVolatility * atrMultiplierSL)
    potentialTakeProfit := potentialEntry - (recentVolatility * atrMultiplierTP)

else
    tendance := "Neutral"
    tendance_couleur := color.gray
    potentialEntry := na
    potentialStopLoss := na
    potentialTakeProfit := na

// --- 5. Trend Display (Persistent Label) ---
var label tendance_label = na

if (barstate.islast)
    if (na(tendance_label))
        tendance_label := label.new(bar_index, high, text="Tendance: " + tendance, color=tendance_couleur, textcolor=color.white, style=label.style_label_down, yloc=yloc.abovebar, y=high*1.02)
        label.set_tooltip(tendance_label, "Current market trend")  // Adding the tooltip to the label
    else
        label.set_text(tendance_label, "Tendance: " + tendance)
        label.set_x(tendance_label, bar_index)
        label.set_y(tendance_label, high*1.02)
        label.set_color(tendance_label, tendance_couleur)
        label.set_tooltip(tendance_label, "Current market trend")
// --- 6. Information Board ---
var table info_table = table.new(position.top_right, 2, 12, border_width=1) // Adding a line for capital
var float currentCapital = initialCapital // Declare currentCapital here

table.cell(info_table, 0, 0, "Indicator", bgcolor=color.gray, text_color=color.white)
table.cell(info_table, 1, 0, "Value", bgcolor=color.gray, text_color=color.white)
table.cell(info_table, 0, 1, "Short SMA", bgcolor=color.white, text_color=color.black)
table.cell(info_table, 1, 1, str.tostring(sma_short, "#.##"), bgcolor=color.white, text_color=color.black)
table.cell_set_tooltip(info_table, 0, 1, "Short moving average")
table.cell_set_tooltip(info_table, 1, 1, str.tostring(sma_short, "#.##"))

table.cell(info_table, 0, 2, "SMA Longue", bgcolor=color.white, text_color=color.black)
table.cell(info_table, 1, 2, str.tostring(sma_long, "#.##"), bgcolor=color.white, text_color=color.black)
table.cell_set_tooltip(info_table, 0, 2, "Long moving average")
table.cell_set_tooltip(info_table, 1, 2, str.tostring(sma_long, "#.##"))

table.cell(info_table, 0, 3, "RSI", bgcolor=color.white, text_color=color.black)
table.cell(info_table, 1, 3, str.tostring(rsi, "#.##"), bgcolor=color.white, text_color=color.black)
table.cell_set_tooltip(info_table, 0, 3, "Relative Strength Index")
table.cell_set_tooltip(info_table, 1, 3, str.tostring(rsi, "#.##"))

table.cell(info_table, 0, 4, "Tendance", bgcolor=color.white, text_color=color.black)
table.cell(info_table, 1, 4, tendance, bgcolor=color.white, text_color=color.black)
table.cell_set_tooltip(info_table, 0, 4, "Current market trend")
table.cell_set_tooltip(info_table, 1, 4, tendance)

table.cell(info_table, 0, 5, "Potential Entry Point", bgcolor=color.white, text_color=color.black)
table.cell(info_table, 1, 5, str.tostring(potentialEntry, "#.##"), bgcolor=color.white, text_color=color.black)
table.cell_set_tooltip(info_table, 0, 5, "Suggested entry point for a trade")
table.cell_set_tooltip(info_table, 1, 5, str.tostring(potentialEntry, "#.##"))

table.cell(info_table, 0, 6, "Stop Loss Potentiel", bgcolor=color.white, text_color=color.black)
table.cell(info_table, 1, 6, str.tostring(potentialStopLoss, "#.##"), bgcolor=color.white, text_color=color.black)
table.cell_set_tooltip(info_table, 0, 6, "Price level at which to automatically close a position to limit losses")
table.cell_set_tooltip(info_table, 1, 6, str.tostring(potentialStopLoss, "#.##"))

table.cell(info_table, 0, 7, "Take Profit Potentiel", bgcolor=color.white, text_color=color.black)
table.cell(info_table, 1, 7, str.tostring(potentialTakeProfit, "#.##"), bgcolor=color.white, text_color=color.black)
table.cell_set_tooltip(info_table, 0, 7, "Price level at which to automatically close a position to lock in profits")
table.cell_set_tooltip(info_table, 1, 7, str.tostring(potentialTakeProfit, "#.##"))

table.cell(info_table, 0, 8, "High Resistance", bgcolor=color.white, text_color=color.red)
table.cell(info_table, 1, 8, str.tostring(resistance_high, "#.##"), bgcolor=color.white, text_color=color.red)
table.cell_set_tooltip(info_table, 0, 8, "Price level at which selling pressure is expected to outweigh buying pressure")
table.cell_set_tooltip(info_table, 1, 8, str.tostring(resistance_high, "#.##"))

table.cell(info_table, 0, 9, "Low Resistance", bgcolor=color.white, text_color=color.green)
table.cell(info_table, 1, 9, str.tostring(resistance_low, "#.##"), bgcolor=color.white, text_color=color.green)
table.cell_set_tooltip(info_table, 0, 9, "Price level at which buying pressure is expected to outweigh selling pressure")
table.cell_set_tooltip(info_table, 1, 9, str.tostring(resistance_low, "#.##"))

table.cell(info_table, 0, 10, "ATR", bgcolor=color.white, text_color=color.black)
table.cell(info_table, 1, 10, str.tostring(atrValue, "#.##"), bgcolor=color.white, text_color=color.black)
table.cell_set_tooltip(info_table, 0, 10, "Average True Range")
table.cell_set_tooltip(info_table, 1, 10, str.tostring(atrValue, "#.##"))

table.cell(info_table, 0, 11, "Recent Volatility", bgcolor=color.white, text_color=color.black)
table.cell(info_table, 1, 11, str.tostring(recentVolatility, "#.##"), bgcolor=color.white, text_color=color.black)
table.cell_set_tooltip(info_table, 0, 11, "Market volatility calculated over the last periods")
table.cell_set_tooltip(info_table, 1, 11, str.tostring(recentVolatility, "#.##"))
// --- 7. Plotting Indicators and Resistances ---
plot(sma_short, color=color.blue, title="SMA Courte")
plot(sma_long, color=color.orange, title="SMA Longue")
plot(sma200, color=color.yellow, title="SMA 200", linewidth=2)
plot(resistance_high, color=color.red, title="High Resistance", linewidth=2, style=plot.style_linebr)
plot(resistance_low, color=color.green, title="Lower Resistance (Support)", linewidth=2, style=plot.style_linebr)

// --- 8. Plotting Potential Entry and Exit Points ---
plotshape(isUptrend and short_above_long, title="Potential Call Entry", style=shape.triangleup, location=location.belowbar, color=color.green, size=size.small, offset=0)
plotshape(isDowntrend and short_below_long, title="Entrée Put Potentielle", style=shape.triangledown, location=location.abovebar, color=color.red, size=size.small, offset=0)

plot(potentialStopLoss, color=color.red, title="Stop Loss Potentiel", linewidth=1, style=plot.style_linebr)
plot(potentialTakeProfit, color=color.green, title="Take Profit Potentiel", linewidth=1, style=plot.style_linebr)

// --- 9. Adding Curve Names ---
if barstate.islast
    var label_sma_short = label.new(bar_index, sma_short, "Short SMA", color=color.blue, style=label.style_label_left)
    label.set_tooltip(label_sma_short, "Short Moving Average")
    var label_sma_long = label.new(bar_index, sma_long, "Long SMA", color=color.orange, style=label.style_label_left)
    label.set_tooltip(label_sma_long, "Long Moving Average")
    var label_sma200 = label.new(bar_index, sma200, "SMA 200", color=color.yellow, style=label.style_label_left)
    label.set_tooltip(label_sma200, "Moving average 200")
    var label_resistance_high = label.new(bar_index, resistance_high, "Résistance Haute", color=color.red, style=label.style_label_left)
    label.set_tooltip(label_resistance_high, "High Resistance Level")
    var label_resistance_low = label.new(bar_index, resistance_low, "Résistance Basse", color=color.green, style=label.style_label_left)
    label.set_tooltip(label_resistance_low, "Low Resistance Level")

// --- 10. Backtest ---
var int tradesCount = 0
var int winningTradesCount = 0
var int losingTradesCount = 0
var float totalProfit = 0.0
var float maxDrawdown = 0.0
var float previousPeak = initialCapital
var int currentPosition = 0 // 0: no position, 1: long (Call), -1: short (Put)
var float entryPrice = 0.0
var float positionSize = 0.0
var float stopLossPrice = 0.0
var float takeProfitPrice = 0.0
var int entryDate = 0
var int exitDate = 0
var float exitPrice = 0.0 // Declare exitPrice here
var float tradeProfit = 0.0
currentCapital := initialCapital // Reset capital at the beginning of the backtest

if (currentPosition == 0) // No open position
    if (isUptrend and short_above_long)
        // Enter a long position (Call)
        tradesCount := tradesCount + 1
        entryPrice := potentialEntry
        positionSize := math.floor((currentCapital * riskPerTrade * leverage) / (entryPrice - potentialStopLoss)) // Position size based on risk
        if (positionSize > 0) // check if positionSize is valid
            currentPosition := 1
            stopLossPrice := potentialStopLoss
            takeProfitPrice := potentialTakeProfit
            entryDate := time
            strategy.entry("Call", strategy.long, qty=positionSize, comment="Call Entry", stop=stopLossPrice, limit=takeProfitPrice) // Utilisez strategy.entry
    else if (isDowntrend and short_below_long)
        // Enter a short position (Put)
        tradesCount := tradesCount + 1
        entryPrice := potentialEntry
        positionSize := math.floor((currentCapital * riskPerTrade * leverage) / (potentialStopLoss - entryPrice)) // Position size based on risk
        if (positionSize > 0) // check if positionSize is valid
            currentPosition := -1
            stopLossPrice := potentialStopLoss
            takeProfitPrice := potentialTakeProfit
            entryDate := time
            strategy.entry("Put", strategy.short, qty=positionSize, comment="Put Entry", stop=stopLossPrice, limit=takeProfitPrice) // Utilisez strategy.entry

else if (currentPosition != 0) // There is an open position
    if (currentPosition == 1) // Long position (Call)
        if (low <= stopLossPrice or high >= takeProfitPrice)
            // Exit the long position
            exitPrice := low <= stopLossPrice ? stopLossPrice : takeProfitPrice
            tradeProfit := positionSize * (exitPrice - entryPrice)
            currentCapital := currentCapital + tradeProfit
            totalProfit := totalProfit + tradeProfit
            if (tradeProfit > 0)
                winningTradesCount := winningTradesCount + 1
            else
                losingTradesCount := losingTradesCount + 1
            currentPosition := 0
            exitDate := time
            strategy.close("Call", comment="Call Exit", qty=positionSize) // Utilisez strategy.close
    else if (currentPosition == -1) // Short position (Put)
        if (high >= stopLossPrice or low <= takeProfitPrice)
            // Exit the short position
            exitPrice := high >= stopLossPrice ? stopLossPrice : takeProfitPrice
            tradeProfit := positionSize * (entryPrice - exitPrice)  // Short position profit
            currentCapital := currentCapital + tradeProfit
            totalProfit := totalProfit + tradeProfit
            if (tradeProfit > 0)
                winningTradesCount := winningTradesCount + 1
            else
                losingTradesCount := losingTradesCount + 1
            currentPosition := 0
            exitDate := time
            strategy.close("Put", comment="Put Exit", qty=positionSize) // Utilisez strategy.close

// --- 11. Calculating performance metrics ---
if (currentCapital > previousPeak)
    previousPeak := currentCapital
var float drawdown = 0.0
drawdown := previousPeak - currentCapital
if (drawdown > maxDrawdown)
    maxDrawdown := drawdown

var float winRate = 0.0
var float averageProfit = 0.0
var float averageLoss = 0.0
var float profitFactor = 0.0

winRate := tradesCount > 0 ? (winningTradesCount / tradesCount) * 100 : 0
averageProfit := winningTradesCount > 0 ? totalProfit / winningTradesCount : 0
averageLoss := losingTradesCount > 0 ? totalProfit / losingTradesCount : 0
profitFactor := averageLoss != 0 ? math.abs(averageProfit / averageLoss) : 0
Enter fullscreen mode Exit fullscreen mode

Strategy parameters

Image description

Image description

The original address: Multi-Dimensional Short-Term Trend Predictor: Technical Analysis and Volatility-Adaptive Trading Strategy

Comments 1 total

  • Rebecca Chow
    Rebecca ChowJun 19, 2025

    Multi-dimensional trend prediction? So you’re basically trying to predict the market’s next mood swing—good luck with that! 😂 Love the volatility adaptation, but if this actually works consistently, I’ll donate my crypto bags to charity. Solid effort though—just needs a ‘panic mode’ for when the market decides chaos is the only strategy.

Add comment