Overall Statistics |
Total Trades 92 Average Win 0.85% Average Loss -0.22% Compounding Annual Return 7.708% Drawdown 6.900% Expectancy 1.461 Net Profit 16.003% Sharpe Ratio 1.176 Probabilistic Sharpe Ratio 59.153% Loss Rate 49% Win Rate 51% Profit-Loss Ratio 3.81 Alpha 0.037 Beta 0.287 Annual Standard Deviation 0.046 Annual Variance 0.002 Information Ratio -0.066 Tracking Error 0.084 Treynor Ratio 0.187 Total Fees $252.70 Estimated Strategy Capacity $45000.00 Lowest Capacity Asset STIP US4OVXDLWVHH |
#region imports from AlgorithmImports import * #endregion class CPIData(PythonData): # 12-month unadjusted CPI data # Source: https://www.bls.gov/charts/consumer-price-index/consumer-price-index-by-category-line-chart.htm # Release dates source: https://www.bls.gov/bls/news-release/cpi.htm def GetSource(self, config: SubscriptionDataConfig, date: datetime, isLive: bool) -> SubscriptionDataSource: return SubscriptionDataSource("https://www.dropbox.com/s/f02a9htg6pyhf9p/CPI%20data%201.csv?dl=1", SubscriptionTransportMedium.RemoteFile) def Reader(self, config: SubscriptionDataConfig, line: str, date: datetime, isLive: bool) -> BaseData: if not (line.strip()): return None cpi = CPIData() cpi.Symbol = config.Symbol try: def parse(pct): return float(pct[:-1]) / 100 data = line.split(',') cpi.EndTime = datetime.strptime(data[0], "%m%d%Y %H:%M %p") cpi["month"] = data[1] cpi['all-items'] = parse(data[2]) cpi['food'] = parse(data[3]) cpi['food-at-home'] = parse(data[4]) cpi['food-away-from-home'] = parse(data[5]) cpi['energy'] = parse(data[6]) cpi['gasoline'] = parse(data[7]) cpi['electricity'] = parse(data[8]) cpi['natural-gas'] = parse(data[9]) cpi['all-items-less-food-and-energy'] = parse(data[10]) cpi['commodities-less-food-and-energy-commodities'] = parse(data[11]) cpi['apparel'] = parse(data[12]) cpi['new-vehicles'] = parse(data[13]) cpi['medical-car-commodities'] = parse(data[14]) cpi['services-less-energy-services'] = parse(data[15]) cpi['shelter'] = parse(data[16]) cpi['medical-care-services'] = parse(data[17]) cpi['education-and-communication'] = parse(data[18]) cpi.Value = cpi['all-items'] except ValueError: # Do nothing return None return cpi
# region imports from AlgorithmImports import * from data import CPIData from symbol import SymbolData # endregion class Modified_60_40(QCAlgorithm): def Initialize(self): self.SetStartDate(2017, 1, 1) # Set Start Date self.SetEndDate(2019, 1, 1) # Set End Date self.SetCash(100000) # Set Strategy Cash self.AddEquity("SPY", Resolution.Minute) self.AddEquity("BND", Resolution.Minute) self.AddEquity("GLD", Resolution.Minute) self.AddEquity("STIP", Resolution.Minute) # requesting data for yield curve self.yield_curve_symbol = self.AddData(USTreasuryYieldCurveRate, "USTYCR").Symbol # historical data for yield curve history = self.History(USTreasuryYieldCurveRate, self.yield_curve_symbol, 60, Resolution.Daily) # requesting data for CPI self.cpi_symbol = self.AddData(CPIData, "CPIData").Symbol # historical data for CPI #history = self.History(CPIData, self.cpi_symbol, 60, Resolution.Daily) # Warm up CPI data history self.cpi_lookback = timedelta(days=395) self.cpi_history = self.History(self.cpi_symbol, self.StartDate - self.cpi_lookback, self.StartDate) if not self.cpi_history.empty: self.cpi_history = self.cpi_history.loc[self.cpi_symbol]['value'] self.RotationInterval = timedelta(days=30) self.first = True def OnData(self, data: Slice): #look for first if self.first: self.first = False self.LastRotationTime = self.Time return #invest in short term treasuries if not invested if not self.Portfolio.Invested: self.SetHoldings("STIP", 1) #check for null for yield curve if not data.ContainsKey(self.yield_curve_symbol): return #define rates rates = data[self.yield_curve_symbol] # Check for null before using the values if not (rates.TenYear is not None and rates.ThreeMonth is not None): return #Update CPI historical data if data.ContainsKey(self.cpi_symbol): self.rebalance = True self.Debug("test") self.cpi_history.loc[self.Time] = data[self.cpi_symbol].Value self.cpi_history = self.cpi_history.loc[self.cpi_history.index >= self.Time] #look for rebalance window delta = self.Time - self.LastRotationTime if delta > self.RotationInterval: #reset rebalance window self.LastRotationTime = self.Time # determine if risk on (10Y-3M is >1) or off (10Y-3M is <1) if (rates.TenYear - rates.ThreeMonth) > 1: risk = 1 else: risk = 0 # if inflation is > X% then inflation = 1 otherwise inflation = 0 # determine if inflationary (expected CPI > 2.5%) or deflationary (expected CPI <2.5%) # Get the current environment and environment history self.Debug(f"{self.Time}") self.Debug(self.cpi_history) self.Debug(self.cpi_history[-13]) if self.cpi_history[-1] > 3: inflation = 1 else: inflation = 0 if risk == 1 and inflation == 1: #risk on, inflationary self.Debug("risk on inflationary") self.Liquidate() self.SetHoldings("SPY", 0.6) self.SetHoldings("GLD", 0.4) elif risk == 1 and inflation == 0: #risk on, deflationary self.Debug("risk on deflationary") self.Liquidate() self.SetHoldings("SPY", 0.6) self.SetHoldings("BND", 0.4) elif risk == 0 and inflation == 1: #risk off, inflationary self.Debug("risk off inflationary") self.Liquidate() self.SetHoldings("STIP", 0.6) self.SetHoldings("GLD", 0.4) elif risk == 0 and inflation == 0: #risk off, deflationary self.Debug("risk off deflationary") self.Liquidate() self.SetHoldings("BND", 0.6) self.SetHoldings("STIP", 0.4)
#region imports from AlgorithmImports import * #endregion class SymbolData: def __init__(self, algorithm, security, lookback): self.algorithm = algorithm self.symbol = security.Symbol self.security = security # Set up consolidators to collect pricing data self.consolidator = TradeBarConsolidator(1) self.consolidator.DataConsolidated += self.consolidation_handler algorithm.SubscriptionManager.AddConsolidator(self.symbol, self.consolidator) self.history = pd.Series() self.lookback = lookback # Get historical data history = algorithm.History(self.symbol, self.lookback, Resolution.Daily) if not history.empty: self.history = history.loc[self.symbol].open def consolidation_handler(self, sender: object, consolidated_bar: TradeBar) -> None: self.history.loc[consolidated_bar.EndTime] = consolidated_bar.Open self.history = self.history.loc[self.history.index > consolidated_bar.EndTime - self.lookback] def dispose(self): self.algorithm.SubscriptionManager.RemoveConsolidator(self.symbol, self.consolidator)