| Overall Statistics |
|
Total Orders 345 Average Win 1.70% Average Loss -1.35% Compounding Annual Return 1.866% Drawdown 10.400% Expectancy 0.137 Start Equity 100000 End Equity 137850.30 Net Profit 37.850% Sharpe Ratio -0.099 Sortino Ratio -0.116 Probabilistic Sharpe Ratio 0.047% Loss Rate 50% Win Rate 50% Profit-Loss Ratio 1.25 Alpha -0.003 Beta -0.007 Annual Standard Deviation 0.04 Annual Variance 0.002 Information Ratio -0.406 Tracking Error 0.171 Treynor Ratio 0.572 Total Fees $593.30 Estimated Strategy Capacity $170000000.00 Lowest Capacity Asset IWM RV0PWMLXVHPH Portfolio Turnover 1.17% |
# https://quantpedia.com/strategies/insights-from-the-geopolitical-sentiment-index-made-with-google-trends/
#
# The investment universe for this strategy consists of two key ETFs: the iShares Russell 2000 ETF (IWM), which represents small-cap stocks, and the SPDR S&P
# 500 ETF Trust (SPY), which represents large-cap stocks.
# Rationale: These instruments are selected based on the research paper’s focus on the performance differential between small-cap and large-cap stocks in
# response to changes in geopolitical sentiment.
# Variable Construction: This strategy’s primary tool is the Geopolitical Sentiment Index (GSI), constructed from Google Trends data. The GSI measures public
# interest in geopolitical issues using keywords such as war, conflict, military, etc. The methodology involves calculating the 12-month percentage change in
# the GSI to generate trading signals.
# Trading: The buy and sell rules are as follows:
# When the 12-month GSI percentage change is positive (indicating rising geopolitical stress), go long on IWM and short on SPY.
# When the 12-month GSI percentage change is negative (indicating declining geopolitical stress), go short on IWM and long on SPY.
# The strategy involves rebalancing the portfolio at the end of each month to reflect changes in the GSI. The weight of individual securities is in a (equal)
# 1:1 ratio (50% / 50%).
# region imports
from AlgorithmImports import *
from dateutil.relativedelta import relativedelta
from typing import List, Tuple
from pandas.core.frame import DataFrame
# endregion
class InsightsFromTheGeopoliticalSentimentIndexIndexMadeWithGoogleTrends(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2008, 1, 1)
self.set_cash(100_000)
self._period: int = 12
tickers: List[str] = ['SPY', 'IWM']
self._traded_assets: List[Symbol] = [
self.add_equity(ticker, Resolution.DAILY).symbol for ticker in tickers
]
self._GSI: Symbol = self.add_data(GeopoliticalSentimentIndex, 'GSI', Resolution.DAILY).symbol
self._selection_flag: bool = False
self.settings.daily_precise_end_time = False
self.settings.minimum_order_margin_portfolio_percentage = 0.
self.schedule.on(self.date_rules.month_end(self._traded_assets[0]),
self.time_rules.after_market_open(self._traded_assets[0]),
self.selection)
def on_data(self, slice: Slice) -> None:
# monthly rebalance
if not self._selection_flag:
return
self._selection_flag = False
if self.securities[self._GSI].get_last_data() and self.time.date() > GeopoliticalSentimentIndex.get_last_update_date():
self.liquidate()
return
history: DataFrame = self.history(self._GSI, start = self.time.date() - relativedelta(months=self._period), end=self.time.date())
if len(history) == self._period:
GSI_diff: float = (history.iloc[-1] / history.iloc[0] - 1).values[0]
trade_directions: Tuple[int] = (-1, 1) if GSI_diff > 0 else (1, -1)
# trade execution
targets: List[PortfolioTarget] = []
for i, symbol in enumerate(self._traded_assets):
if slice.contains_key(symbol) and slice[symbol]:
targets.append(PortfolioTarget(symbol, trade_directions[i] / len(self._traded_assets)))
self.set_holdings(targets, True)
def selection(self) -> None:
self._selection_flag = True
class GeopoliticalSentimentIndex(PythonData):
_last_update_date: datetime.date = datetime(1,1,1).date()
@staticmethod
def get_last_update_date() -> Dict[Symbol, datetime.date]:
return GeopoliticalSentimentIndex._last_update_date
def GetSource(self, config: SubscriptionDataConfig, date: datetime, isLiveMode: bool) -> SubscriptionDataSource:
return SubscriptionDataSource(f"data.quantpedia.com/backtesting_data/index/geopolitical_sentiment_index.csv", SubscriptionTransportMedium.RemoteFile, FileFormat.Csv)
def Reader(self, config: SubscriptionDataConfig, line: str, date: datetime, isLiveMode: bool) -> BaseData:
data = GeopoliticalSentimentIndex()
data.Symbol = config.Symbol
if not line[0].isdigit(): return None
split = line.split(';')
data.Time = datetime.strptime(split[0], "%Y-%m-%d")
if split[-1] != '.':
data.Value = float(split[-1])
if data.Time.date() > GeopoliticalSentimentIndex._last_update_date:
GeopoliticalSentimentIndex._last_update_date = data.Time.date()
return data