<jupyter_start><jupyter_text><jupyter_code>!pip install yfinance
import yfinance as yf
import datetime as dt
import matplotlib.pyplot as plt
import matplotlib.transforms as transform
import matplotlib.gridspec as gridspec
import pandas as pd
from pandas.tseries.offsets import MonthEnd
pd.options.mode.chained_assignment = None
pd.set_option('use_inf_as_na', True)
def get(tickers, startdate, enddate):
  def data(ticker):
    return (yf.download(ticker, start=startdate, end=enddate))
  datas = map (data, tickers)
  return(pd.concat(datas, keys=tickers, names=['Ticker', 'Date']))

tickers=["SGRY", "NTRA", "Z", "FATE", "DIS", "GM", 'BPMC', "PTC"]
start = dt.datetime(2015,12,31)
end = dt.datetime(2020,12,31)
data = get(tickers, start, end)
# data = data.resample('m').mean()
data
# Isolate the `Adj Close` values and transform the DataFrame
daily_close_px = data[['Adj Close']].reset_index().pivot('Date', 'Ticker', 'Adj Close')
# Monthly percentage change 
daily_pct_change = daily_close_px.pct_change()
# distributions plot
daily_pct_change.hist(bins=50, sharex=True, figsize=(12,8))
plt.show()
# Isolate the `Adj Close` values and transform the DataFrame
close_px = data[['Adj Close']].reset_index().pivot('Date', 'Ticker', 'Adj Close')
mtly_close_px = close_px.resample('m').mean()
# print(mtly_close_px)

# Monthly percentage change 
mtly_return = mtly_close_px.pct_change()
# distributions plot
mtly_return.hist(bins=20, sharex=True, figsize=(12,8))
plt.show()
# scatter matrix 
pd.plotting.scatter_matrix(mtly_return, diagonal='kde', figsize=(15,12))
plt.grid(True);plt.show()
bpmc = pd.DataFrame(mtly_close_px['BPMC'])

# The first component to extract is the trend.
bpmc['trend'] = bpmc.rolling(window=3, min_periods=3).mean()

# Seasonality will be cyclical patterns that occur in our time series once the data has had trend removed.
bpmc['detrend_a'] = bpmc['BPMC'] - bpmc['trend']
bpmc['detrend_b'] = bpmc['BPMC'] / bpmc['trend']

# To work out the seasonality we need to work out what the typical de-trended values are over a cycle. 
# Here I will calculate the mean value for the observations in Q1, Q2, Q3, and Q4.
bpmc['seasonal_a'] = bpmc['detrend_a'].mean()
bpmc['seasonal_b'] = bpmc['detrend_b'].mean()

# Now that we have our two components, we can calculate the residual in both situations and see which has the better fit.
bpmc['residual_a'] = bpmc['detrend_a'] - bpmc['seasonal_a']
bpmc['residual_b'] = bpmc['detrend_b'] - bpmc['seasonal_b']
bpmc

import numpy as np

# Define the minumum of periods to consider 
min_periods = 3 
# Calculate the volatility
vol = mtly_return.rolling(min_periods).std() * np.sqrt(min_periods) 
# Plot the volatility
vol.plot(figsize=(15, 6)); plt.grid(True)
plt.show()
from datetime import datetime
tickers=["SGRY", "NTRA", "Z", "FATE", "DIS", "GM", 'BPMC', "PTC"]
ls_key = 'Adj Close'
start = dt.datetime(2015,12,31)
end = dt.datetime(2020,12,31)
 
df = yf.download(tickers,start, end)
prices = df[[("Adj Close", s) for s in tickers]]
prices.columns = prices.columns.droplevel(level=0)
prices.index = pd.to_datetime(prices.index)

monthly_ret = prices.pct_change().resample('m').agg(lambda x: (x+1).prod() - 1)
monthly_ret
import numpy as np

past_11 = (monthly_ret + 1).rolling(11).apply(np.prod) - 1
past_11.head(11)
# portfolio formation date
formation = dt.datetime(2016,12,31)
formation
# 1 month prior to formation date
end_measurement = formation - MonthEnd(1)
end_measurement
# 12 months return
ret_12 = past_11.loc[end_measurement]
ret_12 = ret_12.reset_index()
ret_12<jupyter_output><empty_output><jupyter_text>Rank<jupyter_code>ret_12['decile'] = pd.qcut(ret_12.iloc[:, 1], 10, labels=False, duplicates='drop')
ret_12
winners = ret_12[ret_12.decile >= 8]
losers = ret_12[ret_12.decile <= 1]

print('Winners:'); print(winners); print()
print('Losers:'), print(losers)
winner_ret = monthly_ret.loc[formation + MonthEnd(1),
                             monthly_ret.columns.isin(winners['index'])]

loser_ret = monthly_ret.loc[formation + MonthEnd(1),
                             monthly_ret.columns.isin(losers['index'])]

momentum_profit = winner_ret.mean() - loser_ret.mean()
print(momentum_profit)<jupyter_output>-0.07428603876819745<jupyter_text>Function to define momentun:<jupyter_code>def momentum(formation):
  end_measurement = formation - MonthEnd(1)
  ret_12 = past_11.loc[end_measurement]
  ret_12 = ret_12.reset_index()
  ret_12['decile'] = pd.qcut(ret_12.iloc[:, 1], 10, labels=False, duplicates='drop')
  winners = ret_12[ret_12.decile >= 8]
  losers = ret_12[ret_12.decile <= 1]
  winner_ret = monthly_ret.loc[formation + MonthEnd(1),
                             monthly_ret.columns.isin(winners['index'])]

  loser_ret = monthly_ret.loc[formation + MonthEnd(1),
                             monthly_ret.columns.isin(losers['index'])]
  momentum_profit = winner_ret.mean() - loser_ret.mean()
  return (momentum_profit)

momentum(formation)
for i in range(12*10):
  print(formation + MonthEnd(i))
profits = []
dates = []

for i in range(12*4):
  profits.append(momentum(formation + MonthEnd(i)))
  dates.append(formation + MonthEnd(i))

dataframe = pd.DataFrame(profits[1:])
dataframe
dates<jupyter_output><empty_output><jupyter_text>Benchmarking against SP500<jupyter_code>SP = yf.download('^GSPC', start=dates[0], end=dates[-1])
SP = SP['Adj Close']
SP_mtnhly_ret = SP.pct_change().resample('m').agg(lambda x: (x+1).prod() -1)
print(SP_mtnhly_ret)                                                  
dataframe['SP500'] = SP_mtnhly_ret.values
print(dataframe)
dataframe['excess'] = dataframe.iloc[:,0] - dataframe.iloc[:,1]
print(dataframe)
dataframe['outperformed'] = ['YES' if i > 0 else "NO" for i in dataframe.excess]
print(dataframe)
pd.value_counts(dataframe.outperformed)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1460635359116022, 'fraction_numerical': 0.030904696132596686, 'mean_word_length': 5.3450164293537785, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 5, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '38281', 'license': 'MIT', 'n_tokens_mistral': 2248, 'n_tokens_neox': 2117, 'n_words': 457, 'path': 'Monthly_Series_MomentumStrategy.ipynb', 'repo_name': 'saritmaitra/Momentum', 'size': '5792'}