Overall Statistics |
Total Orders
132
Average Win
1.71%
Average Loss
-0.23%
Compounding Annual Return
2.144%
Drawdown
2.800%
Expectancy
3.584
Start Equity
100000
End Equity
169426.89
Net Profit
69.427%
Sharpe Ratio
-0.514
Sortino Ratio
-0.61
Probabilistic Sharpe Ratio
61.411%
Loss Rate
46%
Win Rate
54%
Profit-Loss Ratio
7.49
Alpha
-0.007
Beta
-0.015
Annual Standard Deviation
0.014
Annual Variance
0
Information Ratio
-0.309
Tracking Error
0.162
Treynor Ratio
0.493
Total Fees
$951.32
Estimated Strategy Capacity
$33000000.00
Lowest Capacity Asset
SHY SGNKIKYGE9NP
Portfolio Turnover
1.23%
|
from AlgorithmImports import * from dateutil.relativedelta import relativedelta # Custom fee model. class CustomFeeModel(FeeModel): def GetOrderFee(self, parameters): fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005 return OrderFee(CashAmount(fee, "USD")) # Quandl "value" data class QuandlValue(PythonQuandl): def __init__(self): self.ValueColumnName = 'Value' # Quantpedia data. # NOTE: IMPORTANT: Data order must be ascending (datewise) class QuantpediaFutures(PythonData): _last_update_date:Dict[str, datetime.date] = {} @staticmethod def get_last_update_date() -> Dict[str, datetime.date]: return QuantpediaFutures._last_update_date def GetSource(self, config, date, isLiveMode): return SubscriptionDataSource("data.quantpedia.com/backtesting_data/futures/{0}.csv".format(config.Symbol.Value), SubscriptionTransportMedium.RemoteFile, FileFormat.Csv) def Reader(self, config, line, date, isLiveMode): data = QuantpediaFutures() data.Symbol = config.Symbol if not line[0].isdigit(): return None split = line.split(';') data.Time = datetime.strptime(split[0], "%d.%m.%Y") + timedelta(days=1) data['back_adjusted'] = float(split[1]) data['spliced'] = float(split[2]) data.Value = float(split[1]) # store last update date if config.Symbol.Value not in QuantpediaFutures._last_update_date: QuantpediaFutures._last_update_date[config.Symbol.Value] = datetime(1,1,1).date() if data.Time.date() > QuantpediaFutures._last_update_date[config.Symbol.Value]: QuantpediaFutures._last_update_date[config.Symbol.Value] = data.Time.date() return data class InterestRate3M(PythonData): _last_update_date:Dict[str, datetime.date] = {} @staticmethod def get_last_update_date() -> Dict[str, datetime.date]: return InterestRate3M._last_update_date def GetSource(self, config:SubscriptionDataConfig, date:datetime, isLiveMode:bool) -> SubscriptionDataSource: return SubscriptionDataSource("data.quantpedia.com/backtesting_data/interbank_rate/{0}.csv".format(config.Symbol.Value), SubscriptionTransportMedium.RemoteFile, FileFormat.Csv) def Reader(self, config:SubscriptionDataConfig, line:str, date:datetime, isLiveMode:bool) -> BaseData: data = InterestRate3M() data.Symbol = config.Symbol if not line[0].isdigit(): return None split = line.split(';') data.Time = datetime.strptime(split[0], "%Y-%m-%d") + relativedelta(months=2) data['value'] = float(split[1]) data.Value = float(split[1]) # store last update date if config.Symbol.Value not in InterestRate3M._last_update_date: InterestRate3M._last_update_date[config.Symbol.Value] = datetime(1,1,1).date() if data.Time.date() > InterestRate3M._last_update_date[config.Symbol.Value]: InterestRate3M._last_update_date[config.Symbol.Value] = data.Time.date() return data
# https://quantpedia.com/strategies/crude-oil-predicts-equity-returns/ # # Several types of oil can be used (Brent, WTI, Dubai etc.) without big differences in results. The source paper for # this anomaly uses Arab Light crude oil. Monthly oil returns are used in the regression equation as an independent # variable and equity returns are used as a dependent variable. The model is re-estimated every month and # observations of the last month are added. The investor determines whether the expected stock market return in # a specific month (based on regression results and conditional on the oil price change in the previous month) is higher # or lower than the risk-free rate. The investor is fully invested in the market portfolio if the expected # return is higher (bull market); he invests in cash if the expected return is lower (bear market). from data_tools import QuantpediaFutures, QuandlValue, CustomFeeModel, InterestRate3M from AlgorithmImports import * from typing import List, Dict import numpy as np from scipy import stats from collections import deque class CrudeOilPredictsEquityReturns(QCAlgorithm): def Initialize(self): self.SetStartDate(2000, 1, 1) self.SetCash(100000) self.min_period:int = 13 self.leverage:int = 2 self.data:Dict[Symbol, deque] = {} self.symbols:List[str] = [ "CME_ES1", # E-mini S&P 500 Futures, Continuous Contract #1 "CME_CL1" # Crude Oil Futures, Continuous Contract #1 ] self.cash:Symbol = self.AddEquity('SHY', Resolution.Daily).Symbol self.risk_free_rate:Symbol = self.AddData(InterestRate3M, 'IR3TIB01USM156N', Resolution.Daily).Symbol for symbol in self.symbols: data = self.AddData(QuantpediaFutures, symbol, Resolution.Daily) data.SetLeverage(self.leverage) data.SetFeeModel(CustomFeeModel()) self.data[symbol] = deque() self.Settings.MinimumOrderMarginPortfolioPercentage = 0. self.recent_month:int = -1 def OnData(self, data:Slice) -> None: rebalance_flag:bool = False for symbol in self.symbols: if symbol in data: if self.recent_month != self.Time.month: rebalance_flag = True if data[symbol]: price:float = data[symbol].Value self.data[symbol].append(price) if rebalance_flag: self.recent_month = self.Time.month ir_last_update_date:Dict[str, datetime.date] = InterestRate3M.get_last_update_date() last_update_date:Dict[str, datetime.date] = QuantpediaFutures.get_last_update_date() rf_rate:float = .0 # check if data is still coming if self.Securities[self.risk_free_rate].GetLastData() and ir_last_update_date[self.risk_free_rate.Value] > self.Time.date(): rf_rate = self.Securities[self.risk_free_rate].Price / 100 else: return if not all(last_update_date[x] > self.Time.date() for x in self.symbols): self.Liquidate() return market_prices:np.ndarray = np.array(self.data[self.symbols[0]]) oil_prices:np.ndarray = np.array(self.data[self.symbols[1]]) # At least one year of data is ready. if len(market_prices) < self.min_period or len(oil_prices) < self.min_period: return # Trim price series lenghts. min_size:float = min(len(market_prices), len(oil_prices)) market_prices = market_prices[-min_size:] oil_prices = oil_prices[-min_size:] market_returns = market_prices[1:] / market_prices[:-1] - 1 oil_returns = oil_prices[1:] / oil_prices[:-1] - 1 # Simple Linear Regression # Y = C + (M * X) # Y = α + (β ∗ X) # Y = Dependent variable (output/outcome/prediction/estimation) # C/α = Constant (Y-Intercept) # M/β = Slope of the regression line (the effect that X has on Y) # X = Independent variable (input variable used in the prediction of Y) slope, intercept, r_value, p_value, std_err = stats.linregress(oil_returns[:-1], market_returns[1:]) expected_market_return = intercept + (slope * oil_returns[-1]) if expected_market_return > rf_rate: if self.Portfolio[self.cash].Invested: self.Liquidate(self.cash) self.SetHoldings(self.symbols[0], 1) else: if self.Portfolio[self.symbols[0]].Invested: self.Liquidate(self.symbols[0]) if self.cash in data and data[self.cash]: self.SetHoldings(self.cash, 1)