View on GitHub

thirdwave

Sornette’s Model and Code

Main equation

\[\small{ \ln(p(t)) = A + B(t_c - t)^\beta \big[ 1 + C \cos (\omega \ln(t_c-t) + \phi ) \big] }\]

Simplified form derivation below: start with logistic equation,

\[\frac{dp}{dt} = rp(t) [ K - p(t) ]\] \[\frac{dp}{dt} = r [p(t)]^{1+\delta}\] \[\int \frac{dp}{p(t)^{1+\delta}} = \int rt\] \[\int p(t)^{-1-\delta} dp = rt + C\]

Define $C = -rt_c$

\[\frac{p(t)^{-\delta}}{-\delta} = rt - rt_c\] \[p(t)^{-\delta}= -\delta r(t - t_c)\]

Because $t-t_c = -(t_c-t)$ the subtraction inside paren is

\[p(t)^{-\delta}= \delta r(t_c - t)\]

Define $\alpha = -\frac{1}{\delta}$, take $\alpha$’th power of both sides

\[(p(t)^{-\delta})^\alpha= (\delta r )^\alpha (t_c - t)^\alpha\]

Let $p(0) = p_0 = (\delta r )^\alpha$

\[p(t) = p_0 (t_c - t)^\alpha\]

For fitting it is better to use

\[p(t) = A + B(t_c - t)^z\]

Install lmfit==0.8.3

import pandas as pd, datetime
from pandas_datareader import data
start=datetime.datetime(2001, 1, 1)
end=datetime.datetime(2018, 1, 1)
df = data.DataReader('^GSPC', 'yahoo', start, end)
df.to_csv('sp500201702.csv')
df = data.DataReader('^DJI', 'yahoo', start, end)
df.to_csv('dji201702.csv')
import pandas as pd, datetime
#df = pd.read_csv('sp500201702.csv', index_col=0,parse_dates=True)
df = pd.read_csv('dji201702.csv', index_col=0,parse_dates=True)
import pandas as pd
from lmfit import Parameters, minimize
from lmfit.printfuncs import report_fit

def init_fit(min, max, init):
    fit_params = Parameters()
    fit_params.add('tc', value=init, max=max, min=min)
    fit_params.add('A', value=2.0, max=10.0, min=0.1)
    fit_params.add('m', value=0.5, max=1.0, min=0.01)
    fit_params.add('C', value=0.0, max=2.0, min=-2)
    fit_params.add('beta', value=0.5, max=1.0, min=0.1)
    fit_params.add('omega', value=20., max=40.0, min=0.1)
    fit_params.add('phi', value=np.pi, max=2*np.pi, min=0.1)
    return fit_params

def f(pars,x):
    tc = pars['tc'].value
    A = pars['A'].value
    m = pars['m'].value
    C = pars['C'].value
    beta = pars['beta'].value
    omega = pars['omega'].value
    phi = pars['phi'].value
    tmp1 = (1 + C*np.cos(omega*np.log(tc-x) + phi))
    model = A - ((m*((tc-x)**beta))*tmp1)
    return model
    
def residual(pars,y,t):
    return y-f(pars,t)

beg_date = df.head(1).index[0].timetuple()
beg_date_dec = beg_date.tm_year + (beg_date.tm_yday / 367.)
end_date = df.tail(1).index[0].timetuple()
end_date_dec = end_date.tm_year + (end_date.tm_yday / 367.)
df['Year'] = np.linspace(beg_date_dec,end_date_dec,len(df))

fit_params = init_fit(2001.0,2100.0,2050.)

y = np.log(df['Adj Close'])
t = df['Year']
out = minimize(residual, fit_params, args=(y,t,))

report_fit(fit_params)
[[Variables]]
    tc:      2018.01977 +/- 0.059571 (0.00%) (init= 2050)
    A:       10         +/- 0.007098 (0.07%) (init= 2)
    m:       0.19663441 +/- 0.019641 (9.99%) (init= 0.5)
    C:      -0.14243083 +/- 0.007036 (4.94%) (init= 0)
    beta:    0.53262341 +/- 0.026568 (4.99%) (init= 0.5)
    omega:   13.1485152 +/- 0.151282 (1.15%) (init= 20)
    phi:     6.28318530 +/- 0.049456 (0.79%) (init= 3.141593)
[[Correlations]] (unreported correlations are <  0.100)
    C(omega, phi)                = -0.994 
    C(m, beta)                   = -0.993 
    C(A, m)                      =  0.972 
    C(tc, phi)                   = -0.940 
    C(A, beta)                   = -0.940 
    C(tc, omega)                 =  0.911 
    C(A, C)                      =  0.801 
    C(m, C)                      =  0.766 
    C(C, beta)                   = -0.729 
    C(tc, C)                     =  0.200 
    C(C, phi)                    = -0.178 
    C(tc, A)                     =  0.173 
    C(C, omega)                  =  0.164 
    C(beta, omega)               =  0.147 
    C(A, phi)                    = -0.128 
    C(beta, phi)                 = -0.121 
    C(A, omega)                  =  0.101 

Analyze residuals for stationarity, if there, fit was good.

import statsmodels.tsa.stattools as st
res = list(st.adfuller(out.residual,maxlag=1))
print res[0], '%1,%5,%10', res[4]['1%'], res[4]['5%'], res[4]['10%']
-2.90299578531 %1,%5,%10 -3.43196089051 -2.86225180324 -2.56714889993
df['Adj Close'].plot()
plt.savefig('bubble_01.png')