Creating a Mutual Fund Plan with Python
Mutual funds are investment plans that pool money from multiple investors to purchase a diversified portfolio of stocks, bonds, and other securities, managed by professional fund managers. A mutual fund plan is created by selecting the stocks where an investor can benefit in the long term.
A mutual fund plan is created by selecting the stocks where an investor can benefit in the long term. Here’s the process we can follow to create a mutual fund plan:
Step 1: Gather historical stock data, such as closing prices and growth trends over time.
Step 2: Calculate key metrics like Return on Investment (ROI) and volatility (risk) to understand how each stock has performed historically.
Step 3: Choose stocks that have a high ROI and low volatility to ensure a balance between risk and reward.
Step 4: Calculate the future value of monthly investments based on the expected ROI of the selected stocks.
1 | !pip install yfinance |
Requirement already satisfied: yfinance in / usr / local / lib / python3. 10 / dist - packages ( 0.2 . 50 ) Requirement already satisfied: pandas> = 1.3 . 0 in / usr / local / lib / python3. 10 / dist - packages ( from yfinance) ( 2.2 . 2 ) Requirement already satisfied: numpy> = 1.16 . 5 in / usr / local / lib / python3. 10 / dist - packages ( from yfinance) ( 1.26 . 4 ) Requirement already satisfied: requests> = 2.31 in / usr / local / lib / python3. 10 / dist - packages ( from yfinance) ( 2.32 . 3 ) Requirement already satisfied: multitasking> = 0.0 . 7 in / usr / local / lib / python3. 10 / dist - packages ( from yfinance) ( 0.0 . 11 ) Requirement already satisfied: lxml> = 4.9 . 1 in / usr / local / lib / python3. 10 / dist - packages ( from yfinance) ( 5.3 . 0 ) Requirement already satisfied: platformdirs> = 2.0 . 0 in / usr / local / lib / python3. 10 / dist - packages ( from yfinance) ( 4.3 . 6 ) Requirement already satisfied: pytz> = 2022.5 in / usr / local / lib / python3. 10 / dist - packages ( from yfinance) ( 2024.2 ) Requirement already satisfied: frozendict> = 2.3 . 4 in / usr / local / lib / python3. 10 / dist - packages ( from yfinance) ( 2.4 . 6 ) Requirement already satisfied: peewee> = 3.16 . 2 in / usr / local / lib / python3. 10 / dist - packages ( from yfinance) ( 3.17 . 8 ) Requirement already satisfied: beautifulsoup4> = 4.11 . 1 in / usr / local / lib / python3. 10 / dist - packages ( from yfinance) ( 4.12 . 3 ) Requirement already satisfied: html5lib> = 1.1 in / usr / local / lib / python3. 10 / dist - packages ( from yfinance) ( 1.1 ) Requirement already satisfied: soupsieve> 1.2 in / usr / local / lib / python3. 10 / dist - packages ( from beautifulsoup4> = 4.11 . 1 - >yfinance) ( 2.6 ) Requirement already satisfied: six> = 1.9 in / usr / local / lib / python3. 10 / dist - packages ( from html5lib> = 1.1 - >yfinance) ( 1.17 . 0 ) Requirement already satisfied: webencodings in / usr / local / lib / python3. 10 / dist - packages ( from html5lib> = 1.1 - >yfinance) ( 0.5 . 1 ) Requirement already satisfied: python - dateutil> = 2.8 . 2 in / usr / local / lib / python3. 10 / dist - packages ( from pandas> = 1.3 . 0 - >yfinance) ( 2.8 . 2 ) Requirement already satisfied: tzdata> = 2022.7 in / usr / local / lib / python3. 10 / dist - packages ( from pandas> = 1.3 . 0 - >yfinance) ( 2024.2 ) Requirement already satisfied: charset - normalizer< 4 ,> = 2 in / usr / local / lib / python3. 10 / dist - packages ( from requests> = 2.31 - >yfinance) ( 3.4 . 0 ) Requirement already satisfied: idna< 4 ,> = 2.5 in / usr / local / lib / python3. 10 / dist - packages ( from requests> = 2.31 - >yfinance) ( 3.10 ) Requirement already satisfied: urllib3< 3 ,> = 1.21 . 1 in / usr / local / lib / python3. 10 / dist - packages ( from requests> = 2.31 - >yfinance) ( 2.2 . 3 ) Requirement already satisfied: certifi> = 2017.4 . 17 in / usr / local / lib / python3. 10 / dist - packages ( from requests> = 2.31 - >yfinance) ( 2024.8 . 30 ) |
Importing Required Libraries
import yfinance as yf import pandas as pd from datetime import date, timedelta, datetime |
Defining Tickers and Time Range
# List of example tickers across various sectors (ticker are symbols, a unique combination of letters and numbers that represent a particular stock or security listed on an exchange.) tickers = [ "JPM" , "BAC" , "WFC" , "C" , "GS" , "AAPL" , "MSFT" , "GOOGL" , "AMZN" , "FB" , "PG" , "KO" , "PEP" , "CL" , "MNST" , "TSLA" , "GM" , "F" , "HMC" , "TM" , "XOM" , "CVX" , "JNJ" , "PFE" , "MRK" , "NKE" , "DIS" , "VZ" , "T" , "CSCO" , "ORCL" , "IBM" , "INTC" , "AMD" , "NVDA" , "ADBE" , "NFLX" , "COST" , "WMT" , "HD" , "MCD" , "SBUX" , "UPS" , "FDX" , "CAT" , "MMM" , "BA" , "GE" , "HON" , "DOW" ] |
end_date = datetime.today() print (end_date) |
2024-12-14 20:55:53.790207
start_date = end_date - timedelta(days = 365 ) #just selected for the past 1 year, but this can be easily modified with prefrence i.e (days = year*365) print (start_date) |
Downloading the Close Prices
close_df = pd.DataFrame() |
for ticker in tickers: data = yf.download(ticker, start = start_date, end = end_date) data.reset_index(inplace = True ) close_df[ 'Date' ] = data[ 'Date' ] close_df[ticker] = data[ 'Close' ] # Remove any duplicate 'Date' columns if present close_df = close_df.loc[:, ~close_df.columns.duplicated()] |
print (close_df) |
data[ 'Date' ] = pd.to_datetime(data[ 'Date' ]) |
print (data) |
import plotly.graph_objs as go import plotly.express as px fig = go.Figure() for company in close_df.columns[ 1 :]: # skip 'Date' column fig.add_trace(go.Scatter(x = close_df[ 'Date' ], y = close_df[company], mode = 'lines' , name = company, opacity = 0.5 )) fig.update_layout( title = 'Stock Price Trends of Top 50 S&P 500 Companies' , xaxis_title = 'Date' , yaxis_title = 'Closing Price (USD)' , xaxis = dict (tickangle = 45 ), legend = dict ( x = 1.05 , y = 1 , traceorder = "normal" , font = dict (size = 10 ), orientation = "v" ), margin = dict (l = 0 , r = 0 , t = 30 , b = 0 ), hovermode = 'x' , template = 'plotly_white' ) fig.show() |
import plotly.graph_objs as go import plotly.express as px from plotly.subplots import make_subplots # Create a more interactive and visually appealing figure fig = make_subplots(specs = [[{ "secondary_y" : True }]]) # Define color palette for lines colors = px.colors.qualitative.Plotly # Get a set of unique colors # Add a line trace for each company with enhanced hover info and colors for i, company in enumerate (close_df.columns[ 1 :]): # skip 'Date' column fig.add_trace(go.Scatter( x = close_df[ 'Date' ], y = close_df[company], mode = 'lines' , name = company, line = dict (color = colors[i % len (colors)], width = 1.5 ), # Use colors in a loop opacity = 0.7 , hovertemplate = f "<b>{company}</b><br>Date: {{x}}<br>Closing Price: {{y}}<extra></extra>" )) # Update layout with enhanced styling fig.update_layout( title = { 'text' : 'Stock Price Trends of Top 50 S&P 500 Companies' , 'y' : 0.9 , 'x' : 0.5 , 'xanchor' : 'center' , 'yanchor' : 'top' , 'font' : dict (size = 20 , color = "RebeccaPurple" ) }, xaxis = dict ( title = 'Date' , tickangle = 45 , tickformat = '%b %d, %Y' , rangeslider = dict (visible = True ) # Add a range slider for better navigation ), yaxis_title = 'Closing Price (USD)' , legend = dict ( x = 1.05 , y = 1 , traceorder = "normal" , font = dict (size = 10 ), orientation = "v" ), margin = dict (l = 20 , r = 20 , t = 50 , b = 50 ), hovermode = 'x unified' , # Unified hover mode for a cleaner look template = 'plotly_dark' , # Dark theme for a more professional look ) # Add animation options fig.update_layout( updatemenus = [ dict ( type = "buttons" , direction = "left" , buttons = [ dict (label = "Play" , method = "animate" , args = [ None , dict (frame = dict (duration = 100 , redraw = True ), fromcurrent = True )])], pad = { "r" : 10 , "t" : 10 }, showactive = True , x = 0.1 , xanchor = "right" , y = 1.15 , yanchor = "top" )] ) # Set up frames for animation frames = [go.Frame( data = [go.Scatter( x = close_df[ 'Date' ][:k + 1 ], y = close_df[company][:k + 1 ] ) for company in close_df.columns[ 1 :]], name = f "frame{k}" ) for k in range ( 0 , len (close_df[ 'Date' ]), 5 )] # Adjust step for smoother/faster animation fig.frames = frames fig.show() |
Let’s look at the companies with the highest risks for investing:
all_companies = close_df.columns[ 1 :] # Calculate the standard deviation (volatility) for each company's closing price volatility_all_companies = close_df[all_companies].std() # Sort and display the top 10 companies with the highest volatility top_10_volatile_companies = volatility_all_companies.sort_values(ascending = False ).head( 10 ) print ( "Top 10 Companies with Highest Volatility (Investment Risk):" ) print (top_10_volatile_companies) |
Now, let’s look at the companies with the highest growth rate for investing:
growth_all_companies = close_df[all_companies].pct_change() * 100 average_growth_all_companies = growth_all_companies.mean() average_growth_all_companies.sort_values(ascending = False ).head( 10 ) |
Now, let’s have a look at the companies with the highest return on investments:
initial_prices_all = close_df[all_companies].iloc[ 0 ] final_prices_all = close_df[all_companies].iloc[ - 1 ] roi_all_companies = ((final_prices_all - initial_prices_all) / initial_prices_all) * 100 roi_all_companies.sort_values(ascending = False ).head( 10 ) |
To create a strategy for selecting companies with high ROI and low risk, we can use a combination of ROI and volatility (standard deviation) metrics. The goal is to find companies that offer a high return on investment (ROI) but with low volatility to minimize risk.
Here are the steps we can follow for creating a mutual fund plan:
Let’s start by defining thresholds and selecting companies that meet the criteria of high ROI and low volatility:
roi_threshold = roi_all_companies.median() volatility_threshold = volatility_all_companies.median() selected_companies = roi_all_companies[(roi_all_companies > roi_threshold) & (volatility_all_companies < volatility_threshold)] selected_companies.sort_values(ascending = False ) |
roi_threshold = roi_all_companies.median() volatility_threshold = volatility_all_companies.median() selected_companies = roi_all_companies[(roi_all_companies > roi_threshold) & (volatility_all_companies < volatility_threshold)] selected_companies = selected_companies.sort_values(ascending = False ) formatted_output = [f "{company} (ROI: {roi:.2f}%)" for company, roi in selected_companies.items()] print ( "The following companies meet the criteria of high ROI and low volatility:\n" ) for line in formatted_output: print (line) |
To balance the investment between these companies, we can use an inverse volatility ratio for allocation. Companies with lower volatility will get a higher weight. Let’s calculate the weight for each company:
selected_volatility = volatility_all_companies[selected_companies.index] inverse_volatility = 1 / selected_volatility investment_ratios = inverse_volatility / inverse_volatility. sum () investment_ratios.sort_values(ascending = False ) |
We have created a mutual fund plan for long-term investments. Now, let’s analyze and compare our mutual fund plan by comparing it with the high-performing companies in the stock market. Let’s start by comparing the risks in our mutual fund with the risk in the high growth companies:
top_growth_companies = average_growth_all_companies.sort_values(ascending = False ).head( 10 ) risk_growth_rate_companies = volatility_all_companies[top_growth_companies.index] risk_mutual_fund_companies = volatility_all_companies[selected_companies.index] fig = go.Figure() fig.add_trace(go.Bar( y = risk_mutual_fund_companies.index, x = risk_mutual_fund_companies, orientation = 'h' , # Horizontal bar name = 'Mutual Fund Companies' , marker = dict (color = 'blue' ) )) fig.add_trace(go.Bar( y = risk_growth_rate_companies.index, x = risk_growth_rate_companies, orientation = 'h' , name = 'Growth Rate Companies' , marker = dict (color = 'green' ), opacity = 0.7 )) fig.update_layout( title = 'Risk Comparison: Mutual Fund vs Growth Rate Companies' , xaxis_title = 'Volatility (Standard Deviation)' , yaxis_title = 'Companies' , barmode = 'overlay' , legend = dict (title = 'Company Type' ), template = 'plotly_white' ) fig.show() |
Now, let’s compare the ROI of both the groups as well:
expected_roi_mutual_fund = roi_all_companies[selected_companies.index] expected_roi_growth_companies = roi_all_companies[top_growth_companies.index] fig = go.Figure() fig.add_trace(go.Bar( y = expected_roi_mutual_fund.index, x = expected_roi_mutual_fund, orientation = 'h' , name = 'Mutual Fund Companies' , marker = dict (color = 'blue' ) )) fig.add_trace(go.Bar( y = expected_roi_growth_companies.index, x = expected_roi_growth_companies, orientation = 'h' , name = 'Growth Rate Companies' , marker = dict (color = 'green' ), opacity = 0.7 )) fig.update_layout( title = 'Expected ROI Comparison: Mutual Fund vs Growth Rate Companies' , xaxis_title = 'Expected ROI (%)' , yaxis_title = 'Companies' , barmode = 'overlay' , legend = dict (title = 'Company Type' ), template = 'plotly_white' ) fig.show() |
The comparison between risk (volatility) and expected ROI for mutual fund companies (displayed in blue) and growth rate companies (shown in green) illustrates a clear risk-reward trade-off. Mutual fund companies exhibit lower volatility, meaning they carry less risk, but they also tend to provide lower expected returns. In contrast, growth rate companies display higher volatility, indicating they are more risky, yet they promise potentially higher returns. Notably, some growth rate companies, like NVIDIA (NVDA) and General Motors (GM), stand out with significant expected returns despite their higher volatility levels. This dynamic underscores the classic investment dilemma: a lower risk generally results in lower returns, while higher risks are associated with the chance of achieving greater rewards.
For long-term investors, the goal is usually to find a balance between stable returns and manageable risk. The mutual fund companies in this analysis show lower volatility, making them less risky options. Their moderate returns make them appealing choices for investors looking for stable, long-term growth without substantial fluctuations in value, which suits conservative investors focused on steady gains.
Calculating Expected Returns for Systematic Investments
Calculating Expected Returns for Systematic Investments
Let’s calculate the expected returns an investor would accumulate by investing $60 each month through our mutual fund plan. Here’s how we can estimate the accumulated returns over different investment horizons:
This approach provides potential investors with a realistic perspective on the cumulative growth they can expect by choosing a lower-risk, steady-return investment strategy over the long term.
monthly_investment = 500 # Monthly investment in INR years = [ 1 , 3 , 5 , 10 ] # Investment periods (in years) n = 12 # Number of times interest is compounded per year (monthly) avg_roi = expected_roi_mutual_fund.mean() / 100 # Convert to decimal def future_value(P, r, n, t): return P * ((( 1 + r / n) * * (n * t) - 1 ) / (r / n)) * ( 1 + r / n) future_values = [future_value(monthly_investment, avg_roi, n, t) for t in years] fig = go.Figure() fig.add_trace(go.Scatter( x = [ str (year) + " year" for year in years], y = future_values, mode = 'lines+markers' , line = dict (color = 'blue' ), marker = dict (size = 8 ), name = 'Future Value' )) fig.update_layout( title = "Expected Value of Investments of 100$ Per Month (Mutual Funds)" , xaxis_title = "Investment Period" , yaxis_title = "Future Value (INR)" , xaxis = dict (showgrid = True , gridcolor = 'lightgrey' ), yaxis = dict (showgrid = True , gridcolor = 'lightgrey' ), template = "plotly_white" , hovermode = 'x' ) fig.show() |
So, this is how a mutual fund plan is designed by investment companies for long-term investors. Mutual funds are investment plans that pool money from multiple investors to purchase a diversified portfolio of stocks, bonds, and other securities, managed by professional fund managers.
I hope you liked My Mutual Fund Plan Project with Python.
Notifications