arbitrage-engine/V52_TASK.md
root ee90b8dcfa feat: sidebar navigation with V5.1/V5.2 separate entries
- Sidebar: 信号/模拟盘 section headers
- Three paper trade entries: 全部持仓, V5.1模拟盘, V5.2模拟盘 (NEW badge)
- Paper page reads strategy from URL query params
- Suspense boundary for useSearchParams
2026-03-01 12:25:40 +00:00

225 lines
7.4 KiB
Markdown

# V5.2 Development Task
## Context
You are working on the `dev` branch of the ArbitrageEngine project.
This is a quantitative trading signal system with:
- Backend: Python (FastAPI + PostgreSQL)
- Frontend: Next.js + shadcn/ui + Tailwind
## Database Connection
- Host: 34.85.117.248 (Cloud SQL)
- Port: 5432, DB: arb_engine, User: arb, Password: arb_engine_2026
## What to Build (V5.2)
### 1. Strategy Configuration Framework
Create `backend/strategies/` directory with JSON configs:
**backend/strategies/v51_baseline.json:**
```json
{
"name": "v51_baseline",
"version": "5.1",
"threshold": 75,
"weights": {
"direction": 45,
"crowding": 20,
"environment": 15,
"confirmation": 15,
"auxiliary": 5
},
"accel_bonus": 5,
"tp_sl": {
"sl_multiplier": 2.0,
"tp1_multiplier": 1.5,
"tp2_multiplier": 3.0
},
"signals": ["cvd", "p99", "accel", "ls_ratio", "oi", "coinbase_premium"]
}
```
**backend/strategies/v52_8signals.json:**
```json
{
"name": "v52_8signals",
"version": "5.2",
"threshold": 75,
"weights": {
"direction": 40,
"crowding": 25,
"environment": 15,
"confirmation": 20,
"auxiliary": 5
},
"accel_bonus": 5,
"tp_sl": {
"sl_multiplier": 2.0,
"tp1_multiplier": 1.5,
"tp2_multiplier": 3.0
},
"signals": ["cvd", "p99", "accel", "ls_ratio", "oi", "coinbase_premium", "funding_rate", "liquidation"]
}
```
### 2. Signal Engine Changes (signal_engine.py)
#### 2a. Add FR scoring to evaluate_signal()
After the crowding section, add funding_rate scoring:
```python
# Funding Rate scoring (拥挤层加分)
# Read from market_indicators table
funding_rate = to_float(self.market_indicators.get("funding_rate"))
fr_score = 0
if funding_rate is not None:
fr_abs = abs(funding_rate)
if fr_abs >= 0.001: # extreme ±0.1%
# Extreme: penalize if going WITH the crowd
if (direction == "LONG" and funding_rate > 0.001) or \
(direction == "SHORT" and funding_rate < -0.001):
fr_score = -5
else:
fr_score = 5
elif fr_abs >= 0.0003: # moderate ±0.03%
# Moderate: reward going AGAINST the crowd
if (direction == "LONG" and funding_rate < -0.0003) or \
(direction == "SHORT" and funding_rate > 0.0003):
fr_score = 5
else:
fr_score = 0
```
#### 2b. Add liquidation scoring
```python
# Liquidation scoring (确认层加分)
liq_score = 0
liq_data = self.fetch_recent_liquidations() # new method
if liq_data:
liq_long_usd = liq_data.get("long_usd", 0)
liq_short_usd = liq_data.get("short_usd", 0)
# Thresholds by symbol
thresholds = {"BTCUSDT": 500000, "ETHUSDT": 200000, "XRPUSDT": 100000, "SOLUSDT": 100000}
threshold = thresholds.get(self.symbol, 100000)
total = liq_long_usd + liq_short_usd
if total >= threshold:
if liq_short_usd > 0 and liq_long_usd > 0:
ratio = liq_short_usd / liq_long_usd
elif liq_short_usd > 0:
ratio = float('inf')
else:
ratio = 0
if ratio >= 2.0 and direction == "LONG":
liq_score = 5 # shorts getting liquidated, price going up
elif ratio <= 0.5 and direction == "SHORT":
liq_score = 5 # longs getting liquidated, price going down
```
#### 2c. Add fetch_recent_liquidations method to SymbolState
```python
def fetch_recent_liquidations(self, window_ms=300000):
"""Fetch last 5min liquidation totals from liquidations table"""
now_ms = int(time.time() * 1000)
cutoff = now_ms - window_ms
with get_sync_conn() as conn:
with conn.cursor() as cur:
cur.execute("""
SELECT
COALESCE(SUM(CASE WHEN side='SELL' THEN usd_value ELSE 0 END), 0) as long_liq,
COALESCE(SUM(CASE WHEN side='BUY' THEN usd_value ELSE 0 END), 0) as short_liq
FROM liquidations
WHERE symbol=%s AND trade_time >= %s
""", (self.symbol, cutoff))
row = cur.fetchone()
if row:
return {"long_usd": row[0], "short_usd": row[1]}
return None
```
#### 2d. Add funding_rate to fetch_market_indicators
Add "funding_rate" to the indicator types:
```python
for ind_type in ["long_short_ratio", "top_trader_position", "open_interest_hist", "coinbase_premium", "funding_rate"]:
```
And the extraction:
```python
elif ind_type == "funding_rate":
indicators[ind_type] = float(val.get("lastFundingRate", 0))
```
#### 2e. Update total_score calculation
Currently:
```python
total_score = direction_score + accel_bonus + crowding_score + environment_score + confirmation_score + aux_score
```
Change to:
```python
total_score = direction_score + accel_bonus + crowding_score + fr_score + environment_score + confirmation_score + liq_score + aux_score
```
#### 2f. Update factors dict
Add fr_score and liq_score to the factors:
```python
result["factors"] = {
...existing factors...,
"funding_rate": {"score": fr_score, "value": funding_rate},
"liquidation": {"score": liq_score, "long_usd": liq_data.get("long_usd", 0) if liq_data else 0, "short_usd": liq_data.get("short_usd", 0) if liq_data else 0},
}
```
#### 2g. Change threshold from 60 to 75
In evaluate_signal, change:
```python
# OLD
elif total_score >= 60 and not no_direction and not in_cooldown:
result["signal"] = direction
result["tier"] = "light"
# NEW: remove the 60 tier entirely, minimum is 75
```
Also update reverse signal threshold from 60 to 75:
In main() loop:
```python
# OLD
if existing_dir and eval_dir and existing_dir != eval_dir and result["score"] >= 60:
# NEW
if existing_dir and eval_dir and existing_dir != eval_dir and result["score"] >= 75:
```
### 3. Strategy field in paper_trades
Add SQL migration at top of init_schema() or in a migration:
```sql
ALTER TABLE paper_trades ADD COLUMN IF NOT EXISTS strategy VARCHAR(32) DEFAULT 'v51_baseline';
```
### 4. AB Test: Both strategies evaluate each cycle
In the main loop, evaluate signal twice (once per strategy config) and potentially open trades for both. Each trade records which strategy triggered it.
### 5. Frontend: Update paper/page.tsx
- Show strategy column in trade history table
- Show FR and liquidation scores in signal details
- Add strategy filter/tab (v51 vs v52)
### 6. API: Add strategy stats endpoint
In main.py, add `/api/paper/stats-by-strategy` that groups stats by strategy field.
## Important Notes
- Keep ALL existing functionality working
- Don't break the existing V5.1 scoring - it should still work as strategy "v51_baseline"
- The FR data is already in market_indicators table (collected every 5min)
- The liquidation data is already in liquidations table
- Test with: `cd frontend && npm run build` to verify no frontend errors
- Test backend: `python3 -c "from signal_engine import *; print('OK')"` to verify imports
- Port for dev testing: API=8100, Frontend=3300
- Total score CAN exceed 100 (that's by design)
## Files to modify:
1. `backend/signal_engine.py` - core scoring changes
2. `backend/main.py` - new API endpoints
3. `backend/db.py` - add strategy column migration
4. `frontend/app/paper/page.tsx` - UI updates
5. NEW: `backend/strategies/v51_baseline.json`
6. NEW: `backend/strategies/v52_8signals.json`
When completely finished, run this command to notify me:
openclaw system event --text "Done: V5.2 core implementation complete - FR+liquidation scoring, threshold 75, strategy configs, AB test framework" --mode now