<jupyter_start><jupyter_text>Factor Model of Portfolio Return<jupyter_code>import sys
!{sys.executable} -m pip install -r requirements.txt
import numpy as np
import pandas as pd
import time
import os
import quiz_helper
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('ggplot')
plt.rcParams['figure.figsize'] = (14, 8)<jupyter_output><empty_output><jupyter_text>data bundle<jupyter_code>import os
import quiz_helper
from zipline.data import bundles
os.environ['ZIPLINE_ROOT'] = os.path.join(os.getcwd(), '..', '..','data','module_4_quizzes_eod')
ingest_func = bundles.csvdir.csvdir_equities(['daily'], quiz_helper.EOD_BUNDLE_NAME)
bundles.register(quiz_helper.EOD_BUNDLE_NAME, ingest_func)
print('Data Registered')<jupyter_output><empty_output><jupyter_text>Build pipeline engine<jupyter_code>from zipline.pipeline import Pipeline
from zipline.pipeline.factors import AverageDollarVolume
from zipline.utils.calendars import get_calendar

universe = AverageDollarVolume(window_length=120).top(500) 
trading_calendar = get_calendar('NYSE') 
bundle_data = bundles.load(quiz_helper.EOD_BUNDLE_NAME)
engine = quiz_helper.build_pipeline_engine(bundle_data, trading_calendar)<jupyter_output><empty_output><jupyter_text>View Data¶With the pipeline engine built, let's get the stocks at the end of the period in the universe we're using. We'll use these tickers to generate the returns data for the our risk model.<jupyter_code>universe_end_date = pd.Timestamp('2016-01-05', tz='UTC')

universe_tickers = engine\
    .run_pipeline(
        Pipeline(screen=universe),
        universe_end_date,
        universe_end_date)\
    .index.get_level_values(1)\
    .values.tolist()
    
universe_tickers
len(universe_tickers)
from zipline.data.data_portal import DataPortal

data_portal = DataPortal(
    bundle_data.asset_finder,
    trading_calendar=trading_calendar,
    first_trading_day=bundle_data.equity_daily_bar_reader.first_trading_day,
    equity_minute_reader=None,
    equity_daily_reader=bundle_data.equity_daily_bar_reader,
    adjustment_reader=bundle_data.adjustment_reader)<jupyter_output><empty_output><jupyter_text>Get pricing data helper function<jupyter_code>from quiz_helper import get_pricing<jupyter_output><empty_output><jupyter_text>get pricing data into a dataframe<jupyter_code>returns_df = \
    get_pricing(
        data_portal,
        trading_calendar,
        universe_tickers,
        universe_end_date - pd.DateOffset(years=5),
        universe_end_date)\
    .pct_change()[1:].fillna(0) #convert prices into returns

returns_df<jupyter_output><empty_output><jupyter_text>Let's look at a two stock portfolioLet's pretend we have a portfolio of two stocks.  We'll pick Apple and Microsoft in this example.<jupyter_code>aapl_col = returns_df.columns[3]
msft_col = returns_df.columns[312]
asset_return_1 = returns_df[aapl_col].rename('asset_return_aapl')
asset_return_2 = returns_df[msft_col].rename('asset_return_msft')
asset_return_df = pd.concat([asset_return_1,asset_return_2],axis=1)
asset_return_df.head(2)<jupyter_output><empty_output><jupyter_text>Factor returnsLet's make up a "factor" by taking an average of all stocks in our list.  You can think of this as an equal weighted index of the 490 stocks, kind of like a measure of the "market".  We'll also make another factor by calculating the median of all the stocks.  These are mainly intended to help us generate some data to work with.  We'll go into how some common risk factors are generated later in the lessons.Also note that we're setting axis=1 so that we calculate a value for each time period (row) instead of one value for each column (assets).<jupyter_code>factor_return_1 = returns_df.mean(axis=1)
factor_return_2 = returns_df.median(axis=1)
factor_return_l = [factor_return_1, factor_return_2]<jupyter_output><empty_output><jupyter_text>Factor exposuresFactor exposures refer to how "exposed" a stock is to each factor.  We'll get into this more later.  For now, just think of this as one number for each stock, for each of the factors.<jupyter_code>from sklearn.linear_model import LinearRegression
"""
For now, just assume that we're calculating a number for each 
stock, for each factor, which represents how "exposed" each stock is
to each factor. 
We'll discuss how factor exposure is calculated later in the lessons.
"""
def get_factor_exposures(factor_return_l, asset_return):
    lr = LinearRegression()
    X = np.array(factor_return_l).T
    y = np.array(asset_return.values)
    lr.fit(X,y)
    return lr.coef_
factor_exposure_l = []
for i in range(len(asset_return_df.columns)):
    factor_exposure_l.append(
        get_factor_exposures(factor_return_l,
                             asset_return_df[asset_return_df.columns[i]]
                            ))
    
factor_exposure_a = np.array(factor_exposure_l)
print(f"factor_exposures for asset 1 {factor_exposure_a[0]}")
print(f"factor_exposures for asset 2 {factor_exposure_a[1]}")<jupyter_output><empty_output><jupyter_text>Quiz 1 Portfolio's factor exposuresLet's make up some portfolio weights for now; in a later lesson, we'll look at how portfolio optimization combines alpha factors and a risk factor model to choose asset weights.$\beta_{p,k} = \sum_{i=1}^{N}(x_i \times \beta_{i,k})$<jupyter_code>weight_1 = 0.60 #let's give AAPL a portfolio weight
weight_2 = 0.40 #give MSFT a portfolio weight
weight_a = np.array([weight_1, weight_2])<jupyter_output><empty_output><jupyter_text>For the sake of understanding, try saving each of the valuesinto a separate variable to perform the multipliations and additionsCheck that your calculations for portfolio factor exposure matchthe output of this dot product:```weight_a.dot(factor_exposure_a)```<jupyter_code># TODO: calculate portfolio's exposure to factor 1
factor_exposure_1_1 = # ...
factor_exposure_2_1 = # ...
factor_exposure_p_1 = # ...
factor_exposure_p_1
# TODO: calculate portfolio's exposure to factor 2
factor_exposure_1_2 = # ...
factor_exposure_2_2 = # ...
factor_exposure_p_2 = # ...
factor_exposure_p_2<jupyter_output><empty_output><jupyter_text>Quiz 2 Calculate portfolio returnFor clarity, try storing the pieces into their own named variables and writing out the multiplications and addition.You can check if your answer matches this output:```asset_return_df.values.dot(weight_a)```<jupyter_code># TODO calculate the portfolio return
asset_return_1 = # ...
asset_return_2 = # ...
portfolio_return = # ...

portfolio_return = pd.Series(portfolio_return,index=asset_return_df.index).rename('portfolio_return')
portfolio_return.head(2)<jupyter_output><empty_output><jupyter_text>Quiz 3 Contribution of FactorsThe sum of the products of factor exposure times factor return is the contribution of the factors.  It's also called the "common return." calculate the common return of the portfolio, given the two factor exposures and the two factor returns.<jupyter_code># TODO: Calculate the contribution of the two factors to the return of this example asset
common_return = # ...
common_return = common_return.rename('common_return')

common_return.head(2)<jupyter_output><empty_output><jupyter_text>Quiz 4 Specific ReturnThe specific return is the part of the portfolio return that isn't explained by the factors.  So it's the actual return minus the common return.  Calculate the specific return of the stock.<jupyter_code># TODO: calculate the specific return of this asset
specific_return = # ...
specific_return = specific_return.rename('specific_return')<jupyter_output><empty_output><jupyter_text>Visualize the common return and specific return<jupyter_code>return_components = pd.concat([common_return,specific_return],axis=1)
return_components.head(2)
return_components.plot(title="asset return = common return + specific return");
pd.DataFrame(portfolio_return).plot(color='purple');<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08569612205975842, 'fraction_numerical': 0.010298792116973935, 'mean_word_length': 5.973404255319149, 'pattern_counts': {'":': 0, '<': 61, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '15', 'dir': 'jupyter-structured-clean-dedup', 'id': '35522', 'license': 'Apache-2.0', 'n_tokens_mistral': 2559, 'n_tokens_neox': 2435, 'n_words': 673, 'path': 'Risk Factor Models/factor_model_portfolio_return.ipynb', 'repo_name': '2series/Trading-AI-Style', 'size': '7865'}