Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update strategy library tutorial #02 to PEP8 #372

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,32 @@

<h3>Step 1: Request Historical Data</h3>
<p>
The first function takes two arguments: symbol and number of daily data points requested. This function requests historical <a href="https://www.quantconnect.com/docs#Consolidating-Data-TradeBars-vs-QuoteBars">QuoteBars</a> and builds it into a pandas DataFrame. For more information about pandas DataFrame, please refer to the help documentation <a href="http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.html">DataFrame</a>. The calculate_return function takes a DataFrame as an argument to calculate the mean and standard deviation of the log prices, and create new columns for the DataFrame (return, reversal factor and momentum) - it prepares the DataFrame for multiple linear regression.
The first function takes two arguments: symbol and number of daily data points requested. This function requests historical <a href="https://www.quantconnect.com/docs#Consolidating-Data-TradeBars-vs-QuoteBars">QuoteBars</a> and builds it into a pandas DataFrame. For more information about pandas DataFrame, please refer to the help documentation <a href="http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.html">DataFrame</a>. The <code>_calculate_return</code> function takes a DataFrame as an argument to calculate the mean and standard deviation of the log prices, and create new columns for the DataFrame (return, reversal factor and momentum) - it prepares the DataFrame for multiple linear regression.
</p>
<div class="section-example-container">

<pre class="python">def get_history(self,symbol, num):
<pre class="python">def _get_history(self,symbol, num):
data = {}
dates = []
history = self.History([symbol], num, Resolution.Daily).loc[symbol]['close'] #request the historical data for a single symbol
history = self.history([symbol], num, Resolution.DAILY).loc[symbol]['close'] #request the historical data for a single symbol
for time in history.index:
t = time.to_pydatetime().date()
dates.append(t)
dates = pd.to_datetime(dates)
dates = pd.to_datetime(dates)
df = pd.DataFrame(history)
df.reset_index(drop=True)
df.index = dates
df.columns = ['price']

return df

def calculate_return(self,df):
def _calculate_return(self,df):
#calculate the mean for further use
mean = np.mean(df.price)
# cauculate the standard deviation
sd = np.std(df.price)
# pandas method to take the last datapoint of each month.
df = df.resample('BM',how = lambda x: x[-1])
df = df.resample('BM').last()
# the following three lines are for further experiment purpose
# df['j1'] = df.price.shift(1) - df.price.shift(2)
# df['j2'] = df.price.shift(2) - df.price.shift(3)
Expand All @@ -45,67 +46,69 @@ <h3>Step 1: Request Historical Data</h3>

<h3>Step 2: Build Predictive Model</h3>
<p>
The concat function requests history and joins the results into a single DataFrame. As \(\mu \) varies by country so we assign the mean and standard deviation to the symbol for each currency for future use. The OLS function takes the resulting DataFrame to conduct an OLS regression. We write it into a function because it's easier to change the formula here if we need.
The <code>_concat</code> function requests history and joins the results into a single DataFrame. As \(\mu \) varies by country so we assign the mean and standard deviation to the symbol for each currency for future use. The OLS function takes the resulting DataFrame to conduct an OLS regression. We write it into a function because it's easier to change the formula here if we need.
</p>

<div class="section-example-container">

<pre class="python">def concat(self):
<pre class="python">def _concat(self):
# we requested as many daily tradebars as we can
his = self.get_history(self.quoted[0].Value,20*365)
his = self._get_history(self._quoted[0].value,20*365)
# get the clean DataFrame for linear regression
his = self.calculate_return(his)
his = self._calculate_return(his)
# add property to the symbol object for further use.
self.quoted[0].mean = his[1]
self.quoted[0].sd = his[2]
self._quoted[0].mean = his[1]
self._quoted[0].sd = his[2]
df = his[0]
# repeat the above procedure for each symbols, and concat the dataframes
for i in range(1,len(self.quoted)):
his = self.get_history(self.quoted[i].Value,20*365)
his = self.calculate_return(his)
self.quoted[i].mean = his[1]
self.quoted[i].sd = his[2]
for i in range(1,len(self._quoted)):
his = self._get_history(self._quoted[i].value,20*365)
his = self._calculate_return(his)
self._quoted[i].mean = his[1]
self._quoted[i].sd = his[2]
df = pd.concat([df,his[0]])
df = df.sort_index()
# remove outliers that outside the 99.9% confidence interval
df = df[df.apply(lambda x: np.abs(x - x.mean()) / x.std() &lt; 3).all(axis=1)]
df = df[df.apply(lambda x: np.abs(x - x.mean()) / x.std() < 3).all(axis=1)]
return df

def OLS(self,df):
res = sm.ols(formula = 'return ~ reversal + mom',data = df).fit()
def _OLS(self,df):
res = sm.ols(formula = 'log_return ~ reversal + mom',data = df).fit()
return res</pre>
</div>

<h3>Step 3: Apply Predictive Model</h3>
<p>
The predict function uses the history for the last 3 months, merges it into a DataFrame and then calculates the updated factors. Using these updated factors (together with the model we built) we calculate the expected return.
The <code>_predict</code> function uses the history for the last 3 months, merges it into a DataFrame and then calculates the updated factors. Using these updated factors (together with the model we built) we calculate the expected return.
</p>
<div class="section-example-container">

<pre class="python">def predict(self,symbol):
<pre class="python">def _predict(self,symbol):
# get current month in string
month = str(self.Time).split(' ')[0][5:7]
month = str(self.time).split(' ')[0][5:7]
# request the data in the last three months
res = self.get_history(symbol.Value,33*3)
res = self._get_history(symbol.value,33*3)
# pandas method to take the last datapoint of each month
res = res.resample('BM',how = lambda x: x[-1])
res = res.resample('BM').last()
# remove the data points in the current month
res = res[res.index.month != int(month)]
# calculate the variables
res = self.calculate_input(res,symbol.mean,symbol.sd)
res = res.ix[0]
res = self._calculate_input(res,symbol.mean,symbol.sd)
res = res.iloc[0]
# take the coefficient. The first one will not be used for sum-product because it's the intercept
params = self.formula.params[1:]
params = self._formula.params[1:]
# calculate the expected return
re = sum([a*b for a,b in zip(res[1:],params)]) + self.formula.params[0]
re = sum([a*b for a,b in zip(res[1:],params)]) + self._formula.params[0]
return re

def calculate_input(self, df, mean, sd):
def _calculate_input(self,df,mean,sd):
# df['j1'] = df.price - df.price.shift(1)
# df['j2'] = df.price.shift(1) - df.price.shift(2)
# df['j3'] = df.price.shift(2) - df.price.shift(3)
df['reversal'] = (df.price - mean)/sd
df['mom'] = df.price - df.price.shift(3)
df = df.dropna()
return df
</pre>
return df</pre>
</div>

<p>
Expand All @@ -121,29 +124,30 @@ <h3>Step 3: Apply Predictive Model</h3>

<h3>Step 4: Initializing the Model</h3>
<p>
In the Initialize function we prepare the data and conduct a linear regression. The class property 'self.formula' is the result of the OLS regression. We will use this object each time we rebalance the portfolio.
In the <a href='/docs/v2/writing-algorithms/initialization'>initialize</a> function we prepare the data and conduct a linear regression. The class property <code>self._formula</code> is the result of the OLS regression. We will use this object each time we rebalance the portfolio.
</p>

<div class="section-example-container">

<pre class="python">def Initialize(self):
self.SetStartDate(2013,6,1)
self.SetEndDate(2016,6,1)
self.SetCash(10000)
self.syls = ['EURUSD','GBPUSD','USDCAD','USDJPY']
self.quoted = []
for i in range(len(self.syls)):
self.quoted.append(self.AddForex(self.syls[i],Resolution.Daily,Market.Oanda).Symbol)
df = self.concat()
self.Log(str(df))
self.formula = self.OLS(df)
self.Log(str(self.formula.summary()))
self.Log(str(df))
self.Log(str(df.describe()))
for i in self.quoted:
self.Log(str(i.mean) + ' ' + str(i.sd))
self.Schedule.On(self.DateRules.MonthStart(), self.TimeRules.At(9,31), Action(self.action))
</pre>
<pre class="python">def initialize(self):
self.set_start_date(2013,6,1)
self.set_end_date(2016,6,1)
self.set_cash(10000)
syls = ['EURUSD','GBPUSD','USDCAD','USDJPY']
self._quoted = []
for i in range(len(syls)):
self._quoted.append(self.add_forex(syls[i],Resolution.DAILY,Market.OANDA).symbol)

df = self._concat()
self.log(str(df))
self._formula = self._OLS(df)
self.log(str(self._formula.summary()))
self.log(str(df))
self.log(str(df.describe()))
for i in self._quoted:
self.log(str(i.mean) + ' ' + str(i.sd))

self.schedule.on(self.date_rules.month_start(), self.time_rules.at(9,31), self._action)</pre>
</div>

<h3>Step 5: Performing Monthly Rebalancing</h3>
Expand All @@ -153,27 +157,26 @@ <h3>Step 5: Performing Monthly Rebalancing</h3>

<div class="section-example-container">

<pre class="python">def action(self):
<pre class="python">def _action(self):
rank = []
long_short = []
for i in self.quoted:
rank.append((i,self.predict(i)))
# rank the symbols by their expected return
for i in self._quoted:
rank.append((i,self._predict(i)))
# rank the symbols by their expected return
rank.sort(key = lambda x: x[1],reverse = True)
# the first element in long_short is the one with the highest expected return, which we are going to long, and the second one is going to be shorted.
# the first element in long_short is the one with the highest expected return, which we are going to long, and the second one is going to be shorted.
long_short.append(rank[0])
long_short.append(rank[-1])
self.Liquidate()

# the product &lt; 0 means the expected return of the first one is positive and that of the second one is negative--we are going to long and short.
if long_short[0][1]*long_short[1][1] &lt; 0:
self.SetHoldings(long_short[0][0],1)
self.SetHoldings(long_short[1][0],-1)
# this means we long only because all of the expected return is positive
elif long_short[0][1] &gt; 0 and long_short[1][1] &gt; 0:
self.SetHoldings(long_short[0][0],1)
# short only
self.liquidate()

# the product < 0 means the expected return of the first one is positive and that of the second one is negative--we are going to long and short.
if long_short[0][1]*long_short[1][1] < 0:
self.set_holdings(long_short[0][0],1)
self.set_holdings(long_short[1][0],-1)
# this means we long only because all of the expected return is positive
elif long_short[0][1] > 0 and long_short[1][1] > 0:
self.set_holdings(long_short[0][0],1)
# short only
else:
self.SetHoldings(long_short[1][0],-1)
</pre>
self.set_holdings(long_short[1][0],-1)</pre>
</div>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="qc-embed-frame" style="display: inline-block; position: relative; width: 100%; min-height: 100px; min-width: 300px;">
<div class="qc-embed-dummy" style="padding-top: 56.25%;"></div>
<div class="qc-embed-element" style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;">
<iframe class="qc-embed-backtest" height="100%" width="100%" style="border: 1px solid #ccc; padding: 0; margin: 0;" src="https://www.quantconnect.com/terminal/processCache/embedded_backtest_73883ad731bcac43e1a94b3983b5f8ca.html"></iframe>
<iframe class="qc-embed-backtest" height="100%" width="100%" style="border: 1px solid #ccc; padding: 0; margin: 0;" src="https://www.quantconnect.com/terminal/processCache?request=embedded_backtest_7c51024e7c791dfacbb9bef49571af13.html"></iframe>
</div>
</div>