Quant BuffetRelax, Not Over Thinking

Mercury Retrograde Astrology Trading Strategy in Chinese Market

Log in to collect

Academic paper

Mercury, Mood, and Mispricing: A Natural Experiment in the Chinese Stock Market

AuthorsShubo Kou; Xiyuan Ma

Institute
  • Tsinghua University
  • ?Tsinghua University, PBC School of Finance, Students
  • SGSingapore Management University
  • ?Singapore Management University - Lee Kong Chian School of Business

Strategy in a nutshell

Universe: CSI300 index (futures, ETF, or CFD). Trade based on Mercury retrograde cycles: short one week after retrograde starts, cover and go long five days after retrograde ends, hold until the next retrograde onset. Strategy uses full capital (100%) and is dynamically rebalanced.

Economic rationale

Behavioral finance explains the pattern: high retail participation in China leads to excess volatility and overtrading. Mercury retrograde correlates with increased selling and loss aversion, reflecting superstition-driven investor behavior.

Backtest performance

Annualised return22.12%
Volatility40.28%
Beta0.039
Sharpe ratio0.55
Win rate57%

Full Python code

from AlgorithmImports import *
import data_tools
from pandas.tseries.offsets import BDay
# endregion

class MercuryRetrogradeAstrologyTradingStrategyinChineseMarket(QCAlgorithm):

def Initialize(self):
self.SetStartDate(2000, 1, 1)
self.SetCash(100000)

self.leverage:int = 5
self.lag:int = 5
self.counter:Union[None, int] = None
self.trade_direction:Union[None, int] = None

# load custom data
self.mercury_retrograde_calendar:Symbol = self.AddData(data_tools.MercuryRetrogradeCalendar, 'mercury_retrograde_calendar', Resolution.Daily).Symbol
self.market:Symbol = self.AddData(data_tools.QuantpediaCSI300, 'CSI_300', Resolution.Daily).Symbol
self.Securities[self.market].SetLeverage(self.leverage)
self.Securities[self.market].SetFeeModel(data_tools.CustomFeeModel())

def OnData(self, data: Slice):
market_last_update_date:datetime.date = data_tools.QuantpediaCSI300._last_update_date
calendar_last_update_date:datetime.date = data_tools.MercuryRetrogradeCalendar._last_update_date

# check if data is still coming
if self.Securities[self.market].GetLastData() and self.Time.date() >= market_last_update_date \
    or self.Securities[self.mercury_retrograde_calendar].GetLastData() and self.Time.date() >= calendar_last_update_date:
    self.Liquidate()
    return

if self.counter is not None:
    if self.market in data and data[self.market]:
        self.counter += 1
        if self.counter == self.lag:
            self.SetHoldings(self.market, self.trade_direction)
            self.trade_direction = None

if self.mercury_retrograde_calendar in data and data[self.mercury_retrograde_calendar] and self.Securities[self.market].GetLastData():
    if self.trade_direction is None:
        self.trade_direction = -1 if data[self.mercury_retrograde_calendar].status == 'start' else 1
        self.counter = 0