File size: 4,319 Bytes
3b0aed5
 
 
 
 
 
 
 
df11b5e
3b0aed5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30cd673
3b0aed5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c36d988
 
 
 
 
3b0aed5
 
3519170
3b0aed5
 
 
3519170
3b0aed5
 
 
 
 
 
 
 
 
 
 
e602312
3b0aed5
 
3519170
3b0aed5
 
 
 
 
 
 
c36d988
3519170
 
3b0aed5
 
3519170
3b0aed5
3519170
 
3b0aed5
 
 
c36d988
3b0aed5
c36d988
 
3b0aed5
c36d988
3b0aed5
 
 
 
 
 
 
 
 
 
 
 
 
 
c36d988
 
 
3b0aed5
 
 
 
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import gradio as gr
import pandas as pd
import numpy as np
import yfinance as yf
from stable_baselines3 import PPO
from gym import Env
from gym.spaces import Box
import matplotlib.pyplot as plt
import io

class PortfolioEnv(Env):
    def __init__(self, returns, initial_balance=10000):
        super(PortfolioEnv, self).__init__()
        self.returns = returns
        self.n_assets = returns.shape[1]
        self.initial_balance = initial_balance
        self.current_balance = initial_balance

        self.action_space = Box(low=0, high=1, shape=(self.n_assets,), dtype=np.float32)
        self.observation_space = Box(low=-np.inf, high=np.inf, shape=(self.n_assets,), dtype=np.float32)

        self.weights = np.ones(self.n_assets) / self.n_assets
        self.state = self.returns.iloc[0].values
        self.current_step = 0

    def step(self, action):
        action = np.clip(action, 0, 1)
        weights = action / (np.sum(action) + 1e-8)  # Prevent division by zero

        portfolio_return = np.dot(weights, self.returns.iloc[self.current_step].values)
        self.current_balance *= (1 + portfolio_return)

        self.current_step += 1
        done = self.current_step >= len(self.returns) - 1

        if not done:
            self.state = self.returns.iloc[self.current_step].values

        reward = self.current_balance
        return self.state, reward, done, {}

    def reset(self):
        self.current_balance = self.initial_balance
        self.current_step = 0
        self.state = self.returns.iloc[0].values
        return self.state

def fetch_data(tickers, start_date, end_date):
    try:
        data = yf.download(tickers, start=start_date, end=end_date)['Adj Close']
        return data
    except Exception as e:
        raise ValueError(f"Failed to fetch data: {e}")

def optimize_portfolio(tickers, start_date, end_date, initial_balance):
    # Fetch real-time data
    data = fetch_data(tickers, start_date, end_date)
    returns = data.pct_change().dropna()

    # Define the environment
    env = PortfolioEnv(returns, initial_balance=initial_balance)
    model = PPO("MlpPolicy", env, verbose=0)
    model.learn(total_timesteps=5000)

    state = env.reset()
    done = False
    portfolio_weights = []
    portfolio_values = [initial_balance]
    while not done:
        action, _ = model.predict(state)
        state, reward, done, _ = env.step(action)
        portfolio_weights = action / (np.sum(action) + 1e-8)
        portfolio_values.append(reward)

    # Save chart
    plt.figure(figsize=(10, 6))
    plt.plot(portfolio_values, label="Portfolio Value")
    plt.title("Portfolio Value Over Time")
    plt.xlabel("Time Steps")
    plt.ylabel("Portfolio Value")
    plt.legend()
    plt.grid()

    chart_path = "portfolio_chart.png"
    plt.savefig(chart_path)
    plt.close()

    # Prepare the output
    weights = {f"Asset_{i + 1} ({tickers.split(',')[i].strip()})": float(weight) for i, weight in enumerate(portfolio_weights)}
    return weights, chart_path


def run_optimization(tickers, start_date, end_date, initial_balance):
    if not tickers or not start_date or not end_date:
        return {"error": "Please fill all the fields."}, None
    try:
        weights, chart_buffer = optimize_portfolio(tickers, start_date, end_date, initial_balance)
        return weights, chart_buffer
    except Exception as e:
        return {"error": str(e)}, None

interface = gr.Interface(
    fn=run_optimization,
    inputs=[
        gr.Textbox(label="Enter Stock Tickers (comma-separated)", placeholder="AAPL, MSFT, TSLA"),
        gr.Textbox(label="Start Date (YYYY-MM-DD)", placeholder="2023-01-01"),
        gr.Textbox(label="End Date (YYYY-MM-DD)", placeholder="2023-12-31"),
        gr.Number(label="Initial Investment Amount", value=10000),
    ],
    outputs=[
        gr.JSON(label="Optimized Portfolio Weights"),
        gr.Image(label="Portfolio Value Chart"),
    ],
    title="AI-Powered Portfolio Optimization",
    description="""Enter stock tickers (e.g., AAPL, MSFT, TSLA), a date range, and your initial investment amount.
                   The app fetches historical data, runs AI optimization, and returns the optimized portfolio weights 
                   along with a performance chart."""
)

if __name__ == "__main__":
    interface.launch()