Sharwan Patel

Weekly Momentum Rebalance Strategy for Gold and Silver Trading

Trading Strategy


If you trade precious metals and want a low-maintenance, rules-based, long-only algorithmic strategy for Gold (XAUUSD) and Silver (XAGUSD), this post is for you. Today I share a complete weekly momentum rotation strategy built in Python for the ALGOGENE platform. It runs on daily data, rebalances once per week, only takes long positions, and is easy to understand, modify, and backtest.


What Is This Strategy?

This is a relative momentum rotation strategy focused on the two most liquid precious metals: Gold and Silver. The core idea is simple:

  • Calculate the 20-day price momentum for both XAUUSD and XAGUSD
  • Each week, select and hold the metal with stronger positive momentum
  • If both have negative momentum, stay in cash (no positions)
  • Fully automated: no short selling, weekly rebalance only, minimal monitoring

It is designed for trend-following in precious metals, reduces whipsaws by using weekly frequency, and avoids directional bias by picking the stronger asset each week.



Strategy Logic & Rules

This strategy runs on daily data (1D interval) and uses position netting = False for clean execution. Here are the clear, step-by-step rules:


Core Parameters

  • Instruments: XAUUSD (Gold), XAGUSD (Silver)
  • Trading mode: Long only (short selling = False)
  • Rebalance frequency: Once per calendar week
  • Momentum lookback: 20 trading days (from 25-day historical bars)
  • Position size: Fixed 0.01 lot per trade
  • Data interval: Daily (1D) bars

Trading Rules

  1. Weekly Check: The strategy runs only once per week to avoid over-trading.
  2. Momentum Calculation: Compute momentum as (Current Close − Close 20 days ago) / Close 20 days ago.
  3. Asset Selection:
    • If both Gold and Silver have negative momentum: hold cash, close all positions.
    • If Gold momentum > Silver momentum: go long XAUUSD.
    • If Silver momentum > Gold momentum: go long XAGUSD.
  4. Rebalance Mechanism:
    • First close all positions in the non-target metal.
    • After closing completes, open a long position in the target metal.
    • If already holding the correct asset, do nothing.

This structure keeps turnover low, transaction costs minimal, and logic transparent for debugging and optimization


How It Works (Step-by-Step)


Step 1: Initialize Strategy & Settings

We define the target instruments, fixed trading volume, and weekly rebalance tracking variables. The strategy uses ALGOGENE’s native API for order management and historical data.


Step 2: Compute 20-Day Price Momentum

The strategy pulls 25 daily historical bars and calculates the 20-day price momentum—a standard trend indicator that measures the strength and direction of the recent price move.


Step 3: Weekly Rebalance Logic

Each new calendar week, it compares momentum scores between Gold and Silver. It then updates the target instrument and triggers the rebalance function.


Step 4: Automated Position Rebalancing

The rebalance() method first closes unwanted positions. After a close order fills, it continues rebalancing to open the target metal. This ensures the portfolio always matches the weekly signal.


Full Python Source Code for ALGOGENE

  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
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
from AlgoAPI import AlgoAPIUtil, AlgoAPI_Backtest


class AlgoEvent:
    def __init__(self):
        self.gold = "XAUUSD"
        self.silver = "XAGUSD"
        self.volume = 0.01

        self.last_week = None
        self.target = None

    def start(self, mEvt):
        self.evt = AlgoAPI_Backtest.AlgoEvtHandler(self, mEvt)
        self.evt.start()

    def on_bulkdatafeed(self, isSync, bd, ab):
        if not isSync:
            return

        timestamp = bd[self.gold]["timestamp"]
        week = timestamp.isocalendar()[:2]

        # Run once every calendar week
        if week == self.last_week:
            return

        gold_momentum = self.momentum(self.gold, timestamp)
        silver_momentum = self.momentum(self.silver, timestamp)

        if gold_momentum is None or silver_momentum is None:
            return

        self.last_week = week

        self.evt.consoleLog(
            "Weekly rebalance:",
            timestamp,
            "XAUUSD momentum=",
            round(gold_momentum, 6),
            "XAGUSD momentum=",
            round(silver_momentum, 6)
        )

        # Both weak: target is cash
        if gold_momentum <= 0 and silver_momentum <= 0:
            self.target = None

        # Hold the stronger metal
        elif gold_momentum > silver_momentum:
            self.target = self.gold

        elif silver_momentum > gold_momentum:
            self.target = self.silver

        else:
            return

        self.rebalance()

    def momentum(self, instrument, timestamp):
        bars = self.evt.getHistoricalBar(
            contract={"instrument": instrument},
            numOfBar=25,
            interval="D",
            timestamp=timestamp
        )

        if bars is None or len(bars) < 25:
            return None

        close = [bars[t]["c"] for t in bars]

        if close[-20] == 0:
            return None

        return (close[-1] - close[-20]) / close[-20]

    def rebalance(self):
        """
        Requires Position Netting = False.

        Close all unwanted trades first.
        Once each close is completed, on_orderfeed() calls rebalance()
        again until the portfolio matches self.target.
        """
        pos, orders, pending = self.evt.getSystemOrders()

        # Close positions when target is cash,
        # or close positions in the non-target metal.
        for tradeID in orders:
            instrument = orders[tradeID]["instrument"]

            if self.target is None or instrument != self.target:
                self.close_order(tradeID)
                return

        # No target means all positions have been closed.
        if self.target is None:
            self.evt.consoleLog("Portfolio is in cash.")
            return

        # Do nothing if already holding the target instrument.
        for tradeID in orders:
            if orders[tradeID]["instrument"] == self.target:
                self.evt.consoleLog("Already holding:", self.target)
                return

        # Portfolio is flat: open the selected metal.
        self.open_order(self.target)

    def open_order(self, instrument):
        order = AlgoAPIUtil.OrderObject(
            instrument=instrument,
            orderRef="momentum",
            openclose="open",
            buysell=1,
            ordertype=0,
            volume=self.volume
        )

        self.evt.consoleLog("Opening long position:", instrument)
        self.evt.sendOrder(order)

    def close_order(self, tradeID):
        order = AlgoAPIUtil.OrderObject(
            tradeID=tradeID,
            openclose="close"
        )

        self.evt.consoleLog("Closing tradeID:", tradeID)
        self.evt.sendOrder(order)

    def on_orderfeed(self, of):
        self.evt.consoleLog(
            "Order update:",
            of.status,
            of.openclose,
            of.instrument,
            of.tradeID
        )

        # After an old trade is closed, continue the rotation.
        if of.status == "success" and of.openclose == "close":
            self.rebalance()

    def on_marketdatafeed(self, md, ab):
        pass

    def on_openPositionfeed(self, op, oo, uo):
        pass

    def on_dailyPLfeed(self, pl):
        pass

Backtest Setup

To test this strategy on ALGOGENE, use these settings:

  • Instruments: XAUUSD, XAGUSD
  • Data Interval: 1 day (Daily)
  • Short Selling: Disabled (False)
  • Position Netting: Disabled (False)
  • Initial Capital: Set based on your risk management

Key Advantages of This Strategy

  • Low Effort: Weekly rebalance only, no daily monitoring
  • Long Only: No short exposure, suitable for conservative precious metals traders
  • Relative Strength: Avoids single-asset drawdowns by rotating to the stronger metal
  • Transparent: Simple momentum calculation, easy to modify and optimize

How to Use & Improve

  • Adjust the momentum lookback period (try 14, 20, 25 days)
  • Modify the position size based on your risk tolerance
  • Add a volatility filter (e.g., ATR) to skip high-volatility weeks
  • Extend to more precious metals (e.g., Platinum XPTUSD)

Wrap Up

This weekly momentum rotation strategy provides a robust, rule-based way to trade Gold and Silver without emotional decisions or short-selling. It is beginner-friendly, algorithmically clean, and optimized for ALGOGENE’s backtesting and live trading environment.

Try backtesting it with your preferred historical period, tweak the parameters, and share your results in the comments below.

Happy backtesting & profitable trading!