File size: 7,954 Bytes
fc115d5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | # Architecture Migration Plan: Local Server + HF Client
## Date: 2026-03-19
---
## Current Architecture (Problem)
```
HuggingFace Space
βββ live_trading_multi.py (bot, all logic)
βββ src/ui/api_server.py (Flask REST API, port 5001)
βββ src/ui/app.py (Streamlit UI, port 8501)
βββ calls http://127.0.0.1:5001/api/*
```
**Problems:**
- Binance Testnet geo-blocked from HuggingFace's datacenter (HTTP 451)
- Cloudflare Workers proxy is unreliable and adds latency
- Bot cannot reach testnet.binance.vision from HF
---
## Target Architecture (Solution)
```
Local Machine
βββ live_trading_multi.py (bot, trading logic)
βββ src/ui/api_server.py (Flask REST API, port 5001)
βββ [tunnel tool] (exposes :5001 as public HTTPS URL)
βββ e.g. https://abc123.ngrok.io β localhost:5001
HuggingFace Space
βββ src/ui/app.py (Streamlit UI, CLIENT only)
βββ calls $API_SERVER_URL/api/*
βββ API_SERVER_URL = https://abc123.ngrok.io (HF Secret)
```
**Benefits:**
- Bot runs locally β direct Binance testnet access (no geo-blocks)
- HF Space becomes a pure UI/client, no trading logic
- Testnet tab works via server-side `testnet_server.py` (direct connection locally)
---
## Implementation Plan
### Step 1: Configure Flask API Server for Remote Access
**File: `src/ui/api_server.py`**
Changes:
1. Enable CORS properly so HuggingFace domain can call the API
2. Add a `/api/ping` health endpoint (lightweight check for client)
3. Add startup banner showing the public URL
```python
from flask_cors import CORS
CORS(app, origins=["*"]) # Allow HF and any origin
```
**File: `start_local_server.sh`** (new)
A convenience script to:
1. Start the Flask API server (port 5001)
2. Optionally start a tunnel (ngrok or Cloudflare)
3. Print the public URL to copy into HF Secrets
---
### Step 2: Make Streamlit UI Configurable
**File: `src/ui/app.py`**
Changes:
1. Replace all hardcoded `http://127.0.0.1:5001` with `get_api_url()` helper
2. `get_api_url()` reads from `os.environ.get('API_SERVER_URL', 'http://127.0.0.1:5001')`
3. Show connection status in UI (connected/disconnected + server URL)
4. Graceful degradation when server is unreachable (not just red error)
**Occurrences of hardcoded URL to replace:**
- Line 1183: `requests.get('http://127.0.0.1:5001/api/state', ...)`
- Line 1221: `requests.get(f'http://127.0.0.1:5001/api/market?symbol=...', ...)`
- Line 1427: `requests.get('http://127.0.0.1:5001/api/state', ...)`
- Line 1438: `requests.get(f'http://127.0.0.1:5001/api/market?symbol=...', ...)`
- Line 1459: `requests.get('http://127.0.0.1:5001/api/trades', ...)`
- Line 1539: `requests.get('http://127.0.0.1:5001/api/state', ...)`
- Line 1553: `requests.get('http://127.0.0.1:5001/api/trades', ...)`
---
### Step 3: Fix Testnet Tab
**File: `src/ui/testnet_server.py`**
Changes:
1. Remove the HuggingFace detection/warning block (it no longer applies)
2. Remove Cloudflare proxy fallback (not needed locally)
3. Simplify to direct `BinanceConnector` with `testnet=True`
4. Server now calls `https://testnet.binance.vision` directly
**File: `src/ui/app.py` (testnet tab section)**
Changes:
1. Always use server-side (`testnet_server.py`) since server runs locally
2. Remove the client-side JS fallback (optional, can keep as backup)
---
### Step 4: Add Tunnel Support (ngrok)
**File: `requirements.txt`**
Add: `pyngrok>=7.0.0`
**File: `start_local_server.py`** (new script)
```python
# Start Flask server with optional ngrok tunnel
# Usage:
# python start_local_server.py # local only
# python start_local_server.py --tunnel # with ngrok tunnel
```
Logic:
1. Start `api_server.py` in a background thread
2. If `--tunnel`: start pyngrok tunnel, print public URL
3. Print instructions to add URL to HF Secrets
---
### Step 5: Integration Tests
**File: `tests/test_integration/test_server_client.py`** (new)
Tests:
1. `test_server_starts()` - Flask server starts, `/health` returns 200
2. `test_api_state()` - `/api/state` returns valid JSON
3. `test_api_trades()` - `/api/trades` returns list
4. `test_api_market()` - `/api/market?symbol=BTCUSDT` returns market data
5. `test_cors_headers()` - Response includes CORS headers
6. `test_binance_testnet_connectivity()` - testnet.binance.vision ping returns 200
7. `test_binance_testnet_with_keys()` - API keys work for account balance
8. `test_client_url_configurable()` - `API_SERVER_URL` env var is read correctly
9. `test_end_to_end_data_flow()` - Bot saves state β API serves it β client reads it
---
### Step 6: Update HuggingFace Space
**File: `src/ui/app.py`** (already updated in Step 2)
**HF Space Secrets to add:**
- `API_SERVER_URL` = `<ngrok-or-tunnel-url>`
- Keep existing: `BINANCE_TESTNET_API_KEY`, `BINANCE_TESTNET_API_SECRET`, etc.
**File: `README.md` or HF Space README** - update deployment instructions
**Push target:** `Chen4700/drl-trading-bot-dev` (DEV space only)
---
## File Changes Summary
| File | Action | Description |
|------|--------|-------------|
| `src/ui/api_server.py` | Modify | Enable CORS, add `/api/ping` |
| `src/ui/app.py` | Modify | Replace hardcoded localhost:5001 with `API_SERVER_URL` env var |
| `src/ui/testnet_server.py` | Modify | Remove proxy/HF-specific code, direct testnet access |
| `start_local_server.py` | Create | Convenience script to start server + optional tunnel |
| `tests/test_integration/test_server_client.py` | Create | Integration tests |
| `tests/test_integration/__init__.py` | Create | Package init |
| `requirements.txt` | Modify | Add `pyngrok>=7.0.0` |
| `.env.example` | Modify | Add `API_SERVER_URL` example |
---
## Blockers & Solutions
### Blocker 1: HuggingFace Space Needs Public URL
**Problem:** The local Flask server on :5001 is not reachable from HF by default.
**Solution:** Use ngrok (pyngrok) or Cloudflare Tunnel (`cloudflared`) to expose it.
- ngrok: requires account but easy setup, free tier works
- Cloudflare Tunnel: free, persistent, but requires `cloudflared` binary
- **Chosen:** ngrok via `pyngrok` (Python package, easiest to automate)
### Blocker 2: CORS on Flask
**Problem:** HuggingFace serves the Streamlit app from a different origin.
**Solution:** Add `flask-cors` (already in requirements) and configure `CORS(app, origins=["*"])`.
### Blocker 3: Testnet Keys in HF Secrets vs .env
**Problem:** The testnet tab reads keys from `os.getenv()` β works both locally (.env) and on HF (secrets).
**Solution:** No change needed. Keep reading from env vars.
### Blocker 4: Streamlit on HF can't do ngrok
**Problem:** Streamlit on HF is the CLIENT, not the server β it doesn't need ngrok.
**Solution:** ngrok only runs on the LOCAL server side.
---
## Execution Order
1. [x] Write PLAN.md (this file)
2. [x] Modify `src/ui/api_server.py` β Enable CORS + add /api/ping
3. [x] Modify `src/ui/app.py` β Replace hardcoded URLs with `API_SERVER_URL`
4. [x] Modify `src/ui/testnet_server.py` β Remove proxy/HF blocks (direct testnet)
5. [x] Create `start_local_server.py` β Server startup script with optional ngrok
6. [x] Add `pyngrok` to `requirements.txt`
7. [x] Add `API_SERVER_URL` to `.env.example`
8. [x] Create integration tests (`tests/test_integration/test_server_client.py`)
9. [~] Run tests locally β BLOCKED: this machine has no project venv (no flask/dotenv/etc.)
- Static checks passed: all 5 modified files parse cleanly (ast.parse)
- Logic check passed: `get_api_url()` default/custom/trailing-slash behavior verified
- Storage check passed: JsonFileStorage save/load verified (when deps available)
- Binance testnet check passed: testnet.binance.vision/api/v3/ping reachable locally
- Full pytest suite must be run in the project's virtualenv:
`pip install -r requirements.txt && pytest tests/test_integration/ -v`
10. [x] Push to `Chen4700/drl-trading-bot-dev`
|