Open Midterm 1#
FINM 36700 - 2025#
UChicago Financial Mathematics#
Mark Hendricks
Instructions#
Please note the following:#
Points
For every minute late you submit the exam, you will lose one point.
Rules
The exam is open-material, closed-communication.
Advice
If you find any question to be unclear, state your interpretation and proceed. We will only answer questions of interpretation if there is a typo, error, etc.
Data#
All data files are found in at the course web-book.
Scoring#
Problem |
Points |
|---|---|
1 |
70 |
2 |
30 |
Numbered problems are worth 5pts unless specified otherwise.
Submitting your Exam#
Your submitted file (ipynb or .zip) must be named in the format…
midterm-1-LASTNAME-FIRSTNAME.ipynbmidterm-1-LASTNAME-FIRSTNAME.zip
Submit the exam via
Canvas at Assignments/Midterm 1
If there is any trouble with Canvas, or for a backup, submit it at the course web-book https://markhendricks.github.io/finm-portfolio/.
Your submission must be complete.
If we can’t run the notebook to reproduce, it is not complete.
You should either…
include all helper functions at the top of this notebook in the
Solution Functionssections.submit a zipped folder containing the solution functions
Exam Submission Structure:
If you are submitting a zipped folder (because you cannot make the ipynb stand-alone) then use this structure…
exam-open-LASTNAME-FIRSTNAME.zip/
│── exam-open.ipynb
│── data/
│ ├── example_data.csv
│── modules/
│ ├── my_functions.py
Your Functions#
Please put all functions needed to run the ipynb here.
We must be able to run the notebook!
If you can’t get all the functions into this ipynb, then make sure to zip your ipynb and supporting materials and submit the zipped folder.
But most of you should be able to submit a single, stand-alone ipynb.
Citations#
AI#
List any AI tools used in the exam. No need to list prompts, but rather just AI models or IDE integrations.
I expect most students will have something to list here.
Other resources#
Please list any other resources aside from course materials from which you used substantially. (No need to list every Google search; just materials from which you used substantially or for specific, original content.)
I expect most students will not have anything to list here.
Your Name#
List your name and CNetID
Name:
CNetID:
1. Portfolio Analysis#
Data#
Use the data in data/midterm_1_stock_returns.xlsx.
The returns are…
excess returns
weekly
through
May 2025
It has returns for
25single-name equitiesSPY
import pandas as pd
DATAFILE_STOCKS = '../data/midterm_1_stock_returns.xlsx'
SHEET = 'stock rets'
rets = pd.read_excel(DATAFILE_STOCKS, sheet_name=SHEET, index_col='date', parse_dates=['date'])
SHEET = 'benchmark rets'
spy = pd.read_excel(DATAFILE_STOCKS, sheet_name=SHEET, index_col='date', parse_dates=['date'])
display(rets.head())
display(spy.tail())
| ADSK | AOS | BKNG | CBRE | CCI | CF | CHRW | DE | DGX | DTE | ... | MRK | MTD | PG | PNR | SBAC | STE | TTWO | VTRS | WM | WMT | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| date | |||||||||||||||||||||
| 2015-01-09 | -0.021166 | -0.008927 | -0.078893 | -0.000577 | 0.026536 | 0.069378 | -0.024107 | -0.030450 | -0.003635 | -0.002414 | ... | 0.093897 | -0.008843 | -0.002101 | -0.023393 | -0.001091 | 0.013659 | -0.008009 | -0.009050 | -0.004251 | 0.040160 |
| 2015-01-16 | -0.024369 | -0.015313 | -0.041579 | -0.045022 | 0.012374 | 0.001515 | 0.019290 | 0.019265 | 0.019738 | 0.036405 | ... | 0.007512 | -0.024325 | 0.011081 | -0.009830 | -0.012042 | -0.020590 | 0.048080 | 0.002865 | 0.013778 | -0.028873 |
| 2015-01-23 | 0.023571 | 0.016282 | 0.029527 | -0.004231 | 0.050829 | 0.013841 | 0.009121 | 0.012027 | 0.016490 | 0.014229 | ... | -0.008567 | 0.037842 | -0.005804 | -0.003565 | 0.080951 | 0.023033 | 0.022253 | -0.031965 | 0.014548 | 0.020053 |
| 2015-01-30 | -0.071920 | 0.069487 | -0.027466 | -0.018513 | -0.003684 | 0.012031 | -0.039124 | -0.035766 | 0.002540 | -0.017317 | ... | -0.035366 | 0.002970 | -0.064277 | -0.033041 | -0.014190 | -0.014506 | -0.004689 | -0.019552 | -0.029622 | -0.039883 |
| 2015-02-06 | 0.056754 | 0.037577 | 0.012818 | 0.048856 | 0.001733 | -0.028619 | -0.009970 | 0.044489 | -0.020120 | -0.049186 | ... | -0.024717 | 0.016318 | 0.015660 | 0.040609 | 0.010108 | 0.026985 | -0.031460 | 0.013922 | 0.019639 | 0.027653 |
5 rows × 25 columns
| SPY | |
|---|---|
| date | |
| 2025-04-25 | 0.046029 |
| 2025-05-02 | 0.029275 |
| 2025-05-09 | -0.004270 |
| 2025-05-16 | 0.052911 |
| 2025-05-23 | -0.025395 |
1 Performance Stats#
1.1. Calculate the Sharpe ratio for each stock during the sample period#
Recall: the sample period ranges from January 2015 to December 2024 (inclusive).
Report the top 5 stocks with the highest Sharpe ratios.
1.2. Display the correlation matrix for the first ten stocks (columns) over the sample period.#
On average, are these stocks highly correlated? Explain.
Which of these stocks offer the best diversification benefits?
2. In-Sample Tangency (excess returns)#
Note#
Consider in-sample to be all the data through the end of 2024.
2.1. Construct the tangency portfolio#
Using just the in-sample data (through 2024), calculate the tangency portfolio weights, assuming we have excess returns (existence of a risk-free rate.)
Display the ten largest portfolio weights.
Plot the Sharpe ratios against the portfolio weights.
2.2.#
Compare the relationship between tangency portfolio weights and individual sharpe ratios.
2.3. Performance of the Tangency#
Continue with the in-sample tangency portfolio constructed above, and analyze how it performs in-sample (through 2024.)
Report the (annualized)
mean
volatility
Sharpe ratio
skewness (not annualized)
Plot the cumulative return of the tangency portfolio over the sample period.
3. Hedging the Tangency Portfolio#
Continue with the in-sample (through 2024) tangency returns calculated in the previous problem.
3.1.#
Compute portfolio returns and regress on SPY to get \(\hat{\beta}\).
Include an intercept in the regression.
Report \(\hat{\beta}\).
3.2.#
Calculate the returns to the hedged position.
Report the (annualized)
mean
volatility
Sharpe ratio
skewness (not annualized)
4. Out-of-Sample#
4.1. Tangency Portfolio Performance: Out-of-Sample (OOS)#
Use the weights of the tangency portfolio calculated above.
Compute the out-of-sample returns (2025), and just for this OOS portion, report the (annualized)
mean
volatility
Sharpe ratio
skewness (not annualized)
4.2. Cumulative performance#
Include the OOS performance in the cumulative return plot (in addition to the in-sample performance).
Show the plot.
5. Optimizing Hedged Returns#
5.1. Construct Market-Hedged Returns#
Active managers might optimize their portfolios using market-hedged returns to focus on alpha generation (maximize portion of returns orthogonal to the market). Market-hedged returns are the residuals from regressing each stock’s excess return on the market’s excess return (e.g., SPY), effectively removing market beta to isolate stock-specific (idiosyncratic) performance.
Regress each stock’s excess return on the SPY index (quoted in excess return) over the sample period.
Include an intercept.
Report your betas.
5.2. The residuals#
Save the residuals from each regression as the market-hedged returns.
Report the .tail() (last 5 observations) of the residual dataframe.
5.3 Diversification Benefits of Market-Hedged Returns#
Display the covariance matrix of the market-hedged returns for the first ten stocks.
5.4. Portfolio Optimization with Market-Hedged Returns#
Construct the tangency portfolio using the alphas (intercept from previous regression) as expected returns and the covariance matrix of the market-hedged returns. Display the portfolio weights.
5.5. Performance of the Tangency of the Hedged#
Calculate the returns to the portfolio with weights from the previous question (just in-sample).
Report the (annualized)
mean
volatility
Sharpe ratio
skewness (not annualized)
2. Managing Risk#
DATAFILE = '../data/midterm_1_fund_returns.xlsx'
df = pd.read_excel(DATAFILE, sheet_name='fund returns').set_index('date')
df
| fund | |
|---|---|
| date | |
| 2015-01-09 | 0.003444 |
| 2015-01-16 | -0.000959 |
| 2015-01-23 | 0.004491 |
| 2015-01-30 | 0.010560 |
| 2015-02-06 | -0.001624 |
| ... | ... |
| 2024-11-29 | 0.013893 |
| 2024-12-06 | -0.027113 |
| 2024-12-13 | 0.002755 |
| 2024-12-20 | 0.020840 |
| 2024-12-27 | -0.046301 |
521 rows × 1 columns
1. Calculating Volatility#
Given the return data provided, calculate the annual volatility grouped by year. Annualize this volatility. That is, your answer should be a DataFrame with 10 rows (one for each year from 2015 to 2024) and a single column representing the annualized volatility for that year.
What do you notice about the volatility across different years?
2. Volatility Estimates#
Using the return data, compute the annualized volatility estimate as of the last data point using the following methods:
EWMA volatility with a half-life of 26 weeks.
Rolling window volatility with a window size of 52 weeks.
Note: do not specify a “min_periods” argument anywhere.
3. Dynamic VaR estimates#
Using the return data, compute the 1-week 1% VaR as of the last data point in the series.
You should use two different volatility models to compute the VaR:
EWMA volatility with a half-life of 26 weeks.
Rolling-window volatility with a window of 52 weeks.
Empirical (expanding) VaR.
4. Dynamic CVaR estimates#
Repeat Question 3, but instead compute the 1-week 1% Conditional VaR (CVaR) as of the last data point in the series using the same three methods.
5. Year Choice#
Suppose instead we were interested in our VaR estimates as of the end of 2023 (ie. use all data that is before 2024-01-01). Report your VaR (1-week, 1%) estimates using the same three methods as in Question 3, but now the last data point in 2023.
Hint: You can use df.loc[:'2023'] to filter the DataFrame to only include data up to the end of 2023.
6. Compare#
What do you notice about the VaR estimates compared to those from question 2.3?