diff --git a/week4/community-contributions/w4d5-Trade.ipynb b/week4/community-contributions/w4d5-Trade.ipynb
new file mode 100644
index 0000000..3a57afa
--- /dev/null
+++ b/week4/community-contributions/w4d5-Trade.ipynb
@@ -0,0 +1,1833 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Trading Code Generator\n",
+ "\n",
+ "This notebook creates a code generator that produces trading code to buy and sell equities in a simulated environment based on free APIs. It uses Gradio for the UI, similar to the approach in day5.ipynb.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "import io\n",
+ "import sys\n",
+ "import time\n",
+ "import random\n",
+ "import numpy as np\n",
+ "from dotenv import load_dotenv\n",
+ "from openai import OpenAI\n",
+ "import gradio as gr\n",
+ "from IPython.display import display\n",
+ "from huggingface_hub import InferenceClient\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "OpenAI API Key exists and begins sk-proj-\n",
+ "Hugging Face Token exists and begins hf_fNncb\n"
+ ]
+ }
+ ],
+ "source": [
+ "load_dotenv(override=True)\n",
+ "openai_api_key = os.getenv('OPENAI_API_KEY')\n",
+ "hf_token = os.getenv('HF_TOKEN')\n",
+ "\n",
+ "if openai_api_key:\n",
+ " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
+ "else:\n",
+ " print(\"OpenAI API Key not set\")\n",
+ " \n",
+ "if hf_token:\n",
+ " print(f\"Hugging Face Token exists and begins {hf_token[:8]}\")\n",
+ "else:\n",
+ " print(\"Hugging Face Token not set\")\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "openai_client = OpenAI()\n",
+ "hf_client = InferenceClient(token=hf_token)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 46,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "models = [\"gpt-4o\", \"gpt-3.5-turbo\", \"meta-llama/Llama-2-70b-chat-hf\"]\n",
+ "\n",
+ "def generate_with_openai(model, messages):\n",
+ " response = openai_client.chat.completions.create(\n",
+ " model=model, \n",
+ " messages=messages\n",
+ " )\n",
+ " return response.choices[0].message.content\n",
+ "\n",
+ "def generate_with_hf(model, messages):\n",
+ " prompt = \"\"\n",
+ " for msg in messages:\n",
+ " role = msg[\"role\"]\n",
+ " content = msg[\"content\"]\n",
+ " if role == \"system\":\n",
+ " prompt += f\"[INST] {content} [/INST]\\n\"\n",
+ " elif role == \"user\":\n",
+ " prompt += f\"[INST] {content} [/INST]\\n\"\n",
+ " else:\n",
+ " prompt += f\"{content}\\n\"\n",
+ " \n",
+ " response = hf_client.text_generation(\n",
+ " prompt,\n",
+ " model=model,\n",
+ " max_new_tokens=1024,\n",
+ " temperature=0.7,\n",
+ " repetition_penalty=1.2\n",
+ " )\n",
+ " return response\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 47,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "CSS = \"\"\"\n",
+ ":root {\n",
+ " --py-color: #209dd7;\n",
+ " --trading-color: #27ae60;\n",
+ " --accent: #753991;\n",
+ " --card: #161a22;\n",
+ " --text: #e9eef5;\n",
+ "}\n",
+ "\n",
+ "/* Full-width layout */\n",
+ ".gradio-container {\n",
+ " max-width: 100% !important;\n",
+ " padding: 0 40px !important;\n",
+ "}\n",
+ "\n",
+ "/* Code card styling */\n",
+ ".card {\n",
+ " background: var(--card);\n",
+ " border: 1px solid rgba(255,255,255,.08);\n",
+ " border-radius: 14px;\n",
+ " padding: 10px;\n",
+ "}\n",
+ "\n",
+ "/* Make code block scrollable but fixed height */\n",
+ "#code-block {\n",
+ " max-height: 400px !important;\n",
+ " overflow-y: auto !important;\n",
+ "}\n",
+ "\n",
+ "#code-block .cm-editor {\n",
+ " height: 400px !important;\n",
+ "}\n",
+ "\n",
+ "/* Buttons */\n",
+ ".generate-btn button {\n",
+ " background: var(--accent) !important;\n",
+ " border-color: rgba(255,255,255,.12) !important;\n",
+ " color: white !important;\n",
+ " font-weight: 700;\n",
+ "}\n",
+ ".run-btn button {\n",
+ " background: #202631 !important;\n",
+ " color: var(--text) !important;\n",
+ " border-color: rgba(255,255,255,.12) !important;\n",
+ "}\n",
+ ".run-btn.py button:hover { box-shadow: 0 0 0 2px var(--py-color) inset; }\n",
+ ".run-btn.trading button:hover { box-shadow: 0 0 0 2px var(--trading-color) inset; }\n",
+ ".generate-btn button:hover { box-shadow: 0 0 0 2px var(--accent) inset; }\n",
+ "\n",
+ "/* Outputs with color tint */\n",
+ ".py-out textarea {\n",
+ " background: linear-gradient(180deg, rgba(32,157,215,.18), rgba(32,157,215,.10));\n",
+ " border: 1px solid rgba(32,157,215,.35) !important;\n",
+ " color: rgba(32,157,215,1) !important;\n",
+ " font-weight: 600;\n",
+ "}\n",
+ ".trading-out textarea {\n",
+ " background: linear-gradient(180deg, rgba(39,174,96,.18), rgba(39,174,96,.10));\n",
+ " border: 1px solid rgba(39,174,96,.35) !important;\n",
+ " color: rgba(39,174,96,1) !important;\n",
+ " font-weight: 600;\n",
+ "}\n",
+ "\n",
+ "/* Align controls neatly */\n",
+ ".controls .wrap {\n",
+ " gap: 10px;\n",
+ " justify-content: center;\n",
+ " align-items: center;\n",
+ "}\n",
+ "\"\"\"\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "system_prompt = \"\"\"\n",
+ "You are an expert algorithmic trading code generator. Generate clean, bug-free Python code for trading strategies.\n",
+ "\n",
+ "Generate code that:\n",
+ "1. Uses synthetic data generation only - no API calls\n",
+ "2. Implements the specified trading strategy\n",
+ "3. Uses proper error handling\n",
+ "4. Visualizes strategy performance with buy/sell signals\n",
+ "5. Calculates performance metrics\n",
+ "6. Handles edge cases properly\n",
+ "\n",
+ "REQUIREMENTS:\n",
+ "1. Include if __name__ == \"__main__\": block that executes immediately\n",
+ "2. Define all variables before use\n",
+ "3. Pass parameters between functions, avoid global variables\n",
+ "4. NO explanatory text outside of code\n",
+ "5. NO markdown blocks or language indicators\n",
+ "6. Code must execute without user input\n",
+ "7. Use str() for pandas objects in f-strings\n",
+ "8. Use .copy() for DataFrame views that will be modified\n",
+ "9. Include min_periods in rolling calculations\n",
+ "10. Check array lengths before scatter plots\n",
+ "11. Configure logging properly\n",
+ "12. Include helper functions for formatting and plotting\n",
+ "\n",
+ "Respond ONLY with Python code. No explanations or markdown.\n",
+ "\"\"\"\n",
+ "\n",
+ "def user_prompt_for(description):\n",
+ " return f\"\"\"\n",
+ "Generate Python code for a trading strategy:\n",
+ "\n",
+ "{description}\n",
+ "\n",
+ "Requirements:\n",
+ "1. Use synthetic data generation only\n",
+ "2. Implement the strategy exactly as described\n",
+ "3. Include backtesting functionality\n",
+ "4. Visualize results with matplotlib\n",
+ "5. Calculate performance metrics\n",
+ "6. Handle all edge cases\n",
+ "7. No comments needed\n",
+ "\n",
+ "Make the code complete and runnable as-is with all necessary imports.\n",
+ "\"\"\"\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 87,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def messages_for(description):\n",
+ " return [\n",
+ " {\"role\": \"system\", \"content\": system_prompt},\n",
+ " {\"role\": \"user\", \"content\": user_prompt_for(description)}\n",
+ " ]\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def validate_code(code):\n",
+ " issues = []\n",
+ " if \"import yfinance\" not in code and \"from yfinance\" not in code:\n",
+ " issues.append(\"Missing yfinance import\")\n",
+ " if \"import matplotlib\" not in code and \"from matplotlib\" not in code:\n",
+ " issues.append(\"Missing matplotlib import\")\n",
+ " if \"__name__ == \\\"__main__\\\"\" not in code and \"__name__ == '__main__'\" not in code:\n",
+ " issues.append(\"Missing if __name__ == '__main__' block\")\n",
+ " if \"f\\\"\" in code or \"f'\" in code:\n",
+ " lines = code.split('\\n')\n",
+ " for i, line in enumerate(lines):\n",
+ " if ('f\"' in line or \"f'\" in line) and ('data[' in line or '.iloc' in line or '.loc' in line):\n",
+ " issues.append(f\"Potentially unsafe f-string formatting with pandas objects on line {i+1}\")\n",
+ " if \"try:\" in code and \"except\" not in code:\n",
+ " issues.append(\"Try block without except clause\")\n",
+ " if \"rolling\" in code and \"min_periods\" not in code:\n",
+ " issues.append(\"Rolling window without min_periods parameter (may produce NaN values)\")\n",
+ " if \".loc\" in code and \"iloc\" not in code and \"copy()\" not in code:\n",
+ " issues.append(\"Potential pandas SettingWithCopyWarning - consider using .copy() before modifications\")\n",
+ " lines = code.split('\\n')\n",
+ " defined_vars = set()\n",
+ " for line in lines:\n",
+ " if line.strip().startswith('#') or not line.strip():\n",
+ " continue\n",
+ " if '=' in line and not line.strip().startswith('if') and not line.strip().startswith('elif') and not line.strip().startswith('while'):\n",
+ " var_name = line.split('=')[0].strip()\n",
+ " if var_name:\n",
+ " defined_vars.add(var_name)\n",
+ " if issues:\n",
+ " return False, issues\n",
+ " return True, []\n",
+ "\n",
+ "def generate_trading_code(model, description, force_gpt4=False):\n",
+ " messages = messages_for(description)\n",
+ " if force_gpt4:\n",
+ " try:\n",
+ " reply = generate_with_openai(\"gpt-4o\", messages)\n",
+ " except Exception as e:\n",
+ " print(f\"Error using GPT-4o: {e}. Falling back to selected model.\")\n",
+ " if \"gpt\" in model.lower():\n",
+ " reply = generate_with_openai(model, messages)\n",
+ " else:\n",
+ " reply = generate_with_hf(model, messages)\n",
+ " else:\n",
+ " if \"gpt\" in model.lower():\n",
+ " reply = generate_with_openai(model, messages)\n",
+ " else:\n",
+ " reply = generate_with_hf(model, messages)\n",
+ " reply = reply.replace('```python','').replace('```','')\n",
+ " is_valid, issues = validate_code(reply)\n",
+ " max_attempts = 3\n",
+ " attempt = 0\n",
+ " fix_model = \"gpt-4o\" if force_gpt4 else model\n",
+ " while not is_valid and attempt < max_attempts and (\"gpt\" in model.lower() or force_gpt4):\n",
+ " attempt += 1\n",
+ " fix_messages = messages.copy()\n",
+ " fix_messages.append({\"role\": \"assistant\", \"content\": reply})\n",
+ " fix_request = f\"\"\"The code has the following issues that need to be fixed:\n",
+ "{chr(10).join([f\"- {issue}\" for issue in issues])}\n",
+ "\n",
+ "Please provide a completely corrected version that addresses these issues. Make sure to:\n",
+ "\n",
+ "1. Avoid using f-strings with pandas Series or DataFrame objects directly\n",
+ "2. Always handle NaN values in calculations with proper checks\n",
+ "3. Use proper error handling with try/except blocks around all API calls and calculations\n",
+ "4. Include min_periods parameter in rolling window calculations\n",
+ "5. Use .copy() when creating views of DataFrames that will be modified\n",
+ "6. Make sure all variables are properly defined before use\n",
+ "7. Add yfinance timeout settings: yf.set_timeout(30)\n",
+ "8. Add proper logging for all steps\n",
+ "9. Use synthetic data generation as a fallback if API calls fail\n",
+ "10. Include proper if __name__ == \"__main__\" block\n",
+ "\n",
+ "Return ONLY the corrected code with no explanation or markdown formatting.\n",
+ "\"\"\"\n",
+ " fix_messages.append({\"role\": \"user\", \"content\": fix_request})\n",
+ " try:\n",
+ " if force_gpt4:\n",
+ " fixed_reply = generate_with_openai(\"gpt-4o\", fix_messages)\n",
+ " else:\n",
+ " if \"gpt\" in model.lower():\n",
+ " fixed_reply = generate_with_openai(model, fix_messages)\n",
+ " else:\n",
+ " fixed_reply = generate_with_hf(model, fix_messages)\n",
+ " fixed_reply = fixed_reply.replace('```python','').replace('```','')\n",
+ " is_fixed_valid, fixed_issues = validate_code(fixed_reply)\n",
+ " if is_fixed_valid or len(fixed_issues) < len(issues):\n",
+ " reply = fixed_reply\n",
+ " is_valid = is_fixed_valid\n",
+ " issues = fixed_issues\n",
+ " except Exception as e:\n",
+ " print(f\"Error during fix attempt {attempt}: {e}\")\n",
+ " reply = add_safety_features(reply)\n",
+ " return reply\n",
+ "\n",
+ "def add_safety_features(code):\n",
+ " if \"pandas\" in code:\n",
+ " safety_imports = \"\"\"\n",
+ "import pandas as pd\n",
+ "pd.set_option('display.float_format', '{:.5f}'.format)\n",
+ "\n",
+ "def safe_format(obj):\n",
+ " if isinstance(obj, (pd.Series, pd.DataFrame)):\n",
+ " return str(obj)\n",
+ " return obj\n",
+ "\"\"\"\n",
+ " import_lines = [i for i, line in enumerate(code.split('\\n')) if 'import' in line]\n",
+ " if import_lines:\n",
+ " lines = code.split('\\n')\n",
+ " lines.insert(import_lines[-1] + 1, safety_imports)\n",
+ " code = '\\n'.join(lines)\n",
+ " code = code.replace(\"yf.set_timeout(30)\", \"\")\n",
+ " code = code.replace(\"yf.pdr_override()\", \"\")\n",
+ " lines = code.split('\\n')\n",
+ " for i, line in enumerate(lines):\n",
+ " if 'f\"' in line or \"f'\" in line:\n",
+ " if any(term in line for term in ['data[', '.iloc', '.loc', 'Series', 'DataFrame']):\n",
+ " for term in ['.mean()', '.sum()', '.std()', '.min()', '.max()']:\n",
+ " if term in line:\n",
+ " lines[i] = line.replace(f\"{term}\", f\"{term})\")\n",
+ " lines[i] = lines[i].replace(\"f\\\"\", \"f\\\"{safe_format(\")\n",
+ " lines[i] = lines[i].replace(\"f'\", \"f'{safe_format(\")\n",
+ " code = '\\n'.join(lines)\n",
+ " if \"plt.scatter\" in code or \".scatter\" in code:\n",
+ " scatter_safety = \"\"\"\n",
+ "def safe_scatter(ax, x, y, *args, **kwargs):\n",
+ " if len(x) != len(y):\n",
+ " min_len = min(len(x), len(y))\n",
+ " x = x[:min_len]\n",
+ " y = y[:min_len]\n",
+ " if len(x) == 0 or len(y) == 0:\n",
+ " return None\n",
+ " return ax.scatter(x, y, *args, **kwargs)\n",
+ "\"\"\"\n",
+ " func_lines = [i for i, line in enumerate(code.split('\\n')) if line.startswith('def ')]\n",
+ " if func_lines:\n",
+ " lines = code.split('\\n')\n",
+ " lines.insert(func_lines[0], scatter_safety)\n",
+ " code = '\\n'.join(lines)\n",
+ " code = code.replace(\"plt.scatter(\", \"safe_scatter(plt.gca(), \")\n",
+ " code = code.replace(\".scatter(\", \"safe_scatter(\")\n",
+ " if \"yfinance\" in code and \"generate_synthetic_data\" not in code:\n",
+ " synthetic_data_func = \"\"\"\n",
+ "def generate_synthetic_data(ticker='AAPL', start_date=None, end_date=None, days=252, seed=42):\n",
+ " import numpy as np\n",
+ " import pandas as pd\n",
+ " from datetime import datetime, timedelta\n",
+ " if start_date is None:\n",
+ " end_date = datetime.now()\n",
+ " start_date = end_date - timedelta(days=days)\n",
+ " elif end_date is None:\n",
+ " if isinstance(start_date, str):\n",
+ " start_date = pd.to_datetime(start_date)\n",
+ " end_date = datetime.now()\n",
+ " np.random.seed(seed)\n",
+ " if isinstance(start_date, str):\n",
+ " start = pd.to_datetime(start_date)\n",
+ " else:\n",
+ " start = start_date\n",
+ " if isinstance(end_date, str):\n",
+ " end = pd.to_datetime(end_date)\n",
+ " else:\n",
+ " end = end_date\n",
+ " days = (end - start).days + 1\n",
+ " price = 100\n",
+ " prices = [price]\n",
+ " for _ in range(days):\n",
+ " change = np.random.normal(0, 0.01)\n",
+ " price *= (1 + change)\n",
+ " prices.append(price)\n",
+ " dates = pd.date_range(start=start, end=end, periods=len(prices))\n",
+ " df = pd.DataFrame({\n",
+ " 'Open': prices[:-1],\n",
+ " 'High': [p * 1.01 for p in prices[:-1]],\n",
+ " 'Low': [p * 0.99 for p in prices[:-1]],\n",
+ " 'Close': prices[1:],\n",
+ " 'Volume': [np.random.randint(1000000, 10000000) for _ in range(len(prices)-1)]\n",
+ " }, index=dates[:-1])\n",
+ " return df\n",
+ "\"\"\"\n",
+ " func_lines = [i for i, line in enumerate(code.split('\\n')) if line.startswith('def ')]\n",
+ " if func_lines:\n",
+ " lines = code.split('\\n')\n",
+ " lines.insert(func_lines[0], synthetic_data_func)\n",
+ " code = '\\n'.join(lines)\n",
+ " if \"logging\" in code and \"basicConfig\" not in code:\n",
+ " logging_config = \"\"\"\n",
+ "import logging\n",
+ "logging.basicConfig(\n",
+ " level=logging.INFO,\n",
+ " format='[%(asctime)s] %(levelname)s: %(message)s',\n",
+ " datefmt='%H:%M:%S'\n",
+ ")\n",
+ "\"\"\"\n",
+ " import_lines = [i for i, line in enumerate(code.split('\\n')) if 'import' in line]\n",
+ " if import_lines:\n",
+ " lines = code.split('\\n')\n",
+ " lines.insert(import_lines[-1] + 1, logging_config)\n",
+ " code = '\\n'.join(lines)\n",
+ " if \"yfinance\" in code and \"try:\" not in code:\n",
+ " lines = code.split('\\n')\n",
+ " for i, line in enumerate(lines):\n",
+ " if \"yf.download\" in line and \"try:\" not in lines[max(0, i-5):i]:\n",
+ " indent = len(line) - len(line.lstrip())\n",
+ " indent_str = \" \" * indent\n",
+ " lines[i] = f\"{indent_str}try:\\n{indent_str} {line}\\n{indent_str}except Exception as e:\\n{indent_str} logging.error(f\\\"Error fetching data: {{e}}\\\")\\n{indent_str} # Use synthetic data as fallback\\n{indent_str} data = generate_synthetic_data(ticker, start_date, end_date)\"\n",
+ " code = '\\n'.join(lines)\n",
+ " break\n",
+ " if \"synthetic data\" in code.lower() and \"yf.download\" in code:\n",
+ " lines = code.split('\\n')\n",
+ " for i, line in enumerate(lines):\n",
+ " if \"yf.download\" in line:\n",
+ " indent = len(line) - len(line.lstrip())\n",
+ " indent_str = \" \" * indent\n",
+ " comment = f\"{indent_str}# Using synthetic data instead of API call\\n\"\n",
+ " synthetic = f\"{indent_str}data = generate_synthetic_data(ticker, start_date, end_date)\\n\"\n",
+ " lines[i] = f\"{indent_str}# {line.strip()} # Commented out to avoid API issues\"\n",
+ " lines.insert(i+1, comment + synthetic)\n",
+ " code = '\\n'.join(lines)\n",
+ " break\n",
+ " if \"plt.figure\" in code:\n",
+ " lines = code.split('\\n')\n",
+ " for i, line in enumerate(lines):\n",
+ " if \"plt.figure\" in line and \"try:\" not in lines[max(0, i-5):i]:\n",
+ " indent = len(line) - len(line.lstrip())\n",
+ " indent_str = \" \" * indent\n",
+ " try_line = f\"{indent_str}try:\\n{indent_str} \"\n",
+ " except_line = f\"\\n{indent_str}except Exception as e:\\n{indent_str} logging.error(f\\\"Error in plotting: {{e}}\\\")\"\n",
+ " j = i\n",
+ " while j < len(lines) and (j == i or lines[j].startswith(indent_str)):\n",
+ " j += 1\n",
+ " for k in range(i, j):\n",
+ " if lines[k].strip():\n",
+ " lines[k] = indent_str + \" \" + lines[k].lstrip()\n",
+ " lines.insert(i, try_line.rstrip())\n",
+ " lines.insert(j+1, except_line)\n",
+ " code = '\\n'.join(lines)\n",
+ " break\n",
+ " lines = code.split('\\n')\n",
+ " for i, line in enumerate(lines):\n",
+ " if \"print(\" in line and any(term in line for term in ['data[', '.iloc', '.loc', 'Series', 'DataFrame']):\n",
+ " lines[i] = line.replace(\"print(\", \"print(safe_format(\")\n",
+ " if \"))\" not in lines[i] and \"),\" in lines[i]:\n",
+ " lines[i] = lines[i].replace(\"),\", \")),\", 1)\n",
+ " elif \"))\" not in lines[i] and \")\" in lines[i]:\n",
+ " lines[i] = lines[i].replace(\")\", \"))\", 1)\n",
+ " code = '\\n'.join(lines)\n",
+ " return code\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 114,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def run_python(code):\n",
+ " # Create a completely separate namespace for execution\n",
+ " namespace = {\n",
+ " '__name__': '__main__',\n",
+ " '__builtins__': __builtins__\n",
+ " }\n",
+ " \n",
+ " # Modify the code to use a non-interactive matplotlib backend\n",
+ " # and fix pandas formatting issues\n",
+ " modified_code = \"\"\"\n",
+ "import matplotlib\n",
+ "matplotlib.use('Agg') # Use non-interactive backend\n",
+ "\n",
+ "# Import yfinance without setting timeout (not available in all versions)\n",
+ "import yfinance as yf\n",
+ "\n",
+ "# Configure logging to show in the output\n",
+ "import logging\n",
+ "logging.basicConfig(\n",
+ " level=logging.INFO,\n",
+ " format='[%(asctime)s] %(levelname)s: %(message)s',\n",
+ " datefmt='%H:%M:%S'\n",
+ ")\n",
+ "\n",
+ "# Fix pandas formatting issues\n",
+ "import pandas as pd\n",
+ "pd.set_option('display.float_format', '{:.5f}'.format)\n",
+ "\n",
+ "# Override print to ensure it flushes immediately\n",
+ "import builtins\n",
+ "original_print = builtins.print\n",
+ "def custom_print(*args, **kwargs):\n",
+ " result = original_print(*args, **kwargs)\n",
+ " import sys\n",
+ " sys.stdout.flush()\n",
+ " return result\n",
+ "builtins.print = custom_print\n",
+ "\n",
+ "# Helper function to safely format pandas objects\n",
+ "def safe_format(obj):\n",
+ " if isinstance(obj, (pd.Series, pd.DataFrame)):\n",
+ " return str(obj)\n",
+ " else:\n",
+ " return obj\n",
+ "\"\"\"\n",
+ " \n",
+ " # Add the user's code\n",
+ " modified_code += \"\\n\" + code\n",
+ " \n",
+ " # Capture all output\n",
+ " output_buffer = io.StringIO()\n",
+ " \n",
+ " # Save original stdout and redirect to our buffer\n",
+ " original_stdout = sys.stdout\n",
+ " sys.stdout = output_buffer\n",
+ " \n",
+ " # Add timestamp for execution start\n",
+ " print(f\"[{time.strftime('%H:%M:%S')}] Executing code...\")\n",
+ " \n",
+ " try:\n",
+ " # Execute the modified code\n",
+ " exec(modified_code, namespace)\n",
+ " print(f\"\\n[{time.strftime('%H:%M:%S')}] Execution completed successfully.\")\n",
+ " \n",
+ " except ModuleNotFoundError as e:\n",
+ " missing_module = str(e).split(\"'\")[1]\n",
+ " print(f\"\\nError: Missing module '{missing_module}'. Click 'Install Dependencies' to install it.\")\n",
+ " namespace[\"__missing_module__\"] = missing_module\n",
+ " \n",
+ " except Exception as e:\n",
+ " print(f\"\\n[{time.strftime('%H:%M:%S')}] Error during execution: {str(e)}\")\n",
+ " import traceback\n",
+ " print(traceback.format_exc())\n",
+ " \n",
+ " finally:\n",
+ " # Restore original stdout\n",
+ " sys.stdout = original_stdout\n",
+ " \n",
+ " # Return the captured output\n",
+ " return output_buffer.getvalue()\n",
+ "\n",
+ "def install_dependencies(code):\n",
+ " import re\n",
+ " import subprocess\n",
+ " \n",
+ " import_pattern = r'(?:from|import)\\s+([a-zA-Z0-9_]+)(?:\\s+(?:import|as))?'\n",
+ " imports = re.findall(import_pattern, code)\n",
+ " \n",
+ " std_libs = ['os', 'sys', 'io', 'time', 'datetime', 'random', 'math', 're', 'json', \n",
+ " 'collections', 'itertools', 'functools', 'operator', 'pathlib', 'typing']\n",
+ " \n",
+ " modules_to_install = [module for module in imports if module not in std_libs]\n",
+ " \n",
+ " if not modules_to_install:\n",
+ " return \"No external dependencies found to install.\"\n",
+ " \n",
+ " results = []\n",
+ " for module in modules_to_install:\n",
+ " try:\n",
+ " result = subprocess.run(\n",
+ " [sys.executable, \"-m\", \"pip\", \"install\", module],\n",
+ " capture_output=True,\n",
+ " text=True,\n",
+ " check=False\n",
+ " )\n",
+ " \n",
+ " if result.returncode == 0:\n",
+ " results.append(f\"Successfully installed {module}\")\n",
+ " else:\n",
+ " results.append(f\"Failed to install {module}: {result.stderr}\")\n",
+ " except Exception as e:\n",
+ " results.append(f\"Error installing {module}: {str(e)}\")\n",
+ " \n",
+ " return \"\\n\".join(results)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 109,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "trading_strategies = [\n",
+ " {\n",
+ " \"name\": \"Moving Average Crossover\",\n",
+ " \"description\": \"Moving Average Crossover strategy for S&P 500 stocks. Buy when the 20-day moving average crosses above the 50-day moving average, and sell when it crosses below.\",\n",
+ " \"buy_signal\": \"20-day MA crosses above 50-day MA\",\n",
+ " \"sell_signal\": \"20-day MA crosses below 50-day MA\",\n",
+ " \"timeframe\": \"Daily\",\n",
+ " \"risk_level\": \"Medium\"\n",
+ " },\n",
+ " {\n",
+ " \"name\": \"RSI Mean Reversion\",\n",
+ " \"description\": \"Mean reversion strategy that buys stocks when RSI falls below 30 (oversold) and sells when RSI rises above 70 (overbought).\",\n",
+ " \"buy_signal\": \"RSI below 30 (oversold)\",\n",
+ " \"sell_signal\": \"RSI above 70 (overbought)\",\n",
+ " \"timeframe\": \"Daily\",\n",
+ " \"risk_level\": \"Medium\"\n",
+ " },\n",
+ " {\n",
+ " \"name\": \"Momentum Strategy\",\n",
+ " \"description\": \"Momentum strategy that buys the top 5 performing stocks from the Dow Jones Industrial Average over the past month and rebalances monthly.\",\n",
+ " \"buy_signal\": \"Stock in top 5 performers over past month\",\n",
+ " \"sell_signal\": \"Stock no longer in top 5 performers at rebalance\",\n",
+ " \"timeframe\": \"Monthly\",\n",
+ " \"risk_level\": \"High\"\n",
+ " },\n",
+ " {\n",
+ " \"name\": \"Pairs Trading\",\n",
+ " \"description\": \"Pairs trading strategy that identifies correlated stock pairs and trades on the divergence and convergence of their price relationship.\",\n",
+ " \"buy_signal\": \"Pairs ratio deviates 2+ standard deviations below mean\",\n",
+ " \"sell_signal\": \"Pairs ratio returns to mean or exceeds mean\",\n",
+ " \"timeframe\": \"Daily\",\n",
+ " \"risk_level\": \"Medium-High\"\n",
+ " },\n",
+ " {\n",
+ " \"name\": \"Bollinger Band Breakout\",\n",
+ " \"description\": \"Volatility breakout strategy that buys when a stock breaks out of its upper Bollinger Band and sells when it reverts to the mean.\",\n",
+ " \"buy_signal\": \"Price breaks above upper Bollinger Band (2 std dev)\",\n",
+ " \"sell_signal\": \"Price reverts to middle Bollinger Band (SMA)\",\n",
+ " \"timeframe\": \"Daily\",\n",
+ " \"risk_level\": \"High\"\n",
+ " },\n",
+ " {\n",
+ " \"name\": \"MACD Crossover\",\n",
+ " \"description\": \"MACD crossover strategy that buys when the MACD line crosses above the signal line and sells when it crosses below.\",\n",
+ " \"buy_signal\": \"MACD line crosses above signal line\",\n",
+ " \"sell_signal\": \"MACD line crosses below signal line\",\n",
+ " \"timeframe\": \"Daily\",\n",
+ " \"risk_level\": \"Medium\"\n",
+ " },\n",
+ " {\n",
+ " \"name\": \"Golden Cross\",\n",
+ " \"description\": \"Golden Cross strategy that buys when the 50-day moving average crosses above the 200-day moving average and sells on the Death Cross (opposite).\",\n",
+ " \"buy_signal\": \"50-day MA crosses above 200-day MA\",\n",
+ " \"sell_signal\": \"50-day MA crosses below 200-day MA\",\n",
+ " \"timeframe\": \"Daily\",\n",
+ " \"risk_level\": \"Low\"\n",
+ " }\n",
+ "]\n",
+ "\n",
+ "sample_strategies = [strategy[\"description\"] for strategy in trading_strategies]\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 110,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "default_description = \"\"\"\n",
+ "Create a moving average crossover strategy with the following specifications:\n",
+ "- Use yfinance to download historical data for a list of stocks (AAPL, MSFT, AMZN, GOOGL, META)\n",
+ "- Calculate 20-day and 50-day moving averages\n",
+ "- Generate buy signals when the 20-day MA crosses above the 50-day MA\n",
+ "- Generate sell signals when the 20-day MA crosses below the 50-day MA\n",
+ "- Implement a simple backtesting framework to evaluate the strategy\n",
+ "- Calculate performance metrics: total return, annualized return, Sharpe ratio, max drawdown\n",
+ "- Visualize the equity curve, buy/sell signals, and moving averages\n",
+ "\"\"\"\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "2025-10-22 18:30:21,233 - INFO - HTTP Request: GET http://127.0.0.1:7875/gradio_api/startup-events \"HTTP/1.1 200 OK\"\n",
+ "2025-10-22 18:30:21,238 - INFO - HTTP Request: HEAD http://127.0.0.1:7875/ \"HTTP/1.1 200 OK\"\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "* Running on local URL: http://127.0.0.1:7875\n",
+ "* To create a public link, set `share=True` in `launch()`.\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "