Lesson 12 · Real-World Data Analytics
Python Data Analytics #12: Portfolio Returns, Risk & the Efficient Frontier in Python
Video twelve of the hundred-video real-world data analytics series. Building a real two-asset portfolio from the real same Apple and Tesla data cleaned last…
- CourseReal-World Data Analytics
- Lesson12 of 100
- Video31 min
- FormatJupyter notebook · 30 code cells
- Data1 dataset
What you'll learn
Datasets used in this lesson
Save these next to the notebook. In Google Colab, upload them with the 📁 icon on the left first.
- aapl_tsla_merged.csv17.9 KB
📓 Full notebook
Download .ipynbData Analytics 100, Video 12: Portfolio Returns, Risk, and the Efficient Frontier#
- Video twelve of the hundred-video real-world data analytics series.
- Building a real two-asset portfolio from the real same Apple and Tesla data cleaned last video, and tracing out its real efficient frontier.
- Let's get into it.
Part 1: Reusing the Real Cleaned Data#
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
merged = pd.read_csv('aapl_tsla_merged.csv', parse_dates=['Date'])
returns = merged[['AAPL_Return', 'TSLA_Return']].dropna()
returns.shape[0]
Part 2: Real Individual Asset Statistics#
mean_aapl = returns['AAPL_Return'].mean()
mean_tsla = returns['TSLA_Return'].mean()
std_aapl = returns['AAPL_Return'].std()
std_tsla = returns['TSLA_Return'].std()
round(mean_aapl, 5), round(mean_tsla, 5), round(std_aapl, 5), round(std_tsla, 5)
Part 3: Real Annualized Statistics#
annual_return_aapl = mean_aapl * 252
annual_return_tsla = mean_tsla * 252
annual_vol_aapl = std_aapl * np.sqrt(252)
annual_vol_tsla = std_tsla * np.sqrt(252)
round(annual_return_aapl * 100, 1), round(annual_return_tsla * 100, 1), round(annual_vol_aapl * 100, 1), round(annual_vol_tsla * 100, 1)
Part 4: Real Covariance and Correlation#
covariance = returns.cov().loc['AAPL_Return', 'TSLA_Return']
correlation = returns.corr().loc['AAPL_Return', 'TSLA_Return']
round(covariance, 6), round(correlation, 3)
Part 5: Real Portfolio Return Formula#
def portfolio_return(w_aapl, r_aapl=mean_aapl, r_tsla=mean_tsla):
return w_aapl * r_aapl + (1 - w_aapl) * r_tsla
round(portfolio_return(0.5), 5)
Part 6: Real Portfolio Variance Formula#
def portfolio_variance(w_aapl, s_aapl=std_aapl, s_tsla=std_tsla, cov=covariance):
w_tsla = 1 - w_aapl
return (w_aapl ** 2) * s_aapl ** 2 + (w_tsla ** 2) * s_tsla ** 2 + 2 * w_aapl * w_tsla * cov
round(portfolio_variance(0.5), 6)
Part 7: Real Grid of Portfolio Weights#
weights = np.linspace(0, 1, 41)
len(weights)
Part 8: Computing Every Real Candidate Portfolio#
results = []
for w in weights:
ann_ret = portfolio_return(w) * 252
ann_vol = np.sqrt(portfolio_variance(w)) * np.sqrt(252)
results.append((w, ann_ret, ann_vol))
frontier = pd.DataFrame(results, columns=['w_aapl', 'AnnualReturn', 'AnnualVol'])
frontier.head(3).round(4)
Part 9: Real Minimum-Variance Portfolio#
min_var_row = frontier.loc[frontier['AnnualVol'].idxmin()]
round(min_var_row['w_aapl'], 2), round(min_var_row['AnnualReturn'] * 100, 1), round(min_var_row['AnnualVol'] * 100, 1)
Part 10: Real Sharpe Ratio#
frontier['Sharpe'] = frontier['AnnualReturn'] / frontier['AnnualVol']
frontier[['w_aapl', 'Sharpe']].head(3).round(3)
Part 11: Real Maximum-Sharpe Portfolio#
max_sharpe_row = frontier.loc[frontier['Sharpe'].idxmax()]
round(max_sharpe_row['w_aapl'], 2), round(max_sharpe_row['AnnualReturn'] * 100, 1), round(max_sharpe_row['AnnualVol'] * 100, 1), round(max_sharpe_row['Sharpe'], 3)
Part 12: Visualizing the Real Efficient Frontier#
plt.figure(figsize=(9, 7))
plt.plot(frontier['AnnualVol'], frontier['AnnualReturn'], color='steelblue')
plt.scatter([annual_vol_aapl], [annual_return_aapl], color='green', s=100, label='Real 100% Apple', zorder=5)
plt.scatter([annual_vol_tsla], [annual_return_tsla], color='red', s=100, label='Real 100% Tesla', zorder=5)
plt.scatter([min_var_row['AnnualVol']], [min_var_row['AnnualReturn']], color='black', s=120, marker='*', label='Real min-variance portfolio', zorder=5)
plt.scatter([max_sharpe_row['AnnualVol']], [max_sharpe_row['AnnualReturn']], color='gold', s=120, marker='*', edgecolor='black', label='Real max-Sharpe portfolio', zorder=5)
plt.xlabel('Real Annualized Volatility')
plt.ylabel('Real Annualized Return')
plt.title('Real Efficient Frontier, Apple and Tesla')
plt.legend()
plt.tight_layout()
plt.savefig('efficient_frontier.png', dpi=120)
plt.close()
Part 13: Real Diversification Benefit#
naive_avg_vol = min_var_row['w_aapl'] * annual_vol_aapl + (1 - min_var_row['w_aapl']) * annual_vol_tsla
actual_vol = min_var_row['AnnualVol']
round((naive_avg_vol - actual_vol) * 100, 2)
Part 14: Real Equal-Weight Portfolio#
equal_weight_row = frontier.iloc[(frontier['w_aapl'] - 0.5).abs().idxmin()]
round(equal_weight_row['AnnualReturn'] * 100, 1), round(equal_weight_row['AnnualVol'] * 100, 1)
Part 15: Real Backtest, Three Portfolios Over Time#
min_var_w = min_var_row['w_aapl']
returns['MinVarPort'] = min_var_w * returns['AAPL_Return'] + (1 - min_var_w) * returns['TSLA_Return']
returns['MinVarCumulative'] = (1 + returns['MinVarPort']).cumprod()
returns['AAPLCumulative'] = (1 + returns['AAPL_Return']).cumprod()
returns['TSLACumulative'] = (1 + returns['TSLA_Return']).cumprod()
round(returns['MinVarCumulative'].iloc[-1], 3), round(returns['AAPLCumulative'].iloc[-1], 3), round(returns['TSLACumulative'].iloc[-1], 3)
Part 16: Visualizing the Real Backtest#
plt.figure(figsize=(10, 6))
plt.plot(merged['Date'].iloc[1:], returns['MinVarCumulative'], label='Real Min-Variance Portfolio')
plt.plot(merged['Date'].iloc[1:], returns['AAPLCumulative'], label='Real 100% Apple')
plt.plot(merged['Date'].iloc[1:], returns['TSLACumulative'], label='Real 100% Tesla')
plt.xlabel('Real Date')
plt.ylabel('Real Growth of $1 Invested')
plt.title('Real Backtest, Three Portfolio Strategies')
plt.legend()
plt.tight_layout()
plt.savefig('portfolio_backtest.png', dpi=120)
plt.close()
Part 17: Real Sanity Check on the Variance Formula#
manual_var_full_aapl = portfolio_variance(1.0)
abs(manual_var_full_aapl - std_aapl ** 2) < 1e-10
Part 18: Saving the Real Frontier Table#
frontier.round(5).to_csv('efficient_frontier_table.csv', index=False)
reloaded = pd.read_csv('efficient_frontier_table.csv')
reloaded.shape[0] == frontier.shape[0]
Part 19: Real Recap Print#
print(f'Minimum-variance portfolio: {round(min_var_row["w_aapl"]*100)}% Apple, {round((1-min_var_row["w_aapl"])*100)}% Tesla, {round(min_var_row["AnnualVol"]*100,1)}% annual volatility.')
Part 20: Real Rolling Correlation Over Time#
rolling_corr = returns['AAPL_Return'].rolling(30).corr(returns['TSLA_Return'])
rolling_corr.dropna().describe().round(3)
Part 21: Visualizing Real Rolling Correlation#
plt.figure(figsize=(10, 5))
plt.plot(merged['Date'].iloc[1:], rolling_corr, color='purple')
plt.axhline(correlation, color='gray', linestyle='--', label='Real full-period correlation')
plt.xlabel('Real Date')
plt.ylabel('Real 30-Day Rolling Correlation')
plt.title('Real Rolling Correlation, Apple and Tesla')
plt.legend()
plt.tight_layout()
plt.savefig('rolling_correlation.png', dpi=120)
plt.close()
Part 22: Real Sanity Check at Full Tesla Weight#
manual_var_full_tsla = portfolio_variance(0.0)
abs(manual_var_full_tsla - std_tsla ** 2) < 1e-10
Part 23: What If These Two Stocks Were Uncorrelated#
hypothetical_var = (0.5 ** 2) * std_aapl ** 2 + (0.5 ** 2) * std_tsla ** 2
actual_var_50 = portfolio_variance(0.5)
round(np.sqrt(hypothetical_var) * np.sqrt(252) * 100, 2), round(np.sqrt(actual_var_50) * np.sqrt(252) * 100, 2)
Part 24: Real Total Return Comparison Over the Full Window#
total_return_aapl = returns['AAPLCumulative'].iloc[-1] - 1
total_return_tsla = returns['TSLACumulative'].iloc[-1] - 1
round(total_return_aapl * 100, 1), round(total_return_tsla * 100, 1)
Part 25: Real Days Each Stock Outperformed the Other#
aapl_wins = (returns['AAPL_Return'] > returns['TSLA_Return']).sum()
tsla_wins = (returns['TSLA_Return'] > returns['AAPL_Return']).sum()
aapl_wins, tsla_wins
Part 26: Real Downside-Only Risk, the Sortino Idea#
downside_aapl = returns['AAPL_Return'][returns['AAPL_Return'] < 0].std()
downside_tsla = returns['TSLA_Return'][returns['TSLA_Return'] < 0].std()
round(downside_aapl * np.sqrt(252) * 100, 1), round(downside_tsla * np.sqrt(252) * 100, 1)
Part 27: Real Min-Variance vs Real Max-Sharpe Side by Side#
comparison = pd.DataFrame({'Portfolio': ['Min-Variance', 'Max-Sharpe'], 'AppleWeight': [min_var_row['w_aapl'], max_sharpe_row['w_aapl']], 'AnnualReturn': [min_var_row['AnnualReturn'], max_sharpe_row['AnnualReturn']], 'AnnualVol': [min_var_row['AnnualVol'], max_sharpe_row['AnnualVol']]})
comparison.round(3)
Part 28: Real Range of Achievable Portfolio Returns#
frontier['AnnualReturn'].min(), frontier['AnnualReturn'].max()
Part 29: Real Volatility Reduction from Min-Variance vs Naive 50/50#
vol_50_50 = frontier.loc[(frontier['w_aapl'] - 0.5).abs().idxmin(), 'AnnualVol']
round((vol_50_50 - min_var_row['AnnualVol']) * 100, 2)
Part 30: Real Final Recap Numbers#
print(f'Tested {len(frontier)} real candidate portfolios; correlation between Apple and Tesla over this window was {round(correlation, 2)}.')
Wrap-Up: What You Learned#
- Portfolio return is genuinely just a weighted average of the real individual asset returns.
- Portfolio variance is not a plain weighted average; the real covariance term is exactly what captures diversification benefit.
- Sweeping a real grid of weights traces out the real efficient frontier, every achievable risk-return combination between two assets.
- The real minimum-variance portfolio and the real maximum-Sharpe portfolio are two different, both genuinely useful, optimal points on that real frontier.
- A real backtest turns the theory concrete: tracking how one real dollar would have actually grown under each real strategy.
- Next video: real technical indicators, moving averages and momentum signals computed directly on these real price series.
Found this useful?
All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.



