Description I am trying to add a chatbot for the backtesting platform built with Flask and there is a button on the right side that toggles the sidebar. If the user clicks the button, it shows the sidebar with a chatbot. Expected Behavior The user should be able to type texts into the chatbot user input field. Current Behavior Currently unable to type texts into the chatbot user input field. HTML, CSS, JS const promptInput = document.getElementById("userInput"); const chatContainer = document.getElementById("chatContainer"); const typingIndicator = document.getElementById("typingIndicator"); const sidebar = document.getElementById("sidebar"); const sidebarContent = document.getElementById("sidebarContent"); async function sendMessage() { const prompt = promptInput.value.trim(); if (!prompt) { alert("Please enter a message."); return; } addMessage(prompt, 'user'); promptInput.value = ""; showTypingIndicator(); const generatedText = await generateText(prompt); addMessage(generatedText, 'bot'); hideTypingIndicator(); } async function generateText(prompt) { try { const response = await fetch("http://127.0.0.1:5000/generate_text_stream", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ prompt }), }); if (!response.ok) { console.error("Error:", response.statusText); return "Error occurred while generating response."; } const reader = response.body.getReader(); const decoder = new TextDecoder(); let isFinished = false; let generatedTextContent = ""; while (!isFinished) { const { done, value } = await reader.read(); if (done) { isFinished = true; break; } generatedTextContent += decoder.decode(value, {stream: true}); } return generatedTextContent; } catch (error) { console.error("Error:", error); return "An error occurred."; } } function addMessage(text, type) { const messageDiv = document.createElement("div"); messageDiv.className = `message ${type}`; messageDiv.innerHTML = `<div class="message-bubble fadeIn">${text}</div>`; chatContainer.appendChild(messageDiv); chatContainer.scrollTop = chatContainer.scrollHeight; hideTypingIndicator(); } let typingTimeout; function showTypingIndicator() { clearTimeout(typingTimeout); typingIndicator.style.display = "inline-block"; } function hideTypingIndicator() { typingTimeout = setTimeout(() => { typingIndicator.style.display = "none"; }, 1000); } function handleKeyPress(event) { if (event.key === "Enter") { sendMessage(); } } function toggleSidebar() { if (sidebar.style.width === "500px") { sidebar.style.width = "0"; sidebarContent.style.display = "none"; } else { sidebar.style.width = "500px"; sidebarContent.style.display = "block"; } } window.onload = () => addMessage("Hello! How can I assist you today?", 'bot'); body { font-family: Arial, sans-serif; margin: 0; padding: 0; text-align: center; background-color: #f4f4f4; } .container { margin-top: 0; width: 90%; max-width: 450px; margin: 10px auto 0; background-color: #fff; border-radius: 12px; box-shadow: 0 6px 18px rgba(0, 0, 0, 0.1); padding: 20px; transition: all 0.3s; } .chat { overflow-y: auto; height: 400px; margin-bottom: 20px; border-bottom: 2px solid #e2e2e2; } .message { display: flex; margin-bottom: 12px; } .message.user { justify-content: flex-end; } .message-bubble { padding: 12px 18px; max-width: 70%; border-radius: 20px; line-height: 1.6; font-size: 0.95rem; } .message.user .message-bubble { background-color: #3182ce; color: white; } .message.bot .message-bubble { background-color: #e2e2e2; color: #333; } input[type="text"] { width: calc(100% - 110px); padding: 12px 18px; border: 2px solid #e2e2e2; border-radius: 8px 0 0 8px; font-size: 1rem; outline: none; z-index: 2; /* Ensure it's above other elements */ } .send-button { width: 110px; background-color: #3182ce; color: white; padding: 12px 18px; border: none; border-radius: 0 8px 8px 0; cursor: pointer; transition: background-color 0.3s; } .send-button:hover { background-color: #2c5282; } .footer { text-align: center; padding: 15px 0; font-size: 0.9rem; color: #666; position: static; border-top: 1px solid #e2e2e2; background-color: #fff; position: fixed; bottom: 0; width: 100%; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .fadeIn { animation: fadeIn 1s; } @media (max-width: 600px) { .container { width: 95%; margin: 10px auto 0; } .chat { height: 300px; } input[type="text"], .send-button { padding: 10px 14px; font-size: 0.9rem; } .footer { font-size: 0.8rem; margin-top: 30px; } } .typing-indicator { display: none; align-items: center; justify-content: flex-end; margin-top: 8px; width: 10px; height: 10px; background-color: #333; border-radius: 50%; margin-left: 4px; animation: typing 1s infinite; } @keyframes typing { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.2); opacity: 0.7; } } /* Sidebar Styles */ .sidebar { position: fixed; right: 0; top: 0; height: 100%; width: 100%; max-width: 500px; background-color: #f4f4f4; overflow-x: hidden; transition: 0.5s; padding-top: 60px; color: white; } .sidebar-content { display: none; } .sidebar-content h2 { text-align: center; } .sidebar-content p { padding: 10px; } .toggle-button { position: fixed; right: 0; top: 0; padding: 15px; background-color: #3182ce; color: white; border: none; cursor: pointer; } .toggle-button:hover { background-color: #2c5282; } h1 { color: rgb(0, 0, 0); padding: 20px 0; margin: 0 0 20px 0; } form { display: inline-block; background-color: #fff; padding: 20px; border-radius: 10px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); } label { display: block; margin-bottom: 10px; font-weight: bold; } select { width: 100%; padding: 10px; margin-bottom: 20px; border-radius: 5px; border: 1px solid #ccc; } button { padding: 10px 20px; border: none; border-radius: 5px; background-color: #4CAF50; color: white; font-size: 16px; cursor: pointer; } button:hover { background-color: #45a049; } img { max-width: 90%; margin: 20px 0; border: 1px solid #ddd; border-radius: 10px; } <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Backtesting Engine</title> <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet" /> <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" /> </head> <body> <h1 class="text-6xl font-bold mb-4 text-center">Backtesting Engine</h1> <div class="sidebar" id="sidebar"> <button class="toggle-button" onclick="toggleSidebar()">☰</button> <div class="sidebar-content" id="sidebarContent"> <div class="container bg-white rounded-lg shadow-md"> <h1 class="text-3xl font-bold mb-4 text-center">ChatBot</h1> <div class="chat" id="chatContainer"></div> <div class="flex"> <input type="text" id="userInput" placeholder="Type your message here..." class="outline-none" onkeyup="handleKeyPress(event)" /> <button class="send-button" onclick="sendMessage()">Send</button> </div> <div class="typing-indicator" id="typingIndicator"></div> </div> </div> </div> <form method="post"> <label for="cs_model">Cross-Sectional Model:</label> <select name="cs_model" id="cs_model"> <option value="EW">EW</option> <option value="MSR">MSR</option> <option value="GMV">GMV</option> <option value="MDP">MDP</option> <option value="EMV">EMV</option> <option value="RP">RP</option></select ><br /><br /> <label for="ts_model">Time-Series Model:</label> <select name="ts_model" id="ts_model"> <option value="VT">VT</option> <option value="CVT">CVT</option> <option value="KL">KL</option> <option value="CPPI">CPPI</option> <option value="">None</option></select ><br /><br /> <button type="submit">Run Backtest</button> </form> {% if plot_url1 and plot_url2 and plot_url3 %} <h2>Backtesting Results</h2> <img src="data:image/png;base64,{{ plot_url1 }}" alt="Portfolio Weights" /> <img src="data:image/png;base64,{{ plot_url2 }}" alt="Underlying Asset Performance" /> <img src="data:image/png;base64,{{ plot_url3 }}" alt="Portfolio Performance" /> {% endif %} <script src="{{ url_for('static', filename='js/script.js') }}"></script> </body> </html> app.py from flask import Flask, render_template, request import numpy as np import pandas as pd import matplotlib.pyplot as plt import yfinance as yf import quantstats as qs from scipy.optimize import minimize from scipy.stats import norm from io import BytesIO import base64 app = Flask(__name__) # Function to get ETF price data def get_etf_price_data(): tickers = ['AAPL', 'MSFT', 'NVDA', 'GOOG', 'XLK'] etf = yf.Tickers(tickers) data = etf.history(start='2010-01-01', actions=False) data.drop(['Open', 'High', 'Low', 'Volume'], inplace=True, axis=1) data = data.droplevel(0, axis=1) data.ffill(inplace=True) df = data.resample('W').last() return df df = get_etf_price_data() # Portfolio Backtesting Engine Class class GEMTU772: # Initialization Function def __init__(self, price, param=52): # Annualization Parameter self.param = param # Intraday Return Rate self.rets = price.pct_change().dropna() # Expected Rate of Return self.er = np.array(self.rets * self.param) # Volatility self.vol = np.array(self.rets.rolling(self.param).std() * np.sqrt(self.param)) # Covariance Matrix cov = self.rets.rolling(self.param).cov().dropna() * self.param # Transaction Cost per Unit self.cov = cov.values.reshape(int(cov.shape[0]/cov.shape[1]), cov.shape[1], cov.shape[1]) self.cost = 0.0005 # Cross-Sectional Risk Models Class class CrossSectional: #EW def ew(self, er): noa = er.shape[0] weights = np.ones_like(er) * (1/noa) return weights def msr(self, er, cov): noa = er.shape[0] init_guess = np.repeat(1/noa, noa) bounds = ((0.0, 1.0), ) * noa weights_sum_to_1 = {'type': 'eq', 'fun': lambda weights: np.sum(weights) - 1} def neg_sharpe(weights, er, cov): r = weights.T @ er # @ means multiplication vol = np.sqrt(weights.T @ cov @ weights) return - r / vol weights = minimize(neg_sharpe, init_guess, args=(er, cov), method='SLSQP', constraints=(weights_sum_to_1,), bounds=bounds) return weights.x #GMV def gmv(self, cov): noa = cov.shape[0] init_guess = np.repeat(1/noa, noa) bounds = ((0.0, 1.0), ) * noa weights_sum_to_1 = {'type': 'eq', 'fun': lambda weights: np.sum(weights) - 1} def port_vol(weights, cov): vol = np.sqrt(weights.T @ cov @ weights) return vol weights = minimize(port_vol, init_guess, args=(cov,), method='SLSQP', constraints=(weights_sum_to_1,), bounds=bounds) return weights.x #MDP def mdp(self, vol, cov): noa = vol.shape[0] init_guess = np.repeat(1/noa, noa) bounds = ((0.0, 1.0), ) * noa weights_sum_to_1 = {'type': 'eq', 'fun': lambda weights: np.sum(weights) - 1} def neg_div_ratio(weights, vol, cov): weighted_vol = weights.T @ vol port_vol = np.sqrt(weights.T @ cov @ weights) return - weighted_vol / port_vol weights = minimize(neg_div_ratio, init_guess, args=(vol, cov), method='SLSQP', constraints=(weights_sum_to_1,), bounds=bounds) return weights.x #RP def rp(self, cov): noa = cov.shape[0] init_guess = np.repeat(1/noa, noa) bounds = ((0.0, 1.0), ) * noa target_risk = np.repeat(1/noa, noa) weights_sum_to_1 = {'type': 'eq', 'fun': lambda weights: np.sum(weights) - 1} def msd_risk(weights, target_risk, cov): port_var = weights.T @ cov @ weights marginal_contribs = cov @ weights risk_contribs = np.multiply(marginal_contribs, weights.T) / port_var w_contribs = risk_contribs return ((w_contribs - target_risk)**2).sum() weights = minimize(msd_risk, init_guess, args=(target_risk, cov), method='SLSQP', constraints=(weights_sum_to_1,), bounds=bounds) return weights.x #EMV def emv(self, vol): inv_vol = 1 / vol weights = inv_vol / inv_vol.sum() return weights # Time-Series Risk Models Class class TimeSeries: #VT def vt(self, port_rets, param, vol_target=0.1): vol = port_rets.rolling(param).std().fillna(0) * np.sqrt(param) weights = (vol_target / vol).replace([np.inf, -np.inf], 0).shift(1).fillna(0) weights[weights > 1] = 1 return weights #CVT def cvt(self, port_rets, param, delta=0.01, cvar_target=0.05): def calculate_CVaR(rets, delta=0.01): VaR = rets.quantile(delta) return rets[rets <= VaR].mean() rolling_CVaR = -port_rets.rolling(param).apply(calculate_CVaR, args=(delta,)).fillna(0) weights = (cvar_target / rolling_CVaR).replace([np.inf, -np.inf], 0).shift(1).fillna(0) weights[weights > 1] = 1 return weights #KL def kl(self, port_rets, param): sharpe_ratio = (port_rets.rolling(param).mean() * np.sqrt(param) / port_rets.rolling(param).std()) weights = pd.Series(2 * norm.cdf(sharpe_ratio) - 1, index=port_rets.index).fillna(0) weights[weights < 0] = 0 weights = weights.shift(1).fillna(0) return weights #CPPI def cppi(self, port_rets, m=3, floor=0.7, init_val=1): n_steps = len(port_rets) port_value = init_val floor_value = init_val * floor peak = init_val port_history = pd.Series(dtype=np.float64).reindex_like(port_rets) weight_history = pd.Series(dtype=np.float64).reindex_like(port_rets) floor_history = pd.Series(dtype=np.float64).reindex_like(port_rets) for step in range(n_steps): peak = np.maximum(peak, port_value) floor_value = peak * floor cushion = (port_value - floor_value) / port_value weight = m * cushion risky_alloc = port_value * weight safe_alloc = port_value * (1 - weight) port_value = risky_alloc * (1 + port_rets.iloc[step]) + safe_alloc port_history.iloc[step] = port_value weight_history.iloc[step] = weight floor_history.iloc[step] = floor_value return weight_history.shift(1).fillna(0) # Transaction Cost Function (Compound rate of return method assuming reinvestment) def transaction_cost(self, weights_df, rets_df, cost=0.0005): prev_weights_df = (weights_df.shift(1).fillna(0) * (1 + rets_df.iloc[self.param-1:,:])) \ .div((weights_df.shift(1).fillna(0) * (1 + rets_df.iloc[self.param-1:,:])).sum(axis=1), axis=0) # Investment Weight of Previous Period (The backslash ('\') in Python is used as a line continuation character.) cost_df = abs(weights_df - prev_weights_df) * cost cost_df.fillna(0, inplace=True) return cost_df # Backtesting Execution Function def run(self, cs_model, ts_model, cost): # Empty Dictionary backtest_dict = {} # Intraday Return Rate DataFrame rets = self.rets # Select and Run Cross-Sectional Risk Models for i, index in enumerate(rets.index[self.param-1:]): if cs_model == 'EW': backtest_dict[index] = self.CrossSectional().ew(self.er) elif cs_model == 'MSR': backtest_dict[index] = self.CrossSectional().msr(self.er, self.cov) elif cs_model == 'GMV': backtest_dict[index] = self.CrossSectional().gmv(self.cov) elif cs_model == 'MDP': backtest_dict[index] = self.CrossSectional().mdp(self.vol, self.cov) elif cs_model == 'EMV': backtest_dict[index] = self.CrossSectional().emv(self.vol) elif cs_model == 'RP': backtest_dict[index] = self.CrossSectional().rp(self.cov) # Cross-Sectional Weights DataFrame cs_weights = pd.DataFrame(list(backtest_dict.values()), index=backtest_dict.keys(), columns=rets.columns) cs_weights.fillna(0, inplace=True) # Cross-Sectional Risk Models Return on Assets cs_rets = cs_weights.shift(1) * rets.iloc[self.param-1:,:] # Cross-Sectional Risk Models Portfolio Return cs_port_rets = cs_rets.sum(axis=1) # Select and Run Time-Series Risk Models if ts_model == 'VT': ts_weights = self.TimeSeries().vt(cs_port_rets, self.param) elif ts_model == 'CVT': ts_weights = self.TimeSeries().cvt(cs_port_rets, self.param) elif ts_model == 'KL': ts_weights = self.TimeSeries().kl(cs_port_rets, self.param) elif ts_model == 'CPPI': ts_weights = self.TimeSeries().cppi(cs_port_rets) elif ts_model == None: ts_weights = 1 # Final Portfolio Investment Weights port_weights = cs_weights.multiply(ts_weights, axis=0) # Transaction Cost DataFrame cost = self.transaction_cost(port_weights, rets) # Final Portfolio Return by Assets port_asset_rets = port_weights.shift() * rets - cost # Final Portfolio Return port_rets = port_asset_rets.sum(axis=1) port_rets.index = pd.to_datetime(port_rets.index).strftime("%Y-%m-%d") return port_weights, port_asset_rets, port_rets def performance_analytics(self, port_weights, port_asset_rets, port_rets): img = BytesIO() plt.figure(figsize=(12, 7)) port_weights['Cash'] = 1 - port_weights.sum(axis=1) plt.stackplot(port_weights.index, port_weights.T, labels=port_weights.columns) plt.title('Portfolio Weights') plt.xlabel('Date') plt.ylabel('Weights') plt.legend(loc='upper left') plt.savefig(img, format='png') plt.close() img.seek(0) plot_url1 = base64.b64encode(img.getvalue()).decode('utf8') img = BytesIO() plt.figure(figsize=(12, 7)) plt.plot((1 + port_asset_rets).cumprod() - 1) plt.title('Underlying Asset Performance') plt.xlabel('Date') plt.ylabel('Returns') plt.legend(port_asset_rets.columns, loc='upper left') plt.savefig(img, format='png') plt.close() img.seek(0) plot_url2 = base64.b64encode(img.getvalue()).decode('utf8') img = BytesIO() plt.figure(figsize=(12, 7)) plt.plot((1 + port_rets).cumprod() - 1) plt.title('Portfolio Performance') plt.xlabel('Date') plt.ylabel('Returns') plt.savefig(img, format='png') plt.close() img.seek(0) plot_url3 = base64.b64encode(img.getvalue()).decode('utf8') return plot_url1, plot_url2, plot_url3 #Route function @app.route('/', methods=['GET', 'POST']) def index(): if request.method == 'POST': cs_model = request.form.get('cs_model') # cs model selection ts_model = request.form.get('ts_model') # ts model selection engine = GEMTU772(df) # Run backtesting res = engine.run(cs_model=cs_model, ts_model=ts_model, cost=0.0005) # Run method port_weights = res[0] port_asset_rets = res[1] port_rets = res[2] plot_url1, plot_url2, plot_url3 = engine.performance_analytics(port_weights, port_asset_rets, port_rets) return render_template('index.html', plot_url1=plot_url1, plot_url2=plot_url2, plot_url3=plot_url3) # Rendering return render_template('index.html') if __name__ == '__main__': app.run(debug=True) Continue reading...