<jupyter_start><jupyter_text>Causal Impact example - Bitcoin & <NAME> * In this notebook, we explore how to use CausalImpact package to analyze causal impact of an event on a time series data. * motivating example (image source : https://www.vox.com/recode/2021/5/18/22441831/elon-musk-bitcoin-dogecoin-crypto-prices-tesla)  Bitcoin price data & first look * data source : yahoo finance data * https://finance.yahoo.com/chart/BTC-USD<jupyter_code>import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
import statsmodels as sm
from statsmodels.tsa.statespace.structural import UnobservedComponents
from statsmodels.tsa.arima_process import ArmaProcess
from statsmodels.tsa.seasonal import seasonal_decompose
from causalimpact import CausalImpact<jupyter_output><empty_output><jupyter_text>First peek into the data<jupyter_code>df = pd.read_csv("../data/bitcoin_historical_price.csv")
df.head()
df['Date'] = pd.to_datetime(df['Date'])
df.set_index('Date', inplace=True)
df.sort_index(ascending=True, inplace=True)<jupyter_output><empty_output><jupyter_text>@title Plot Close Pricefig, ax = plt.subplots(figsize=(14,6))df['AdjClose'].plot(lw=2., label='AdjClose Price',c='royalblue', ax=ax)df['AdjClose'].rolling(3).mean().plot(lw=1.5, label='Roll_3d',c='orange', ax=ax)df['AdjClose'].rolling(7).mean().plot(lw=1.5, label='Roll_7d', c='salmon', ax=ax)plt.title('Bitcoin Price USD - 1Y')plt.ylabel('AdjClose Price')plt.grid(); plt.legend()<jupyter_code>df_2021 = df[df.index.year>=2021].copy()
#@title Plot Close Price
fig, ax = plt.subplots(figsize=(14,6))
#ax.plot(df['AdjClose'], lw=2., label='AdjClose Price',c='royalblue')
df_2021['AdjClose'].plot(lw=2., label='AdjClose Price',c='royalblue', ax=ax)
df_2021['AdjClose'].rolling(3).mean().plot(lw=1.5, label='Roll_3d',c='orange', ax=ax)
df_2021['AdjClose'].rolling(7).mean().plot(lw=1.5, label='Roll_7d', c='salmon', ax=ax)
ax.axvline(pd.to_datetime('2021-05-12'), color='r', linestyle='--', lw=1)
ax.text(pd.to_datetime('2021-05-12'), 55000, 
        "Tesla no longer \n accepting Bitcoin", size=12)
plt.title('Bitcoin Daily Price USD, 2021')
plt.ylabel('AdjClose Price')
plt.grid(); plt.legend()
#@title Plot Close Price
fig, ax = plt.subplots(figsize=(14,6))
#ax.plot(df['AdjClose'], lw=2., label='AdjClose Price',c='royalblue')
df_2021[df_2021.index.month>=4]['AdjClose'].plot(lw=2., label='AdjClose Price',c='royalblue', ax=ax)
df_2021[df_2021.index.month>=4]['AdjClose'].rolling(3).mean().plot(lw=1.5, label='Roll_3d',c='orange', ax=ax)
df_2021[df_2021.index.month>=4]['AdjClose'].rolling(7).mean().plot(lw=1.5, label='Roll_7d', c='salmon', ax=ax)
ax.axvline(pd.to_datetime('2021-05-12'), color='r', linestyle='--', lw=1)
ax.text(pd.to_datetime('2021-05-12'), 55000, 
        "Tesla no longer \n accepting Bitcoin \n (2021-05-12) ", size=12)
#ax.axvline(pd.to_datetime('2015-11-01'), color='r', linestyle='--', lw=2)
plt.title('Bitcoin Daily Price USD')
plt.ylabel('AdjClose Price')
plt.grid(); plt.legend()<jupyter_output><empty_output><jupyter_text>Apply Causal Impact model - default model, local level only<jupyter_code>pre_period = [df_2021.index.min(), '2021-05-12']
post_period = ['2021-05-13', df_2021.index.max()] #june 12th 
ci = CausalImpact(df_2021['AdjClose'], pre_period, post_period,)
ci.plot(panels=['original', 'pointwise'], figsize=(14, 8))
print(ci.summary())<jupyter_output>Posterior Inference {Causal Impact}
                          Average            Cumulative
Actual                    38737.26           1200855.19
Prediction (s.d.)         51946.23 (1651.95) 1610333.1 (51210.38)
95% CI                    [48750.2, 55225.72][1511256.22, 1711997.21]

Absolute effect (s.d.)    -13208.96 (1651.95)-409477.91 (51210.38)
95% CI                    [-16488.45, -10012.94][-511142.02, -310401.03]

Relative effect (s.d.)    -25.43% (3.18%)    -25.43% (3.18%)
95% CI                    [-31.74%, -19.28%] [-31.74%, -19.28%]

Posterior tail-area probability p: 0.0
Posterior prob. of a causal effect: 100.0%

For more details run the command: print(impact.summary('report'))<jupyter_text>Add seasonality component * Defaults to 0.01, a typical choice for well-behaved and stable datasets with low residual volatility after regressing out known predictors (e.g., web searches or sales in high quantities). * When in doubt, a safer option is to use 0.1, as validated on synthetic data, although this may sometimes give rise to unrealistically wide prediction intervals.* in python implementation, specifying it as `None` - the package will by itself to optimize this parameter using maximum likelihood estimation.<jupyter_code>ci_season = CausalImpact(df_2021['AdjClose'], 
                         pre_period, post_period, 
                         nseasons=[{'period': 7, 'harmonics':1}, 
                                   {'period': 30, 'harmonnics':1}], 
                         prior_level_sd=0.1) #0.01 is the default
ci_season.plot(panels=['original', 'pointwise'], figsize=(14, 8))
ci_season.trained_model.params
print(ci_season.summary())
print(ci_season.summary('report'))
# Prior as None
ci_season = CausalImpact(df_2021['AdjClose'], 
                         pre_period, post_period, 
                         nseasons=[{'period': 7, 'harmonics':1}, 
                                   {'period': 30, 'harmonnics':1}], 
                         prior_level_sd=None) #0.01 is the default
ci_season.plot(panels=['original'], figsize=(14, 4))
print(ci_season.summary())<jupyter_output>Posterior Inference {Causal Impact}
                          Average            Cumulative
Actual                    38737.26           1200855.19
Prediction (s.d.)         48101.5 (6329.16)  1491146.56 (196204.07)
95% CI                    [35637.19, 60447.06][1104752.95, 1873858.77]

Absolute effect (s.d.)    -9364.24 (6329.16) -290291.37 (196204.07)
95% CI                    [-21709.79, 3100.07][-673003.58, 96102.24]

Relative effect (s.d.)    -19.47% (13.16%)   -19.47% (13.16%)
95% CI                    [-45.13%, 6.44%]   [-45.13%, 6.44%]

Posterior tail-area probability p: 0.08
Posterior prob. of a causal effect: 91.91%

For more details run the command: print(impact.summary('report'))<jupyter_text>Use covariates - S&P500 as a regressor<jupyter_code>SP500 = pd.read_csv("../data/S&P500.csv", header=0)
SP500['Date'] = pd.to_datetime(SP500['Date'])
SP500.set_index(SP500['Date'], inplace=True)
SP500.drop(columns={'Date'},inplace=True)
SP500.rename(columns={"AdjClose":"AdjClose_sp500"}, inplace=True)
SP500.head()
#@title Plot Close Price
fig, axes = plt.subplots(2,1, figsize=(14,10), sharex=True)
axes = axes.flatten()
#ax.plot(df['AdjClose'], lw=2., label='AdjClose Price',c='royalblue')
df_2021['AdjClose'].plot(lw=2., label='AdjClose Price',c='royalblue', ax=axes[0])
SP500[SP500.index.year>=2021].AdjClose_sp500.plot(lw=2., label='S&P500 Price',c='royalblue', ax=axes[1])
axes[0].axvline(pd.to_datetime('2021-05-12'), color='r', linestyle='--', lw=1)
axes[1].axvline(pd.to_datetime('2021-05-12'), color='r', linestyle='--', lw=1)
axes[0].text(pd.to_datetime('2021-05-12'), 55000, 
        "Tesla no longer \n accepting Bitcoin", size=12)
axes[0].set_title('Bitcoin Daily Price USD, 2021')
axes[1].set_title('S&P500 Daily Price USD, 2021')
df_cov = df.merge(SP500['AdjClose_sp500'], 
                  left_index=True, right_index=True)
df_cov=df_cov[['AdjClose', 'AdjClose_sp500']]
df_cov.head()
df_cov.corr()
df_cov = df_cov[df_cov.index.year>=2021].copy()
pre_period = [df_cov.index.min(), '2021-05-12']
post_period = ['2021-05-13', df_cov.index.max()]
ci_sp500 = CausalImpact(df_cov, 
                        pre_period, post_period, 
                        nseasons=[{'period': 7}, 
                                   {'period': 30}], 
                        prior_level_sd=0.1)
ci_sp500.plot(panels=['original', 'pointwise'], figsize=(14, 8))
ci_sp500.trained_model.params
print(ci_sp500.summary())<jupyter_output>Posterior Inference {Causal Impact}
                          Average            Cumulative
Actual                    39069.01           820449.24
Prediction (s.d.)         52958.26 (3454.05) 1112123.53 (72535.08)
95% CI                    [46362.78, 59902.41][973618.38, 1257950.68]

Absolute effect (s.d.)    -13889.25 (3454.05)-291674.29 (72535.08)
95% CI                    [-20833.4, -7293.77][-437501.44, -153169.14]

Relative effect (s.d.)    -26.23% (6.52%)    -26.23% (6.52%)
95% CI                    [-39.34%, -13.77%] [-39.34%, -13.77%]

Posterior tail-area probability p: 0.0
Posterior prob. of a causal effect: 100.0%

For more details run the command: print(impact.summary('report'))<jupyter_text>Try some customeized model<jupyter_code>from statsmodels.tsa.statespace.structural import UnobservedComponents
from causalimpact.misc import standardize
normed_pre_data, _ = standardize(df_cov.iloc[:90])
normed_pre_data.head()
cust_model = UnobservedComponents(
    endog=normed_pre_data['AdjClose'].iloc[:90],
    level='lltrend',
    exog=normed_pre_data['AdjClose_sp500'].iloc[:90],
    freq_seasonal=[{'period':30}, {'period':7}]
)
ci_cust = CausalImpact(df_cov, 
                       pre_period, post_period, 
                       model=cust_model, prior_level_sd=0.1)
ci_cust.plot(panels=['original', 'pointwise'], figsize=(14, 8))
ci_cust.trained_model.params
print(ci_cust.summary())<jupyter_output>Posterior Inference {Causal Impact}
                          Average            Cumulative
Actual                    39069.01           820449.24
Prediction (s.d.)         25210.5 (22418.56) 529420.53 (470789.74)
95% CI                    [-20105.09, 67774.04][-422206.96, 1423254.91]

Absolute effect (s.d.)    13858.51 (22418.56)291028.71 (470789.74)
95% CI                    [-28705.03, 59174.1][-602805.67, 1242656.2]

Relative effect (s.d.)    54.97% (88.93%)    54.97% (88.93%)
95% CI                    [-113.86%, 234.72%][-113.86%, 234.72%]

Posterior tail-area probability p: 0.26
Posterior prob. of a causal effect: 73.83%

For more details run the command: print(impact.summary('report'))<jupyter_text>VW<jupyter_code>vw = pd.read_csv('../data/vw.csv', header=0)
vw['Date'] = pd.to_datetime(vw['Date'])
vw.set_index(vw['Date'], inplace=True)
vw.drop(columns={'Date'},inplace=True)
vw.head()
pre_period = [vw.index.min(), "2015-09-13"]
post_period = ["2015-09-20", vw.index.max()]

ci = CausalImpact(vw, pre_period, post_period, nseasons=[{'period': 52}])
print(ci.summary())
ci.plot()
ci.trained_model.params<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.16699971453040252, 'fraction_numerical': 0.1090493862403654, 'mean_word_length': 3.6442775077330976, 'pattern_counts': {'":': 1, '<': 30, '<?xml version=': 0, '>': 36, 'https://': 2, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '301000', 'license': 'MIT', 'n_tokens_mistral': 4606, 'n_tokens_neox': 3846, 'n_words': 742, 'path': 'python/CausalImpact/notebooks/causal_impact_bitcoin.ipynb', 'repo_name': 'datenzauberai/btsa', 'size': '10512'}