codelion's picture
Update app.py
fb027d3 verified
raw
history blame
6.88 kB
import gradio as gr
import yfinance as yf
import requests
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
import time
# Polymarket GraphQL API endpoint
POLYMARKET_API = "https://api.polymarket.com/graphql"
# Function to fetch Polymarket data
def fetch_polymarket_data(search_term="S&P"):
try:
query = """
query {
markets(first: 5, searchTerm: "%s") {
edges {
node {
id
question
outcomes {
name
price
}
}
}
}
}
""" % search_term
response = requests.post(POLYMARKET_API, json={"query": query}, timeout=10)
if response.status_code != 200:
return None
data = response.json()
markets = data["data"]["markets"]["edges"]
if not markets:
return None
# Parse the first relevant market
for market in markets:
node = market["node"]
outcomes = node["outcomes"]
if len(outcomes) >= 2:
return {
"question": node["question"],
"outcomes": {outcome["name"]: float(outcome["price"]) for outcome in outcomes}
}
return None
except Exception as e:
return None
# Function to fetch Yahoo Finance data with retry
def fetch_yahoo_data(ticker, retries=3, delay=2):
for attempt in range(retries):
try:
stock = yf.download(ticker, period="1y", auto_adjust=False, progress=False)
if stock.empty or len(stock) < 2:
return None, None, None, f"No data returned for ticker '{ticker}'. It may be invalid or lack sufficient history."
daily_returns = stock["Close"].pct_change().dropna()
if daily_returns.empty:
return None, None, None, f"No valid returns calculated for ticker '{ticker}'. Insufficient price data."
mu = daily_returns.mean() * 252 # Annualized drift
sigma = daily_returns.std() * np.sqrt(252) # Annualized volatility
last_price = stock["Close"][-1] # Use most recent unadjusted Close
return mu, sigma, last_price, None
except Exception as e:
error_msg = f"Attempt {attempt + 1}/{retries} failed for ticker '{ticker}': {str(e)}"
if attempt < retries - 1:
time.sleep(delay) # Wait before retrying
else:
return None, None, None, error_msg
return None, None, None, f"Failed to fetch data for '{ticker}' after {retries} attempts."
# Monte Carlo Simulation with GBM
def monte_carlo_simulation(S0, mu, sigma, T, N, sims, risk_factor, pm_data):
dt = 1 / 252 # Daily time step
steps = int(T * 252)
sim_paths = np.zeros((sims, steps + 1))
sim_paths[:, 0] = S0
# Adjust drift based on Polymarket probabilities
if pm_data and "outcomes" in pm_data:
outcomes = pm_data["outcomes"]
bullish_prob = outcomes.get("Yes", 0.5) if "Yes" in outcomes else 0.5
bearish_prob = 1 - bullish_prob
mu_bull = mu * 1.2 * risk_factor
mu_bear = mu * -0.5 * risk_factor
mu_adjusted = bullish_prob * mu_bull + bearish_prob * mu_bear
else:
mu_adjusted = mu * risk_factor
for t in range(1, steps + 1):
Z = np.random.standard_normal(sims)
sim_paths[:, t] = sim_paths[:, t-1] * np.exp(
(mu_adjusted - 0.5 * sigma**2) * dt + sigma * np.sqrt(dt) * Z
)
return sim_paths
# Main simulation function
def run_simulation(investment, ticker, horizon, num_sims, risk_factor):
# Validate inputs
if not ticker or investment <= 0 or horizon <= 0 or num_sims <= 0:
return None, "Please provide valid inputs: positive investment, horizon, and simulations, and a ticker."
# Fetch data
mu, sigma, S0, error_msg = fetch_yahoo_data(ticker)
if mu is None:
return None, error_msg
pm_data = fetch_polymarket_data("S&P")
# Run Monte Carlo simulation
sim_paths = monte_carlo_simulation(S0, mu, sigma, horizon, num_sims, num_sims, risk_factor, pm_data)
final_values = sim_paths[:, -1] * (investment / S0) # Scale to investment amount
# Create histogram
fig, ax = plt.subplots(figsize=(8, 6))
ax.hist(final_values, bins=50, color="skyblue", edgecolor="black")
ax.set_title(f"Distribution of Final Investment Value ({ticker})")
ax.set_xlabel("Final Value ($)")
ax.set_ylabel("Frequency")
plt.tight_layout()
# Calculate stats
mean_val = np.mean(final_values)
min_val = np.min(final_values)
max_val = np.max(final_values)
std_val = np.std(final_values)
# Prepare summary text
summary = f"Market Data (Yahoo Finance):\n"
summary += f"- Drift (μ): {mu:.4f} (based on unadjusted Close)\n"
summary += f"- Volatility (σ): {sigma:.4f}\n"
summary += f"- Last Close Price: ${S0:.2f}\n\n"
if pm_data:
summary += f"Polymarket Data:\n- Question: {pm_data['question']}\n"
for outcome, prob in pm_data["outcomes"].items():
summary += f"- {outcome}: {prob*100:.1f}%\n"
else:
summary += "Polymarket Data: No relevant market found or API unavailable.\n"
summary += f"\nSimulation Results ({num_sims} runs):\n"
summary += f"- Mean Final Value: ${mean_val:.2f}\n"
summary += f"- Min Final Value: ${min_val:.2f}\n"
summary += f"- Max Final Value: ${max_val:.2f}\n"
summary += f"- Std Dev: ${std_val:.2f}"
return fig, summary
# Gradio UI
with gr.Blocks(title="Investment Simulation Platform") as demo:
gr.Markdown("# Investment Decision Simulation Platform")
gr.Markdown("Simulate investment outcomes using Yahoo Finance data (unadjusted) and Polymarket probabilities.")
with gr.Row():
with gr.Column():
investment = gr.Number(label="Investment Amount ($)", value=10000)
ticker = gr.Textbox(label="Ticker Symbol (e.g., AAPL, ^GSPC)", value="AAPL")
horizon = gr.Number(label="Investment Horizon (Years)", value=1)
num_sims = gr.Number(label="Number of Simulations", value=1000)
risk_factor = gr.Slider(0.5, 2.0, value=1.0, label="Risk Factor")
submit_btn = gr.Button("Run Simulation")
with gr.Column():
plot_output = gr.Plot(label="Final Value Distribution")
text_output = gr.Textbox(label="Simulation Summary", lines=12)
submit_btn.click(
fn=run_simulation,
inputs=[investment, ticker, horizon, num_sims, risk_factor],
outputs=[plot_output, text_output]
)
if __name__ == "__main__":
demo.launch()