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`