PulseForce Dual Moving Average Strategy Analysis | PulseForce Docs

PulseForce Dual Moving Average Strategy Analysis

1. Overview

Dual Moving Average is one of the most classic and fundamental trend-following strategies.
It relies solely on the crossover relationship between two simple moving averages (short-term ma_fast and long-term ma_slow):

  • When the short MA crosses above the long MA from below → Golden Cross → generates a buy signal
  • When the short MA crosses below the long MA from above → Death Cross → generates a sell signal

In PulseForce, the dual_moving_avg strategy is a minimal yet highly standardized trend-following implementation:

  • Uses 1-minute candles (timeframe = "1m")
  • Only uses dual moving averages + volume > 0 as signal conditions
  • Exit logic follows a unified risk framework: “sell signal + daily SL/TP + hard SL/TP”
  • Supports HyperOpt optimization, searching over parameters like ma_fast / ma_slow / daily_* / force_*
  • All strategy parameters can be configured visually in the PulseForce App

If ma_crossover is a “reinforced version” with more trend filters and volatility adjustments, then dual_moving_avg is a more pure dual moving average crossover version, with a very simple structure and excellent interpretability.


2. Origin and Positioning

The dual moving average crossover strategy can be traced back to early technical analysis. With the rise of computers and systematic trading, it became the “Hello World” of trend-following strategies:

  • Early CTA / trend-following funds
    Widely used moving average crossovers as core trend signals.
  • Modern quant systems
    Dual moving averages remain a cornerstone of trend-following, embedded in multi-factor models, CTA systems, trend filters, and more.
  • Teaching and research
    Whether for beginners or academic discussion, dual MAs are almost always the default example for trend strategies.

Within PulseForce, the positioning of dual_moving_avg is:

“A structurally ultra-clean baseline trend strategy.”
It is used to compare returns, drawdowns, and trading style against more complex strategies such as MA Crossover, Momentum, MACD Trend, etc.


3. What Problem Does the Strategy Solve?

At its core, the dual moving average strategy addresses three main questions:

  1. How to identify changes in trend direction in a simple and direct way?
    → Use the relative position and crossover of the short MA against the long MA to determine bullish/bearish trend states.

  2. How to avoid over-reliance on single candles or local patterns?
    → Moving averages smooth price, reducing the impact of high-frequency noise and abnormal single candles.

  3. How to build a “tunable” trend-following strategy?
    → By adjusting ma_fast / ma_slow, it’s easy to obtain variants that are faster or more stable.


4. Core Indicators and Signal Logic

4.1 Indicator Calculation

The strategy uses simple moving averages (SMA):

1
2
3
4
5
6
f = int(self._val(self.ma_fast))
s = int(self._val(self.ma_slow))

# Simple moving averages
df["ma_fast"] = ta.SMA(df, timeperiod=f)
df["ma_slow"] = ta.SMA(df, timeperiod=s)

Required base data:

  • close / open / high / low: all forward-filled (ffill) for cleaning
  • volume: filled with 0 to avoid signal errors caused by missing data

4.2 Buy Logic: Golden Cross

1
2
3
4
5
6
7
8
9
10
def populate_buy_trend(self, dataframe: DataFrame, metadata: Dict) -> DataFrame:
df = dataframe.copy()
cond = (
(df["ma_fast"].shift(1) < df["ma_slow"].shift(1)) & # Previous bar: short MA below long MA
(df["ma_fast"] > df["ma_slow"]) & # Current bar: short MA crosses above long MA → Golden Cross
(df["volume"] > 0) # Must have volume
)
df.loc[:, "buy"] = 0
df.loc[cond, "buy"] = 1
return df

Meaning:

  • It must be a state change from “short MA below” to “short MA above”, not just a simple > check → avoids repeated triggers.
  • Uses the purest definition of a Golden Cross, without stacking RSI, breakout, volatility filters, etc. → ideal as a baseline strategy.
  • volume > 0 is used to filter out halted symbols or abnormal data.

4.3 Sell Signal Layer: Death Cross

The strategy uses a static method _has_sell_signal to determine if a Death Cross has occurred:

1
2
3
4
5
6
7
8
9
@staticmethod
def _has_sell_signal(prev: Optional[pd.Series], curr: Optional[pd.Series]) -> bool:
try:
if prev is None or curr is None:
return False
return (float(prev.get("ma_fast", np.nan)) > float(prev.get("ma_slow", np.nan))) and \
(float(curr.get("ma_fast", np.nan)) < float(curr.get("ma_slow", np.nan)))
except Exception:
return False

Conditions:

  • Previous bar: ma_fast > ma_slow (short-term trend still above long-term trend)
  • Current bar: ma_fast < ma_slow (short-term trend breaks below the long-term MA)

When both conditions are met, this is considered a Death Cross (bullish → bearish trend reversal).
However, the strategy does not exit immediately; instead, it passes this signal to custom_exit, which then decides whether to exit based on SL/TP logic.


5. Unified Exit Logic: Sell Signal + SL/TP

In custom_exit, dual_moving_avg implements the full PulseForce-standard risk control process:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def custom_exit(
self,
pair: str,
trade: Trade,
current_time: datetime,
current_rate: float,
current_profit: float,
**kwargs,
):
# ① Hard thresholds first (Force SL/TP)
if current_profit <= -self.force_stop_loss.value:
return "hard_sl"
if current_profit >= self.force_take_profit.value:
return "hard_tp"

# ② Fetch prev/curr rows and check for Death Cross
df = self._get_analyzed_df(pair)
if df is None or df.empty:
return None
prev, curr = self._get_prev_curr_rows(df, current_time)
if not self._has_sell_signal(prev, curr):
return None

# ③ Only after the sell signal layer occurs, apply daily SL/TP to decide the actual exit
th = self._pick_trend_thresholds()
sl = th["sl"]
tp = th["tp"]
if current_profit <= -sl:
return "sl_signal"
if current_profit >= tp:
return "tp_signal"
return None

This can be summarized as three layers of protection:

  1. Force SL/TP (hard thresholds)

    • Ignores all signals: as long as unrealized loss/profit reaches the hard threshold, the position is closed immediately.
    • Used to protect against extreme market moves (flash crashes, spikes, etc.).
  2. Sell signal layer (Death Cross)

    • If no Death Cross is present, the trade will not exit purely due to daily SL/TP.
    • Reflects the idea: “As long as the trend is intact, try to keep holding.”
  3. Daily SL/TP (trend-based exit)

    • Only when a Death Cross has occurred will daily_stop_loss / daily_take_profit be applied to decide whether to exit.
    • This is more aligned with trend-following logic than pure PnL-based exits.

6. PulseForce Parameters and HyperOpt Optimization

PulseForce App(available on Apple App Store & Google Play)provides a visual configuration interface for trading tasks, backtests, and hyper-parameter optimization, making the strategy fully parameterized.

Backtest configuration UI:

HyperOpt configuration UI:

The following parameters can be optimized per symbol to find the best-performing combination:

6.1 Basic Parameters

These determine the shape of the trend signal itself:

Param Key Name Description Optimizable Suggested initial search range (otp_init_min ~ otp_init_max)
ma_fast Fast MA Period Short-term MA period; smaller values react faster 8 ~ 25
ma_slow Slow MA Period Long-term MA period; larger values are smoother 40 ~ 90

They map directly to the HyperOpt space in code:

1
2
ma_fast = IntParameter(3, 100, default=10, space="buy", optimize=True)
ma_slow = IntParameter(5, 200, default=30, space="buy", optimize=True)

You can use HyperOpt within PulseForce to automatically search for the most suitable fast/slow MA periods for each symbol.


6.2 Capital Allocation

Capital allocation parameters are consistent with other strategies.
They control maximum capital usage and trend-dependent exposure. They are not optimized by default but can be adjusted via the UI:

Param Key Description
max_funds_allowed_using Maximum capital allocated to this strategy
allowable_funds_neutral Capital allocation when the market is neutral
allowable_funds_uptrend Capital allocation during uptrend
allowable_funds_downtrend Capital allocation during downtrend

6.3 Stop Loss Parameters

Stop-loss parameters support HyperOpt optimization:

Param Key Name Meaning Optimizable Suggested Range
force_stop_loss Force Stop-Loss Ratio Hard stop-loss threshold 0.005 ~ 0.30
daily_stop_loss Daily Stop-Loss Ratio Daily stop-loss, only active after a sell signal (Death Cross) 0.01 ~ 0.3

6.4 Take Profit Parameters

Take-profit parameters are also fully optimizable:

Param Key Name Meaning Optimizable Suggested Range
force_take_profit Force Take-Profit Ratio Hard take-profit threshold 0.005 ~ 0.30
daily_take_profit Daily Take-Profit Ratio Daily take-profit, only active after a sell signal 0.01 ~ 0.3

By combining hard TP with daily TP, you can build multi-layer profit protection mechanisms.


7. When to Use Dual MA

7.1 Best-suited Scenarios

  • Assets with clear trend characteristics:
    such as hot US growth stocks, trending ETFs, major indices, and volatile cryptocurrencies.
  • Users who prefer simple rules + clean signals:
    no reliance on multiple indicators, lower risk of overfitting.
  • Research setups that need a baseline strategy:
    for comparing excess returns of MA Crossover, MACD, Momentum, etc.

7.2 Less Suitable Scenarios

  • Symbols that stay in long-term sideways ranges with frequent whipsaws
  • Extremely high-frequency noise environments (e.g., ultra-short-term random moves)
  • Use cases requiring very precise entries (dual MA tends to capture the “middle of the trend” rather than exact turning points)

8. Strengths and Weaknesses

Strengths

  • Extremely simple and transparent structure, highly interpretable
  • Very few parameters → HyperOpt optimization is more stable and less prone to overfitting
  • Ideal for:
    • Baseline strategy (Benchmark Strategy)
    • Teaching and onboarding
    • Comparing against more complex strategies
  • Fully adheres to the PulseForce risk framework:
    • Hard SL/TP thresholds
    • Daily SL/TP
    • Unified custom_exit process

Weaknesses

  • Does not consider RSI, volume, volatility, etc. → more sensitive to choppy markets, prone to false signals
  • Compared with more advanced trend strategies (e.g., Momentum with AI filters), performance on some symbols may be slightly inferior
  • No explicit daily trend filters; in extremely noisy environments, it may need to be combined with additional filters

9. Summary

dual_moving_avg is the purest and most standard dual moving average crossover implementation within PulseForce:

  • Uses Golden Cross / Death Cross of short and long MAs as the sole trend signal
  • Applies a unified risk framework with hard thresholds + daily stop-loss/take-profit
  • Supports HyperOpt over ma_fast / ma_slow / daily_* / force_*
  • Through UI configuration + backtesting + hyper-optimization, users can quickly build their own preferred style of dual MA strategy

It is especially suitable as:

  • Your first trend-following strategy
  • A baseline to compare against Momentum / MA Crossover / MACD Trend and other strategies
  • An example strategy to learn the full PulseForce pipeline: strategy → parameters → risk control → backtest → HyperOpt

To learn more about strategies and the latest features, visit:
👉 PulseForce Official Website: https://pulse.hiforce.ai