Spaces:
Sleeping
Sleeping
File size: 4,156 Bytes
b651d9b 538f8e7 86ea38c b651d9b 538f8e7 b651d9b 538f8e7 b651d9b 538f8e7 b651d9b 538f8e7 b651d9b 538f8e7 b651d9b 538f8e7 b651d9b 538f8e7 b651d9b b8afc46 538f8e7 b651d9b 538f8e7 b651d9b 538f8e7 b651d9b 538f8e7 b651d9b 538f8e7 b651d9b 538f8e7 b651d9b 538f8e7 b651d9b b8afc46 538f8e7 b651d9b 538f8e7 b651d9b 86ea38c b8afc46 86ea38c b651d9b 538f8e7 b8afc46 e127f01 b651d9b 86ea38c b651d9b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
import yfinance as yf
import pandas as pd
import gradio as gr
import plotly.graph_objs as go
def fetch_and_rebalance(etfs_str, period, rebalance_threshold_percentage):
etfs = [etf.strip() for etf in etfs_str.split(",")]
# Fetch historical data for the given period
etf_data = {etf: yf.Ticker(etf).history(period=period) for etf in etfs}
# Ensure the index is a DatetimeIndex
for etf in etfs:
etf_data[etf].index = pd.to_datetime(etf_data[etf].index)
# Fetch dividend data and ensure the index is a DatetimeIndex
etf_dividends = {etf: yf.Ticker(etf).dividends for etf in etfs}
# Ensure both data series have the same dates
common_dates = etf_data[etfs[0]].index
for etf in etfs[1:]:
common_dates = common_dates.intersection(etf_data[etf].index)
for etf in etfs:
etf_data[etf] = etf_data[etf].reindex(common_dates)
etf_dividends[etf] = etf_dividends[etf].reindex(common_dates, fill_value=0)
# Initial investment
initial_investment = 10000
investments = {etf: initial_investment / len(etfs) for etf in etfs}
shares = {etf: investments[etf] / etf_data[etf]['Close'].iloc[0] for etf in etfs}
# DataFrame to track the investments
df_rebalance = pd.DataFrame(columns=["Date"] + [f"{etf} Value" for etf in etfs] + ["Total Value"])
rebalance_dates = []
# Perform the rebalancing based on the percentage difference condition
for date in common_dates[1:]: # Skip the first date for the initial investment
total_value = 0
for etf in etfs:
# Update the values based on daily returns
previous_date = etf_data[etf].index[etf_data[etf].index.get_loc(date) - 1]
investments[etf] *= (etf_data[etf]['Close'].loc[date] / etf_data[etf]['Close'].loc[previous_date])
# Add dividends
investments[etf] += etf_dividends[etf].loc[date] * shares[etf]
total_value += investments[etf]
# Check if rebalancing is needed
value_difference = abs(investments[etfs[0]] - investments[etfs[1]])
percentage_difference = value_difference / total_value
rebalance_needed = percentage_difference > rebalance_threshold_percentage
if rebalance_needed:
# Rebalance to equal weight
investments = {etf: total_value / len(etfs) for etf in etfs}
shares = {etf: investments[etf] / etf_data[etf]['Close'].loc[date] for etf in etfs}
rebalance_dates.append(date)
# Append to DataFrame using pd.concat
df_rebalance = pd.concat([
df_rebalance,
pd.DataFrame({
"Date": [date],
**{f"{etf} Value": [investments[etf]] for etf in etfs},
"Total Value": [total_value]
})
], ignore_index=True)
# Check if df_rebalance is empty
if df_rebalance.empty:
return "No data was appended."
final_value = df_rebalance["Total Value"].iloc[-1]
total_return = (final_value - initial_investment) / initial_investment * 100
# Create the plot
fig = go.Figure()
for etf in etfs:
fig.add_trace(go.Scatter(x=df_rebalance["Date"], y=df_rebalance[f"{etf} Value"], mode='lines', name=etf))
# Add rebalancing markers
for date in rebalance_dates:
fig.add_trace(go.Scatter(x=[date], y=[df_rebalance.loc[df_rebalance['Date'] == date, f"{etf} Value"].values[0]],
mode='markers', name='Rebalance', marker=dict(size=10, color='red')))
fig.update_layout(title='ETF Valuations Over Time', xaxis_title='Date', yaxis_title='Value')
return f"Final Value: {final_value}, Total Return: {total_return}%", fig
interface = gr.Interface(
fn=fetch_and_rebalance,
inputs=[
gr.Textbox(lines=1, placeholder="ETFs (comma separated)", value="TQQQ,JEPI"),
gr.Textbox(lines=1, placeholder="Period", value="ytd"),
gr.Slider(minimum=0.0, maximum=1.0, value=0.05, label="Rebalance Threshold Percentage")
],
outputs=["text", "plot"]
)
interface.launch()
|