diff --git a/week4/community-contributions/dkisselev-zz/Week4_Excersise_Trading.ipynb b/week4/community-contributions/dkisselev-zz/Week4_Excersise_Trading.ipynb new file mode 100644 index 0000000..7c75859 --- /dev/null +++ b/week4/community-contributions/dkisselev-zz/Week4_Excersise_Trading.ipynb @@ -0,0 +1,1402 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5cd7107e", + "metadata": {}, + "source": [ + "# Crypto Trading System with Kraken API\n", + "\n", + "A crypto trading system that fetches data from Kraken, visualizes price charts with Simple Moving Averages (SMA), uses LLM to analyze trading signals, and executes real trades on Kraken's sandbox environment.\n", + "\n", + "## Features\n", + "- **Real-time Data**: Fetch BTC/USD and ETH/USD data from Kraken API\n", + "- **Technical Analysis**: Interactive charts with customizable SMA overlays\n", + "- **AI Trading Advisor**: LLM-powered trading recommendations based on technical indicators\n", + "- **Real Trading**: Execute actual trades on Kraken's sandbox environment\n", + "- **Multi-Model Support**: Choose from GPT-5, Claude Sonnet, or Gemini for analysis\n", + "\n", + "## Setup Requirements\n", + "1. **API Keys**: Configure OpenAI, Anthropic, Google, and Kraken API keys in `.env` file\n", + "2. **Kraken Sandbox**: Get sandbox API credentials from [Kraken Developer Portal](https://www.kraken.com/features/api)\n", + "3. **Sandbox Funding**: Kraken sandbox may require initial funding for testing trades\n", + "\n", + "## Process stes\n", + "1. **Market Data Tab**: Select crypto pair and SMA windows, fetch data\n", + "2. **Trading Analysis Tab**: Get LLM-powered trading recommendations\n", + "3. **Paper Trading Tab**: Execute real trades based on AI recommendations\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31c1f5f0", + "metadata": {}, + "outputs": [], + "source": [ + "# Install dependencies\n", + "!uv add ccxt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c58efdd", + "metadata": {}, + "outputs": [], + "source": [ + "# Imports and Setup\n", + "import os\n", + "import time\n", + "import pandas as pd\n", + "import numpy as np\n", + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "import ccxt\n", + "from datetime import datetime\n", + "from dotenv import load_dotenv\n", + "from openai import OpenAI\n", + "import gradio as gr\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "48648e9e", + "metadata": {}, + "outputs": [], + "source": [ + "# API Key Setup\n", + "load_dotenv(override=True)\n", + "\n", + "# LLM API Keys\n", + "openai_api_key = os.getenv('OPENAI_API_KEY')\n", + "anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')\n", + "google_api_key = os.getenv('GOOGLE_API_KEY')\n", + "\n", + "# Kraken API Keys (for sandbox trading)\n", + "kraken_api_key = os.getenv('KRAKEN_API_KEY')\n", + "kraken_secret = os.getenv('KRAKEN_API_SECRET')\n", + "\n", + "# Initialize API clients\n", + "anthropic_url = \"https://api.anthropic.com/v1/\"\n", + "gemini_url = \"https://generativelanguage.googleapis.com/v1beta/openai/\"\n", + "\n", + "clients = {}\n", + "if openai_api_key:\n", + " clients['openai'] = OpenAI(api_key=openai_api_key)\n", + " print(f\"✅ OpenAI API Key loaded: {openai_api_key[:8]}...\")\n", + "else:\n", + " print(\"❌ OpenAI API Key not set\")\n", + "\n", + "if anthropic_api_key:\n", + " clients['anthropic'] = OpenAI(api_key=anthropic_api_key, base_url=anthropic_url)\n", + " print(f\"✅ Anthropic API Key loaded: {anthropic_api_key[:7]}...\")\n", + "else:\n", + " print(\"❌ Anthropic API Key not set\")\n", + "\n", + "if google_api_key:\n", + " clients['google'] = OpenAI(api_key=google_api_key, base_url=gemini_url)\n", + " print(f\"✅ Google API Key loaded: {google_api_key[:8]}...\")\n", + "else:\n", + " print(\"❌ Google API Key not set\")\n", + "\n", + "if kraken_api_key and kraken_secret:\n", + " print(f\"✅ Kraken API Keys loaded: {kraken_api_key[:8]}...\")\n", + "else:\n", + " print(\"❌ Kraken API Keys not set - paper trading will be disabled\")\n", + "\n", + "# Model configuration\n", + "MODELS = {\n", + " \"GPT-5\": {\"model_id\": \"gpt-5-mini\", \"provider\": \"openai\"},\n", + " \"Claude Sonnet\": {\"model_id\": \"claude-sonnet-4-5-20250929\", \"provider\": \"anthropic\"},\n", + " \"Gemini\": {\"model_id\": \"gemini-2.5-flash-lite\", \"provider\": \"google\"}\n", + "}\n", + "\n", + "CRYPTO_PAIRS = {\n", + " \"BTC/USD\": \"PI_XBTUSD\", # Bitcoin futures symbol\n", + " \"ETH/USD\": \"PI_ETHUSD\" # Ethereum futures symbol\n", + "}\n", + "\n", + "print(\"✅ API setup complete!\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59cdc0df", + "metadata": {}, + "outputs": [], + "source": [ + "# Kraken Data Fetcher Module\n", + "class KrakenDataFetcher:\n", + " \"\"\"Handles data fetching from Kraken API and SMA calculations\"\"\"\n", + " \n", + " def __init__(self):\n", + " self.exchange = ccxt.kraken({\n", + " 'enableRateLimit': True,\n", + " 'timeout': 30000\n", + " })\n", + " self.cached_data = {}\n", + " \n", + " def fetch_ohlcv(self, symbol, timeframe='1h', limit=720):\n", + " \"\"\"Fetch OHLCV data from Kraken\"\"\"\n", + " try:\n", + " # Convert symbol to Kraken format\n", + " # kraken_symbol = CRYPTO_PAIRS.get(symbol, symbol)\n", + " kraken_symbol = symbol\n", + " \n", + " # Check cache first\n", + " cache_key = f\"{kraken_symbol}_{timeframe}_{limit}\"\n", + " if cache_key in self.cached_data:\n", + " cache_time, data = self.cached_data[cache_key]\n", + " if time.time() - cache_time < 300: # 5 minute cache\n", + " print(f\"📊 Using cached data for {symbol}\")\n", + " return data\n", + " \n", + " print(f\"🔄 Fetching {symbol} data from Kraken...\")\n", + " ohlcv = self.exchange.fetch_ohlcv(kraken_symbol, timeframe, limit=limit)\n", + " \n", + " # Convert to DataFrame\n", + " df = pd.DataFrame(ohlcv, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])\n", + " df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')\n", + " df.set_index('timestamp', inplace=True)\n", + " \n", + " # Cache the data\n", + " self.cached_data[cache_key] = (time.time(), df)\n", + " \n", + " print(f\"✅ Fetched {len(df)} records for {symbol}\")\n", + " return df\n", + " \n", + " except Exception as e:\n", + " print(f\"❌ Error fetching data for {symbol}: {str(e)}\")\n", + " return pd.DataFrame()\n", + " \n", + " def calculate_sma(self, data, window):\n", + " \"\"\"Calculate Simple Moving Average\"\"\"\n", + " if len(data) < window:\n", + " return pd.Series(index=data.index, dtype=float)\n", + " return data['close'].rolling(window=window).mean()\n", + " \n", + " def detect_crossover(self, sma_short, sma_long):\n", + " \"\"\"Detect SMA crossover signals\"\"\"\n", + " if len(sma_short) < 2 or len(sma_long) < 2:\n", + " return \"No data\"\n", + " \n", + " # Get the last two values\n", + " short_current = sma_short.iloc[-1]\n", + " short_previous = sma_short.iloc[-2]\n", + " long_current = sma_long.iloc[-1]\n", + " long_previous = sma_long.iloc[-2]\n", + " \n", + " # Check for crossover\n", + " if pd.isna(short_current) or pd.isna(long_current) or pd.isna(short_previous) or pd.isna(long_previous):\n", + " return \"Insufficient data\"\n", + " \n", + " # Golden cross: short SMA crosses above long SMA\n", + " if short_previous <= long_previous and short_current > long_current:\n", + " return \"Golden Cross (BUY Signal)\"\n", + " \n", + " # Death cross: short SMA crosses below long SMA\n", + " if short_previous >= long_previous and short_current < long_current:\n", + " return \"Death Cross (SELL Signal)\"\n", + " \n", + " # No crossover\n", + " if short_current > long_current:\n", + " return \"Short SMA above Long SMA\"\n", + " else:\n", + " return \"Short SMA below Long SMA\"\n", + " \n", + " def get_market_summary(self, data, sma_short, sma_long):\n", + " \"\"\"Get market summary for LLM analysis\"\"\"\n", + " if data.empty:\n", + " return {}\n", + " \n", + " current_price = data['close'].iloc[-1]\n", + " price_change = ((current_price - data['close'].iloc[-2]) / data['close'].iloc[-2]) * 100 if len(data) > 1 else 0\n", + " \n", + " # Volume analysis\n", + " avg_volume = data['volume'].rolling(window=10).mean().iloc[-1] if len(data) >= 10 else data['volume'].mean()\n", + " current_volume = data['volume'].iloc[-1]\n", + " volume_ratio = current_volume / avg_volume if avg_volume > 0 else 1\n", + " \n", + " # Price momentum (last 5 periods)\n", + " momentum = ((data['close'].iloc[-1] - data['close'].iloc[-6]) / data['close'].iloc[-6]) * 100 if len(data) >= 6 else 0\n", + " \n", + " return {\n", + " 'current_price': current_price,\n", + " 'price_change_pct': round(price_change, 2),\n", + " 'current_volume': current_volume,\n", + " 'volume_ratio': round(volume_ratio, 2),\n", + " 'momentum_5d': round(momentum, 2),\n", + " 'sma_short': sma_short.iloc[-1] if not sma_short.empty else None,\n", + " 'sma_long': sma_long.iloc[-1] if not sma_long.empty else None,\n", + " 'crossover_status': self.detect_crossover(sma_short, sma_long)\n", + " }\n", + "\n", + "# Initialize data fetcher\n", + "data_fetcher = KrakenDataFetcher()\n", + "print(\"✅ Kraken Data Fetcher initialized!\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5976c68", + "metadata": {}, + "outputs": [], + "source": [ + "# Chart Builder Module\n", + "class ChartBuilder:\n", + " \"\"\"Creates interactive charts with candlesticks and SMA overlays\"\"\"\n", + " \n", + " def __init__(self):\n", + " self.colors = {\n", + " 'bullish': '#00ff88',\n", + " 'bearish': '#ff4444',\n", + " 'sma_short': '#2196F3',\n", + " 'sma_long': '#FF9800',\n", + " 'volume': '#9C27B0'\n", + " }\n", + " \n", + " def create_candlestick_chart(self, data, sma_short, sma_long, symbol):\n", + " \"\"\"Create interactive candlestick chart with SMA overlays\"\"\"\n", + " if data.empty:\n", + " return go.Figure()\n", + " \n", + " try:\n", + " # Create a simple figure without subplots first\n", + " fig = go.Figure()\n", + " \n", + " # Add candlestick chart using only the data values\n", + " fig.add_trace(go.Candlestick(\n", + " x=list(range(len(data))), \n", + " open=data['open'].values,\n", + " high=data['high'].values,\n", + " low=data['low'].values,\n", + " close=data['close'].values,\n", + " name='Price',\n", + " increasing_line_color='#00ff00',\n", + " decreasing_line_color='#ff0000'\n", + " ))\n", + " \n", + " # Add SMA lines if available\n", + " if sma_short is not None and not sma_short.empty:\n", + " # Filter out NaN values and align with data\n", + " sma_short_clean = sma_short.dropna()\n", + " if not sma_short_clean.empty:\n", + " sma_x = list(range(len(sma_short_clean)))\n", + " fig.add_trace(go.Scatter(\n", + " x=sma_x,\n", + " y=sma_short_clean.values,\n", + " mode='lines',\n", + " name='SMA Short',\n", + " line=dict(color='#2196F3', width=2)\n", + " ))\n", + " \n", + " if sma_long is not None and not sma_long.empty:\n", + " # Filter out NaN values and align with data\n", + " sma_long_clean = sma_long.dropna()\n", + " if not sma_long_clean.empty:\n", + "\n", + " sma_x = list(range(len(sma_long_clean)))\n", + " fig.add_trace(go.Scatter(\n", + " x=sma_x,\n", + " y=sma_long_clean.values,\n", + " mode='lines',\n", + " name='SMA Long',\n", + " line=dict(color='#FF9800', width=2)\n", + " ))\n", + " \n", + " # Update layout\n", + " fig.update_layout(\n", + " title=f'{symbol} Trading Chart',\n", + " xaxis_title=\"Time Period\",\n", + " yaxis_title=\"Price ($)\",\n", + " height=600,\n", + " showlegend=True,\n", + " template='plotly_white'\n", + " )\n", + " \n", + " return fig\n", + " \n", + " except Exception as e:\n", + " print(f\"❌ Error in chart creation: {str(e)}\")\n", + " # Return a simple empty figure\n", + " return go.Figure()\n", + " \n", + " def _find_crossover_points(self, sma_short, sma_long, timestamps):\n", + " \"\"\"Find crossover points between SMAs\"\"\"\n", + " if len(sma_short) < 2 or len(sma_long) < 2:\n", + " return []\n", + " \n", + " crossover_points = []\n", + " \n", + " for i in range(1, len(sma_short)):\n", + " if pd.isna(sma_short.iloc[i]) or pd.isna(sma_long.iloc[i]) or \\\n", + " pd.isna(sma_short.iloc[i-1]) or pd.isna(sma_long.iloc[i-1]):\n", + " continue\n", + " \n", + " # Golden cross: short SMA crosses above long SMA\n", + " if sma_short.iloc[i-1] <= sma_long.iloc[i-1] and sma_short.iloc[i] > sma_long.iloc[i]:\n", + " crossover_points.append({\n", + " 'timestamp': timestamps[i],\n", + " 'type': 'Golden Cross'\n", + " })\n", + " \n", + " # Death cross: short SMA crosses below long SMA\n", + " elif sma_short.iloc[i-1] >= sma_long.iloc[i-1] and sma_short.iloc[i] < sma_long.iloc[i]:\n", + " crossover_points.append({\n", + " 'timestamp': timestamps[i],\n", + " 'type': 'Death Cross'\n", + " })\n", + " \n", + " return crossover_points\n", + " \n", + " def create_summary_chart(self, data, symbol):\n", + " \"\"\"Create a simple price summary chart\"\"\"\n", + " if data.empty:\n", + " return go.Figure()\n", + " \n", + " fig = go.Figure()\n", + " \n", + " # Add price line\n", + " fig.add_trace(go.Scatter(\n", + " x=data.index,\n", + " y=data['close'],\n", + " mode='lines',\n", + " name='Price',\n", + " line=dict(color='white', width=2)\n", + " ))\n", + " \n", + " # Add volume as background bars\n", + " fig.add_trace(go.Bar(\n", + " x=data.index,\n", + " y=data['volume'],\n", + " name='Volume',\n", + " opacity=0.3,\n", + " yaxis='y2'\n", + " ))\n", + " \n", + " fig.update_layout(\n", + " title=f'{symbol} Price Summary',\n", + " xaxis_title='Time',\n", + " yaxis_title='Price (USD)',\n", + " yaxis2=dict(title='Volume', overlaying='y', side='right'),\n", + " template='plotly_dark',\n", + " height=400\n", + " )\n", + " \n", + " return fig\n", + "\n", + "# Initialize chart builder\n", + "chart_builder = ChartBuilder()\n", + "print(\"✅ Chart Builder initialized!\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ad44be2f", + "metadata": {}, + "outputs": [], + "source": [ + "# LLM Trading Advisor Module\n", + "class TradingAdvisor:\n", + " \"\"\"Uses LLM to analyze market data and provide trading recommendations\"\"\"\n", + " \n", + " def __init__(self):\n", + " self.system_prompt = \"\"\"You are an expert cryptocurrency trading advisor with deep knowledge of technical analysis and market dynamics. \n", + "\n", + "Your task is to analyze cryptocurrency market data and provide clear, actionable trading recommendations.\n", + "\n", + "Key factors to consider:\n", + "1. SMA Crossover Signals: Golden Cross (short SMA > long SMA) suggests bullish momentum, Death Cross (short SMA < long SMA) suggests bearish momentum\n", + "2. Price Momentum: Recent price movements and trends\n", + "3. Volume Analysis: Volume spikes often indicate strong moves\n", + "4. Market Context: Overall market conditions and volatility\n", + "\n", + "Provide your analysis in this exact format:\n", + "DECISION: [BUY/SELL/HOLD]\n", + "CONFIDENCE: [1-10 scale]\n", + "REASONING: [2-3 sentence explanation of your decision]\n", + "RISK_LEVEL: [LOW/MEDIUM/HIGH]\n", + "\n", + "Be concise but thorough in your analysis. Focus on the most relevant technical indicators.\"\"\"\n", + "\n", + " def analyze_market(self, market_summary, model_name=\"GPT-5\", temperature=0.3):\n", + " \"\"\"Analyze market data and provide trading recommendation\"\"\"\n", + " try:\n", + " if not market_summary:\n", + " return {\n", + " 'decision': 'HOLD',\n", + " 'confidence': 1,\n", + " 'reasoning': 'Insufficient market data for analysis',\n", + " 'risk_level': 'HIGH'\n", + " }\n", + " \n", + " # Prepare context for LLM\n", + " context = self._prepare_market_context(market_summary)\n", + " \n", + " # Query the selected model\n", + " response = self._query_llm(model_name, context, temperature)\n", + " \n", + " # Parse the response\n", + " recommendation = self._parse_llm_response(response)\n", + " \n", + " return recommendation\n", + " \n", + " except Exception as e:\n", + " print(f\"❌ Error in trading analysis: {str(e)}\")\n", + " return {\n", + " 'decision': 'HOLD',\n", + " 'confidence': 1,\n", + " 'reasoning': f'Analysis error: {str(e)}',\n", + " 'risk_level': 'HIGH'\n", + " }\n", + " \n", + " def _prepare_market_context(self, market_summary):\n", + " \"\"\"Prepare market context for LLM analysis\"\"\"\n", + " # Handle SMA values safely\n", + " sma_short = market_summary.get('sma_short')\n", + " sma_long = market_summary.get('sma_long')\n", + " \n", + " sma_short_str = f\"${sma_short:.2f}\" if sma_short is not None else \"N/A\"\n", + " sma_long_str = f\"${sma_long:.2f}\" if sma_long is not None else \"N/A\"\n", + " \n", + " context = f\"\"\"\n", + "Current Market Analysis for Trading Decision:\n", + "\n", + "PRICE DATA:\n", + "- Current Price: ${market_summary.get('current_price', 0):.2f}\n", + "- Price Change: {market_summary.get('price_change_pct', 0):.2f}%\n", + "- 5-Day Momentum: {market_summary.get('momentum_5d', 0):.2f}%\n", + "\n", + "TECHNICAL INDICATORS:\n", + "- Short SMA: {sma_short_str}\n", + "- Long SMA: {sma_long_str}\n", + "- Crossover Status: {market_summary.get('crossover_status', 'Unknown')}\n", + "\n", + "VOLUME ANALYSIS:\n", + "- Current Volume: {market_summary.get('current_volume', 0):,.0f}\n", + "- Volume Ratio: {market_summary.get('volume_ratio', 1):.2f}x (vs 10-day average)\n", + "\n", + "Based on this data, provide a trading recommendation following the specified format.\n", + "\"\"\"\n", + " return context\n", + " \n", + " def _query_llm(self, model_name, context, temperature):\n", + " if model_name not in MODELS:\n", + " raise ValueError(f\"Unknown model: {model_name}\")\n", + " \n", + " model_info = MODELS[model_name]\n", + " provider = model_info['provider']\n", + " model_id = model_info['model_id']\n", + " \n", + " if provider not in clients:\n", + " raise ValueError(f\"Client not available for {provider}\")\n", + " \n", + " try:\n", + " response = clients[provider].chat.completions.create(\n", + " model=model_id,\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": self.system_prompt},\n", + " {\"role\": \"user\", \"content\": context}\n", + " ],\n", + " temperature=temperature if model_id != \"gpt-5-mini\" else 1,\n", + " max_completion_tokens=500 \n", + " )\n", + " \n", + " return response.choices[0].message.content\n", + " \n", + " except Exception as e:\n", + " raise Exception(f\"LLM query failed: {str(e)}\")\n", + "\n", + " \n", + " def _parse_llm_response(self, response):\n", + " \"\"\"Parse LLM response into structured format\"\"\"\n", + " try:\n", + " lines = response.strip().split('\\n')\n", + " decision = 'HOLD'\n", + " confidence = 5\n", + " reasoning = 'No clear reasoning provided'\n", + " risk_level = 'MEDIUM'\n", + " \n", + " for line in lines:\n", + " line = line.strip()\n", + " if line.startswith('DECISION:'):\n", + " decision = line.split(':', 1)[1].strip().upper()\n", + " elif line.startswith('CONFIDENCE:'):\n", + " try:\n", + " confidence = int(line.split(':', 1)[1].strip())\n", + " confidence = max(1, min(10, confidence)) # Clamp between 1-10\n", + " except:\n", + " confidence = 5\n", + " elif line.startswith('REASONING:'):\n", + " reasoning = line.split(':', 1)[1].strip()\n", + " elif line.startswith('RISK_LEVEL:'):\n", + " risk_level = line.split(':', 1)[1].strip().upper()\n", + " \n", + " # Validate decision\n", + " if decision not in ['BUY', 'SELL', 'HOLD']:\n", + " decision = 'HOLD'\n", + " \n", + " # Validate risk level\n", + " if risk_level not in ['LOW', 'MEDIUM', 'HIGH']:\n", + " risk_level = 'MEDIUM'\n", + " \n", + " return {\n", + " 'decision': decision,\n", + " 'confidence': confidence,\n", + " 'reasoning': reasoning,\n", + " 'risk_level': risk_level,\n", + " 'raw_response': response\n", + " }\n", + " \n", + " except Exception as e:\n", + " return {\n", + " 'decision': 'HOLD',\n", + " 'confidence': 1,\n", + " 'reasoning': f'Failed to parse LLM response: {str(e)}',\n", + " 'risk_level': 'HIGH',\n", + " 'raw_response': response\n", + " }\n", + " \n", + " def get_confidence_color(self, confidence):\n", + " \"\"\"Get color based on confidence level\"\"\"\n", + " if confidence >= 8:\n", + " return '#00ff88' # Green\n", + " elif confidence >= 6:\n", + " return '#ffaa00' # Orange\n", + " else:\n", + " return '#ff4444' # Red\n", + " \n", + " def get_decision_emoji(self, decision):\n", + " \"\"\"Get emoji for decision\"\"\"\n", + " emojis = {\n", + " 'BUY': '🟢',\n", + " 'SELL': '🔴',\n", + " 'HOLD': '🟡'\n", + " }\n", + " return emojis.get(decision, '❓')\n", + "\n", + "# Initialize trading advisor\n", + "trading_advisor = TradingAdvisor()\n", + "print(\"✅ Trading Advisor initialized!\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ae115eb0", + "metadata": {}, + "outputs": [], + "source": [ + "# Paper Trader Module for Kraken Sandbox\n", + "class PaperTrader:\n", + " \"\"\"Handles real trading on Kraken sandbox environment\"\"\"\n", + " \n", + " def __init__(self):\n", + " self.exchange = None\n", + " self.connected = False\n", + " self.trade_history = []\n", + " \n", + " if kraken_api_key and kraken_secret:\n", + " self._connect_sandbox()\n", + " \n", + " def _connect_sandbox(self):\n", + " \"\"\"Connect to Kraken sandbox environment\"\"\"\n", + " try:\n", + " self.exchange = ccxt.krakenfutures({\n", + " 'apiKey': kraken_api_key, \n", + " 'secret': kraken_secret,\n", + " 'enableRateLimit': True,\n", + " })\n", + "\n", + " self.exchange.set_sandbox_mode(True)\n", + " \n", + " # Test connection\n", + " self.exchange.load_markets()\n", + " self.connected = True\n", + " print(\"✅ Connected to Kraken sandbox environment\")\n", + " \n", + " except Exception as e:\n", + " print(f\"❌ Failed to connect to Kraken sandbox: {str(e)}\")\n", + " self.connected = False\n", + " \n", + " def get_balance(self):\n", + " \"\"\"Get real balance from Kraken sandbox\"\"\"\n", + " if not self.connected:\n", + " return {'USD': 0, 'BTC': 0, 'ETH': 0}\n", + " \n", + " try:\n", + " # Fetch real balance from Kraken\n", + " balance = self.exchange.fetch_balance()\n", + " \n", + " # Extract relevant currencies\n", + " kraken_balance = {\n", + " 'USD': balance.get('USD', {}).get('free', 0),\n", + " 'BTC': balance.get('BTC', {}).get('free', 0),\n", + " 'ETH': balance.get('ETH', {}).get('free', 0)\n", + " }\n", + " \n", + " return kraken_balance\n", + " \n", + " except Exception as e:\n", + " print(f\"❌ Error fetching balance: {str(e)}\")\n", + " return {'USD': 0, 'BTC': 0, 'ETH': 0}\n", + " \n", + " def place_order(self, symbol, side, amount, price=None):\n", + " \"\"\"Place a real trade order on Kraken sandbox\"\"\"\n", + " if not self.connected:\n", + " return {\n", + " 'success': False,\n", + " 'message': \"❌ Not connected to Kraken sandbox\",\n", + " 'trade': None\n", + " }\n", + " \n", + " try:\n", + " # Convert symbol to Kraken format\n", + " kraken_symbol = CRYPTO_PAIRS.get(symbol, symbol)\n", + " \n", + " # Get current market price if not provided\n", + " ticker = self.exchange.fetch_ticker(kraken_symbol)\n", + " \n", + " price = ticker[\"ask\"] if price is None else price\n", + " \n", + " # Check balance before placing order\n", + " balance = self.get_balance()\n", + " crypto_symbol = symbol.split('/')[0]\n", + " \n", + " if side == 'buy':\n", + " required_usd = amount / price\n", + " if balance['USD'] < required_usd:\n", + " return {\n", + " 'success': False,\n", + " 'message': f\"❌ Insufficient USD balance. Required: ${required_usd:.2f}, Available: ${balance['USD']:.2f}\",\n", + " 'trade': None\n", + " }\n", + " else: # sell \n", + " if balance[crypto_symbol] < (amount / price):\n", + " return {\n", + " 'success': False,\n", + " 'message': f\"❌ Insufficient {crypto_symbol} balance. Required: {amount}, Available: {balance[crypto_symbol]}\",\n", + " 'trade': None\n", + " }\n", + " \n", + " # Place market order\n", + "\n", + " order = self.exchange.create_market_order(\n", + " symbol=kraken_symbol,\n", + " side=side,\n", + " amount=amount\n", + " )\n", + " return {\n", + " 'success': True,\n", + " 'message': f\"✅ Placed {side} order for {amount} {kraken_symbol}\",\n", + " 'trade': order\n", + " }\n", + " \n", + " except Exception as e:\n", + " return {\n", + " 'success': False,\n", + " 'message': f\"❌ Order failed: {str(e)}\",\n", + " 'trade': None\n", + " }\n", + "\n", + " def get_positions(self):\n", + " \"\"\"Get current positions from real balance\"\"\"\n", + " try:\n", + " balance = self.get_balance()\n", + " positions = []\n", + " \n", + " for crypto, amount in balance.items():\n", + " if crypto != 'USD' and amount > 0:\n", + " price = self._get_crypto_price(crypto)\n", + " positions.append({\n", + " 'symbol': crypto,\n", + " 'amount': amount,\n", + " 'value_usd': amount * price\n", + " })\n", + " \n", + " return positions\n", + " \n", + " except Exception as e:\n", + " print(f\"❌ Error getting positions: {str(e)}\")\n", + " return []\n", + " \n", + " def _get_crypto_price(self, crypto):\n", + " \"\"\"Get current price for crypto\"\"\"\n", + " try:\n", + " symbol = f\"{crypto}/USD\"\n", + " ticker = self.exchange.fetch_ticker(CRYPTO_PAIRS.get(symbol, symbol))\n", + " return ticker['last']\n", + " except:\n", + " return 0\n", + " \n", + " def get_trade_history(self, limit=50):\n", + " \"\"\"Get real trade history from Kraken\"\"\"\n", + " if not self.connected:\n", + " return []\n", + " \n", + " try:\n", + " # Fetch recent trades from Kraken\n", + " trades = self.exchange.fetch_my_trades(limit=limit)\n", + " print(\"Obtianed trades\")\n", + " print(trades)\n", + "\n", + " # Format trades for display\n", + " formatted_trades = []\n", + " for trade in trades:\n", + " formatted_trades.append({\n", + " 'timestamp': datetime.fromtimestamp(trade['timestamp'] / 1000),\n", + " 'symbol': trade['symbol'],\n", + " 'side': trade['side'],\n", + " 'amount': trade['cost'],\n", + " 'price': trade['price'],\n", + " 'order_id': trade.get('order', 'unknown'),\n", + " 'status': 'filled'\n", + " })\n", + " \n", + " return formatted_trades\n", + " \n", + " except Exception as e:\n", + " print(f\"❌ Error fetching trade history: {str(e)}\")\n", + " return self.trade_history[-limit:] if self.trade_history else []\n", + " \n", + " def get_portfolio_value(self):\n", + " \"\"\"Calculate total portfolio value in USD from real balance\"\"\"\n", + " try:\n", + " balance = self.get_balance()\n", + " total_value = balance['USD']\n", + " \n", + " for crypto, amount in balance.items():\n", + " if crypto != 'USD' and amount > 0:\n", + " price = self._get_crypto_price(crypto)\n", + " total_value += amount * price\n", + " \n", + " return total_value\n", + " \n", + " except Exception as e:\n", + " print(f\"❌ Error calculating portfolio value: {str(e)}\")\n", + " return 0\n", + " \n", + " def get_order_status(self, order_id):\n", + " \"\"\"Get status of a specific order\"\"\"\n", + " if not self.connected:\n", + " return None\n", + " \n", + " try:\n", + " order = self.exchange.fetch_order(order_id)\n", + " return order\n", + " except Exception as e:\n", + " print(f\"❌ Error fetching order status: {str(e)}\")\n", + " return None\n", + " \n", + " def cancel_order(self, order_id):\n", + " \"\"\"Cancel a pending order\"\"\"\n", + " if not self.connected:\n", + " return False\n", + " \n", + " try:\n", + " result = self.exchange.cancel_order(order_id)\n", + " return result\n", + " except Exception as e:\n", + " print(f\"❌ Error canceling order: {str(e)}\")\n", + " return False\n", + "\n", + "# Initialize paper trader\n", + "paper_trader = PaperTrader()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d586451a", + "metadata": {}, + "outputs": [], + "source": [ + "# Global state variables\n", + "current_data = pd.DataFrame()\n", + "current_sma_short = pd.Series()\n", + "current_sma_long = pd.Series()\n", + "current_symbol = \"BTC/USD\"\n", + "current_market_summary = {}\n", + "current_recommendation = {}\n", + "\n", + "# Gradio UI Functions\n", + "def fetch_market_data(symbol, sma_short_window, sma_long_window):\n", + " \"\"\"Fetch market data and calculate SMAs\"\"\"\n", + " global current_data, current_sma_short, current_sma_long, current_symbol, current_market_summary\n", + " \n", + " try:\n", + " current_symbol = symbol\n", + " \n", + " # Fetch data\n", + " data = data_fetcher.fetch_ohlcv(symbol, '1h', 720) # 30 days of hourly data\n", + " \n", + " if data.empty:\n", + " return None, \"❌ Failed to fetch market data\", \"No data available\", \"No data available\"\n", + " \n", + " # Calculate SMAs\n", + " sma_short = data_fetcher.calculate_sma(data, sma_short_window)\n", + " sma_long = data_fetcher.calculate_sma(data, sma_long_window)\n", + " \n", + " # Store in global state\n", + " current_data = data\n", + " current_sma_short = sma_short\n", + " current_sma_long = sma_long\n", + " \n", + " # Get market summary\n", + " current_market_summary = data_fetcher.get_market_summary(data, sma_short, sma_long)\n", + " \n", + " # Create chart\n", + " chart = chart_builder.create_candlestick_chart(data, sma_short, sma_long, symbol)\n", + " \n", + " # Prepare status message\n", + " status = f\"✅ Fetched {len(data)} records for {symbol}\"\n", + " status += f\"\\n📊 Current Price: ${current_market_summary.get('current_price', 0):.2f}\"\n", + " status += f\"\\n📈 Price Change: {current_market_summary.get('price_change_pct', 0):.2f}%\"\n", + " status += f\"\\n🔄 Crossover: {current_market_summary.get('crossover_status', 'Unknown')}\"\n", + " \n", + " # Market summary for display\n", + " sma_short = current_market_summary.get('sma_short')\n", + " sma_long = current_market_summary.get('sma_long')\n", + " \n", + " sma_short_str = f\"${sma_short:.2f}\" if sma_short is not None else \"N/A\"\n", + " sma_long_str = f\"${sma_long:.2f}\" if sma_long is not None else \"N/A\"\n", + " \n", + " summary = f\"\"\"\n", + "**Market Summary for {symbol}**\n", + "- Current Price: ${current_market_summary.get('current_price', 0):.2f}\n", + "- Price Change: {current_market_summary.get('price_change_pct', 0):.2f}%\n", + "- 5-Day Momentum: {current_market_summary.get('momentum_5d', 0):.2f}%\n", + "- Volume Ratio: {current_market_summary.get('volume_ratio', 1):.2f}x\n", + "- Short SMA: {sma_short_str}\n", + "- Long SMA: {sma_long_str}\n", + "- Crossover Status: {current_market_summary.get('crossover_status', 'Unknown')}\n", + "\"\"\"\n", + " \n", + " # Crossover status\n", + " crossover = current_market_summary.get('crossover_status', 'Unknown')\n", + " \n", + " return chart, status, summary, crossover\n", + " \n", + " except Exception as e:\n", + " return None, f\"❌ Error: {str(e)}\", \"Error occurred\", \"Error\"\n", + "\n", + "def get_trading_recommendation(model_name, temperature):\n", + " \"\"\"Get LLM trading recommendation\"\"\"\n", + " global current_market_summary, current_recommendation\n", + " \n", + " try:\n", + " if not current_market_summary:\n", + " return \"❌ No market data available. Please fetch data first.\", \"No data\", \"No data\", \"No data\"\n", + " \n", + " # Get recommendation from LLM\n", + " recommendation = trading_advisor.analyze_market(current_market_summary, model_name, temperature)\n", + " current_recommendation = recommendation\n", + " \n", + " # Format recommendation display\n", + " decision_emoji = trading_advisor.get_decision_emoji(recommendation['decision'])\n", + " confidence_color = trading_advisor.get_confidence_color(recommendation['confidence'])\n", + " \n", + " recommendation_text = f\"\"\"\n", + "**{decision_emoji} Trading Recommendation: {recommendation['decision']}**\n", + "\n", + "**Confidence:** {recommendation['confidence']}/10\n", + "**Risk Level:** {recommendation['risk_level']}\n", + "**Reasoning:** {recommendation['reasoning']}\n", + "\n", + "**Raw Analysis:**\n", + "{recommendation.get('raw_response', 'No raw response available')}\n", + "\"\"\"\n", + " \n", + " status = f\"✅ Analysis complete using {model_name}\"\n", + " return status, recommendation_text, recommendation['decision'], recommendation['confidence'], recommendation['reasoning']\n", + " \n", + " except Exception as e:\n", + " return f\"❌ Error getting recommendation: {str(e)}\", \"ERROR\", 0, \"Error occurred\"\n", + "\n", + "def execute_trade(trade_action, trade_amount):\n", + " \"\"\"Execute paper trade\"\"\"\n", + " global current_symbol, current_recommendation\n", + " \n", + " try:\n", + " if not current_recommendation:\n", + " return \"❌ No trading recommendation available. Please get analysis first.\", \"No recommendation\"\n", + " \n", + " if trade_action == \"SKIP\":\n", + " return \"⏭️ Trade skipped as requested\", \"Skipped\"\n", + " \n", + " # Determine trade side\n", + " if trade_action == \"BUY\" and current_recommendation['decision'] == 'BUY':\n", + " side = 'buy'\n", + " elif trade_action == \"SELL\" and current_recommendation['decision'] == 'SELL':\n", + " side = 'sell'\n", + " else:\n", + " return f\"⚠️ Trade action {trade_action} doesn't match recommendation {current_recommendation['decision']}\", \"Mismatch\"\n", + " \n", + " # Execute trade\n", + " result = paper_trader.place_order(current_symbol, side, trade_amount)\n", + " \n", + " print(result)\n", + "\n", + " if result['success']:\n", + " return result['message'], \"Success\"\n", + " else:\n", + " return result['message'], \"Failed\"\n", + " \n", + " except Exception as e:\n", + " return f\"❌ Trade execution error: {str(e)}\", \"Error\"\n", + "\n", + "def get_portfolio_status():\n", + " \"\"\"Get current portfolio status\"\"\"\n", + " try:\n", + " balance = paper_trader.get_balance()\n", + " portfolio_value = paper_trader.get_portfolio_value()\n", + " positions = paper_trader.get_positions()\n", + " trade_history = paper_trader.get_trade_history(10)\n", + " \n", + " # Format balance display\n", + " balance_text = f\"\"\"\n", + "**Portfolio Balance:**\n", + "- USD: ${balance['USD']:.2f}\n", + "- BTC: {balance['BTC']:.6f}\n", + "- ETH: {balance['ETH']:.6f}\n", + "- **Total Value: ${portfolio_value:.2f}**\n", + "\"\"\"\n", + " \n", + " # Format positions\n", + " if positions:\n", + " positions_text = \"**Current Positions:**\\n\"\n", + " for pos in positions:\n", + " positions_text += f\"- {pos['symbol']}: {pos['amount']:.6f} (${pos['value_usd']:.2f})\\n\"\n", + " else:\n", + " positions_text = \"**No current positions**\"\n", + " \n", + " # Format trade history\n", + " if trade_history:\n", + " history_df = pd.DataFrame(trade_history)\n", + " history_df['timestamp'] = pd.to_datetime(history_df['timestamp']).dt.strftime('%Y-%m-%d %H:%M')\n", + " history_df = history_df[['timestamp', 'symbol', 'side', 'amount', 'price', 'status']]\n", + " else:\n", + " history_df = pd.DataFrame(columns=['timestamp', 'symbol', 'side', 'amount', 'price', 'status'])\n", + " \n", + " return balance_text, positions_text, history_df\n", + " \n", + " except Exception as e:\n", + " return f\"❌ Error getting portfolio: {str(e)}\", \"Error\", pd.DataFrame()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3ba1f9a8", + "metadata": {}, + "outputs": [], + "source": [ + "# Create Gradio Interface\n", + "def create_trading_interface():\n", + " \"\"\"Create the main Gradio interface with three tabs\"\"\"\n", + " \n", + " with gr.Blocks(title=\"Crypto Trading System\", theme=gr.themes.Soft()) as interface:\n", + " \n", + " gr.Markdown(\"# 🚀 Crypto Trading System with AI Analysis\")\n", + " gr.Markdown(\"Fetch real-time crypto data, analyze with AI, and execute paper trades using Kraken API\")\n", + " \n", + " # Status bar\n", + " with gr.Row():\n", + " api_status = gr.Textbox(\n", + " label=\"API Status\",\n", + " value=\"✅ All systems ready\" if clients else \"❌ No API keys configured\",\n", + " interactive=False,\n", + " scale=1\n", + " )\n", + " portfolio_status = gr.Textbox(\n", + " label=\"Portfolio Status\",\n", + " value=\"Ready for trading\",\n", + " interactive=False,\n", + " scale=2\n", + " )\n", + " \n", + " # Tab 1: Market Data\n", + " with gr.Tab(\"📊 Market Data\"):\n", + " gr.Markdown(\"### Fetch and analyze crypto market data\")\n", + " \n", + " with gr.Row():\n", + " with gr.Column(scale=2):\n", + " symbol_input = gr.Dropdown(\n", + " choices=list(CRYPTO_PAIRS.keys()),\n", + " value=\"BTC/USD\",\n", + " label=\"Crypto Pair\"\n", + " )\n", + " \n", + " with gr.Row():\n", + " sma_short_input = gr.Number(\n", + " value=20,\n", + " minimum=5,\n", + " maximum=100,\n", + " step=1,\n", + " label=\"Short SMA Window\"\n", + " )\n", + " sma_long_input = gr.Number(\n", + " value=50,\n", + " minimum=10,\n", + " maximum=200,\n", + " step=1,\n", + " label=\"Long SMA Window\"\n", + " )\n", + " \n", + " fetch_btn = gr.Button(\"📊 Fetch Market Data\", variant=\"primary\", size=\"lg\")\n", + " \n", + " with gr.Column(scale=1):\n", + " market_status = gr.Textbox(\n", + " label=\"Fetch Status\",\n", + " lines=4,\n", + " interactive=False\n", + " )\n", + " \n", + " crossover_status = gr.Textbox(\n", + " label=\"Crossover Status\",\n", + " interactive=False\n", + " )\n", + " \n", + " # Chart display\n", + " chart_output = gr.Plot(\n", + " label=\"Price Chart with SMA Analysis\",\n", + " show_label=True\n", + " )\n", + " \n", + " # Market summary\n", + " market_summary = gr.Markdown(\n", + " label=\"Market Summary\",\n", + " value=\"No data loaded\"\n", + " )\n", + " \n", + " # Tab 2: Trading Analysis\n", + " with gr.Tab(\"🤖 AI Trading Analysis\"):\n", + " gr.Markdown(\"### Get AI-powered trading recommendations\")\n", + " \n", + " with gr.Row():\n", + " with gr.Column(scale=2):\n", + " analysis_model = gr.Dropdown(\n", + " choices=list(MODELS.keys()),\n", + " value=list(MODELS.keys())[0],\n", + " label=\"AI Model\"\n", + " )\n", + " \n", + " analysis_temperature = gr.Slider(\n", + " minimum=0.0,\n", + " maximum=1.0,\n", + " value=0.3,\n", + " step=0.1,\n", + " label=\"Temperature (Lower = More Consistent)\"\n", + " )\n", + " \n", + " analyze_btn = gr.Button(\"🤖 Get AI Recommendation\", variant=\"primary\", size=\"lg\")\n", + " \n", + " with gr.Column(scale=1):\n", + " analysis_status = gr.Textbox(\n", + " label=\"Analysis Status\",\n", + " lines=2,\n", + " interactive=False\n", + " )\n", + " \n", + " # Analysis results\n", + " recommendation_display = gr.Markdown(\n", + " label=\"Trading Recommendation\",\n", + " value=\"No analysis performed yet\"\n", + " )\n", + " \n", + " with gr.Row():\n", + " decision_output = gr.Textbox(\n", + " label=\"Decision\",\n", + " interactive=False\n", + " )\n", + " confidence_output = gr.Number(\n", + " label=\"Confidence (1-10)\",\n", + " interactive=False\n", + " )\n", + " reasoning_output = gr.Textbox(\n", + " label=\"Reasoning\",\n", + " lines=3,\n", + " interactive=False\n", + " )\n", + " \n", + " # Tab 3: Paper Trading\n", + " with gr.Tab(\"💰 Paper Trading\"):\n", + " gr.Markdown(\"### Execute paper trades based on AI recommendations\")\n", + " \n", + " with gr.Row():\n", + " with gr.Column(scale=2):\n", + " # Portfolio status\n", + " portfolio_balance = gr.Markdown(\n", + " label=\"Portfolio Balance\",\n", + " value=\"Loading portfolio...\"\n", + " )\n", + " \n", + " portfolio_positions = gr.Markdown(\n", + " label=\"Current Positions\",\n", + " value=\"No positions\"\n", + " )\n", + " \n", + " # Trade execution\n", + " trade_action = gr.Radio(\n", + " choices=[\"BUY\", \"SELL\", \"SKIP\"],\n", + " value=\"SKIP\",\n", + " label=\"Trade Action\"\n", + " )\n", + " \n", + " trade_amount = gr.Number(\n", + " value=5,\n", + " minimum=1.0,\n", + " maximum=100.0,\n", + " step=1,\n", + " label=\"Trade Amount in USD (BTC/ETH)\"\n", + " )\n", + " \n", + " execute_btn = gr.Button(\"💰 Execute Trade\", variant=\"primary\", size=\"lg\")\n", + " \n", + " trade_result = gr.Textbox(\n", + " label=\"Trade Result\",\n", + " lines=3,\n", + " interactive=False\n", + " )\n", + " trade_status = gr.Textbox(\n", + " label=\"Trade Status\",\n", + " interactive=False,\n", + " visible=False \n", + " )\n", + " \n", + " with gr.Column(scale=1):\n", + " # Trade history\n", + " trade_history = gr.Dataframe(\n", + " label=\"Recent Trades\",\n", + " interactive=False,\n", + " wrap=True\n", + " )\n", + " \n", + " refresh_portfolio_btn = gr.Button(\"🔄 Refresh Portfolio\", variant=\"secondary\")\n", + " \n", + " # Event handlers\n", + " fetch_btn.click(\n", + " fetch_market_data,\n", + " inputs=[symbol_input, sma_short_input, sma_long_input],\n", + " outputs=[chart_output, market_status, market_summary, crossover_status]\n", + " )\n", + " \n", + " analyze_btn.click(\n", + " get_trading_recommendation,\n", + " inputs=[analysis_model, analysis_temperature],\n", + " outputs=[analysis_status, recommendation_display, decision_output, confidence_output, reasoning_output]\n", + " )\n", + " \n", + " execute_btn.click(\n", + " execute_trade,\n", + " inputs=[trade_action, trade_amount],\n", + " outputs=[trade_result, trade_status]\n", + " )\n", + " \n", + " refresh_portfolio_btn.click(\n", + " get_portfolio_status,\n", + " outputs=[portfolio_balance, portfolio_positions, trade_history]\n", + " )\n", + " \n", + " # Auto-refresh portfolio on load\n", + " interface.load(\n", + " get_portfolio_status,\n", + " outputs=[portfolio_balance, portfolio_positions, trade_history]\n", + " )\n", + " \n", + " return interface\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1b040594", + "metadata": {}, + "outputs": [], + "source": [ + "# Launch the Trading System\n", + "interface = create_trading_interface()\n", + "\n", + "interface.launch(\n", + " server_name=\"0.0.0.0\",\n", + " server_port=7860\n", + ")\n" + ] + }, + { + "cell_type": "markdown", + "id": "580a4048", + "metadata": {}, + "source": [ + "## Testing and Validation\n", + "\n", + "### Quick Test Functions\n", + "\n", + "Run these cells to test individual components:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2ea5bfeb", + "metadata": {}, + "outputs": [], + "source": [ + "# Test 1: Data Fetching\n", + "print(\"🧪 Testing Data Fetching...\")\n", + "test_data = data_fetcher.fetch_ohlcv(\"BTC/USD\", \"1h\", 24) # Last 24 hours\n", + "if not test_data.empty:\n", + " print(f\"✅ Data fetch successful: {len(test_data)} records\")\n", + " print(f\" Latest price: ${test_data['close'].iloc[-1]:.2f}\")\n", + " \n", + " # Test SMA calculation\n", + " sma_20 = data_fetcher.calculate_sma(test_data, 20)\n", + " sma_50 = data_fetcher.calculate_sma(test_data, 50)\n", + " print(f\"✅ SMA calculation successful\")\n", + " print(f\" SMA 20: ${sma_20.iloc[-1]:.2f}\" if not sma_20.empty else \" SMA 20: N/A\")\n", + " print(f\" SMA 50: ${sma_50.iloc[-1]:.2f}\" if not sma_50.empty else \" SMA 50: N/A\")\n", + " \n", + " # Test crossover detection\n", + " crossover = data_fetcher.detect_crossover(sma_20, sma_50)\n", + " print(f\"✅ Crossover detection: {crossover}\")\n", + "else:\n", + " print(\"❌ Data fetch failed\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb983b26", + "metadata": {}, + "outputs": [], + "source": [ + "# Test 2: Chart Generation\n", + "print(\"\\n🧪 Testing Chart Generation...\")\n", + "if not test_data.empty:\n", + " try:\n", + " test_chart = chart_builder.create_candlestick_chart(test_data, sma_20, sma_50, \"BTC/USD\")\n", + " print(\"✅ Chart generation successful\")\n", + " print(f\" Chart type: {type(test_chart)}\")\n", + " print(f\" Data points: {len(test_data)}\")\n", + " except Exception as e:\n", + " print(f\"❌ Chart generation failed: {str(e)}\")\n", + "else:\n", + " print(\"❌ No data available for chart testing\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e58aab73", + "metadata": {}, + "outputs": [], + "source": [ + "# Test 3: LLM Trading Analysis\n", + "print(\"\\n🧪 Testing LLM Trading Analysis...\")\n", + "if not test_data.empty and clients:\n", + " try:\n", + " # Get market summary\n", + " market_summary = data_fetcher.get_market_summary(test_data, sma_20, sma_50)\n", + " print(f\"✅ Market summary generated: {len(market_summary)} fields\")\n", + " \n", + " # Test LLM analysis (use first available model)\n", + " available_models = [name for name, info in MODELS.items() if info['provider'] in clients]\n", + " if available_models:\n", + " model_name = available_models[0]\n", + " print(f\" Testing with model: {model_name}\")\n", + " \n", + " recommendation = trading_advisor.analyze_market(market_summary, model_name, 0.3)\n", + " print(f\"✅ LLM analysis successful\")\n", + " print(f\" Decision: {recommendation['decision']}\")\n", + " print(f\" Confidence: {recommendation['confidence']}/10\")\n", + " print(f\" Risk Level: {recommendation['risk_level']}\")\n", + " else:\n", + " print(\"❌ No LLM models available\")\n", + " except Exception as e:\n", + " print(f\"❌ LLM analysis failed: {str(e)}\")\n", + "else:\n", + " print(\"❌ No data or LLM clients available for testing\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c87193", + "metadata": {}, + "outputs": [], + "source": [ + "# Test 4: Real Kraken Sandbox Trading\n", + "print(\"\\n🧪 Testing Real Kraken Sandbox Trading...\")\n", + "try:\n", + " # Test portfolio status\n", + " balance = paper_trader.get_balance()\n", + " portfolio_value = paper_trader.get_portfolio_value()\n", + " print(f\"✅ Portfolio status retrieved from Kraken sandbox\")\n", + " print(f\" USD Balance: ${balance['USD']:.2f}\")\n", + " print(f\" BTC Balance: {balance['BTC']:.6f}\")\n", + " print(f\" ETH Balance: {balance['ETH']:.6f}\")\n", + " print(f\" Total Value: ${portfolio_value:.2f}\")\n", + " \n", + " # Test real trade (very small amount to avoid issues)\n", + " if balance['USD'] > 10: # Only test if we have some USD balance\n", + " test_result = paper_trader.place_order(\"BTC/USD\", \"sell\", 5)\n", + " if test_result['success']:\n", + " print(f\"✅ Real trade successful: {test_result['message']}\")\n", + " \n", + " # Check updated balance\n", + " new_balance = paper_trader.get_balance()\n", + " print(f\" Updated USD: ${new_balance['USD']:.2f}\")\n", + " print(f\" Updated BTC: {new_balance['BTC']:.6f}\")\n", + " else:\n", + " print(f\"❌ Real trade failed: {test_result['message']}\")\n", + " else:\n", + " print(\"⚠️ Insufficient USD balance for testing real trades\")\n", + " print(\" Note: Kraken sandbox may require initial funding\")\n", + " \n", + " # Test trade history\n", + " trade_history = paper_trader.get_trade_history(5)\n", + " print(f\"✅ Trade history retrieved: {len(trade_history)} recent trades\")\n", + " \n", + " # Test positions\n", + " positions = paper_trader.get_positions()\n", + " print(f\"✅ Current positions: {len(positions)} active positions\")\n", + " for pos in positions:\n", + " print(f\" {pos['symbol']}: {pos['amount']:.6f} (${pos['value_usd']:.2f})\")\n", + " \n", + "except Exception as e:\n", + " print(f\"❌ Real trading test failed: {str(e)}\")\n", + "\n", + "print(\"\\n🎉 All tests completed!\")\n", + "print(\"=\" * 50)\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}