diff --git a/04 Strategy Library/02 Combining Mean Reversion and Momentum in Forex Market/03 Method.html b/04 Strategy Library/02 Combining Mean Reversion and Momentum in Forex Market/03 Method.html index e74071f..fda68e8 100755 --- a/04 Strategy Library/02 Combining Mean Reversion and Momentum in Forex Market/03 Method.html +++ b/04 Strategy Library/02 Combining Mean Reversion and Momentum in Forex Market/03 Method.html @@ -4,31 +4,32 @@
- The first function takes two arguments: symbol and number of daily data points requested. This function requests historical QuoteBars and builds it into a pandas DataFrame. For more information about pandas DataFrame, please refer to the help documentation DataFrame. 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 QuoteBars and builds it into a pandas DataFrame. For more information about pandas DataFrame, please refer to the help documentation DataFrame. 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.
def get_history(self,symbol, num): +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) @@ -45,67 +46,69 @@Step 1: Request Historical Data
Step 2: Build Predictive Model
- 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
_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.-def concat(self): +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() < 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 resStep 3: Apply Predictive Model
- 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
_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.-def predict(self,symbol): +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 -+ return df@@ -121,29 +124,30 @@
Step 3: Apply Predictive Model
Step 4: Initializing the Model
- 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 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.-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)) -+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)Step 5: Performing Monthly Rebalancing
@@ -153,27 +157,26 @@Step 5: Performing Monthly Rebalancing
-diff --git a/04 Strategy Library/02 Combining Mean Reversion and Momentum in Forex Market/06 Algorithm.html b/04 Strategy Library/02 Combining Mean Reversion and Momentum in Forex Market/06 Algorithm.html index 9717146..8199c1d 100755 --- a/04 Strategy Library/02 Combining Mean Reversion and Momentum in Forex Market/06 Algorithm.html +++ b/04 Strategy Library/02 Combining Mean Reversion and Momentum in Forex Market/06 Algorithm.html @@ -1,6 +1,6 @@def action(self): +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 < 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.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] > 0 and long_short[1][1] > 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) -+ self.set_holdings(long_short[1][0],-1)