codelion's picture
Update app.py
fb027d3 verified
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()