alphax/tests/test_market_context_enrichment.py
2026-05-13 22:32:50 +08:00

209 lines
8.4 KiB
Python

import os
import sqlite3
import sys
import pytest
from fastapi.testclient import TestClient
PROJECT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
if PROJECT_DIR not in sys.path:
sys.path.insert(0, PROJECT_DIR)
import altcoin_db
import web_server
@pytest.fixture()
def temp_db(tmp_path, monkeypatch):
db_path = tmp_path / "altcoin_monitor.db"
monkeypatch.setattr(altcoin_db, "DB_PATH", str(db_path))
altcoin_db.init_db()
yield db_path
def _require_columns(conn, columns):
rows = conn.execute("PRAGMA table_info(recommendation)").fetchall()
existing = {row[1] for row in rows}
for column in columns:
assert column in existing, f"missing column: {column}"
def _insert_recommendation(conn, **overrides):
row = {
"symbol": "TEST/USDT",
"rec_time": "2026-04-30T10:00:00",
"rec_state": "加速",
"rec_score": 15.5,
"entry_price": 1.25,
"stop_loss": 1.1,
"tp1": 1.4,
"tp2": 1.55,
"sector": "AI",
"signals": "[]",
"is_meme": 0,
"status": "active",
"current_price": 1.32,
"max_price": 1.36,
"max_drawdown_pct": -2.5,
"max_pnl_pct": 5.2,
"pnl_pct": 3.1,
"last_track_time": "2026-04-30T11:00:00",
"action_status": "可即刻买入",
"entry_plan_json": '{"entry_action": "可即刻买入", "entry_price": 1.25}',
"direction": "多头启动",
"force_reason": "",
"base_state": "加速",
"sector_signal_count": 0,
"strategy_version": "v11.1",
"market_context_json": "{}",
"derivatives_context_json": "{}",
"sector_context_json": "{}",
}
row.update(overrides)
conn.execute(
"""
INSERT INTO recommendation (
symbol, rec_time, rec_state, rec_score, entry_price,
stop_loss, tp1, tp2, sector, signals, is_meme,
status, current_price, max_price, max_drawdown_pct,
max_pnl_pct, pnl_pct, last_track_time, action_status,
entry_plan_json, direction, force_reason, base_state,
sector_signal_count, strategy_version,
market_context_json, derivatives_context_json, sector_context_json
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
row["symbol"], row["rec_time"], row["rec_state"], row["rec_score"], row["entry_price"],
row["stop_loss"], row["tp1"], row["tp2"], row["sector"], row["signals"], row["is_meme"],
row["status"], row["current_price"], row["max_price"], row["max_drawdown_pct"],
row["max_pnl_pct"], row["pnl_pct"], row["last_track_time"], row["action_status"],
row["entry_plan_json"], row["direction"], row["force_reason"], row["base_state"],
row["sector_signal_count"], row["strategy_version"],
row["market_context_json"], row["derivatives_context_json"], row["sector_context_json"],
),
)
def test_active_recommendations_expose_enriched_context(temp_db):
conn = altcoin_db.get_conn()
_require_columns(conn, [
"market_context_json", "derivatives_context_json", "sector_context_json",
])
_insert_recommendation(
conn,
symbol="PNT/USDT",
force_reason="静K蓄力旁路",
base_state="蓄力",
sector_signal_count=2,
market_context_json='{"volume_24h": 12000000, "turnover_acceleration_1h": 2.8, "turnover_acceleration_4h": 1.6, "change_24h": 8.2}',
derivatives_context_json='{"funding_rate": 0.0008, "top_trader_long_pct": 62.5, "top_trader_long_short_ratio": 1.7}',
sector_context_json='{"sectors": ["AI", "Infra"], "hot_sectors": ["AI"], "leader_symbol": "WLD/USDT", "leader_move_pct": 9.6}',
)
conn.commit()
conn.close()
rows = altcoin_db.get_active_recommendations_deduped(actionable_only=False)
assert len(rows) == 1
item = rows[0]
assert item["force_reason"] == "静K蓄力旁路"
assert item["base_state"] == "蓄力"
assert item["sector_signal_count"] == 2
assert item["market_context"]["turnover_acceleration_1h"] == 2.8
assert item["market_context"]["change_24h"] == 8.2
assert item["derivatives_context"]["top_trader_long_pct"] == 62.5
assert item["sector_context"]["leader_symbol"] == "WLD/USDT"
assert item["sector_context"]["hot_sectors"] == ["AI"]
def test_stats_exposes_market_context_summary(temp_db):
conn = altcoin_db.get_conn()
_require_columns(conn, [
"market_context_json", "derivatives_context_json", "sector_context_json",
])
_insert_recommendation(
conn,
symbol="PNT/USDT",
force_reason="静K蓄力旁路",
base_state="蓄力",
sector_signal_count=1,
market_context_json='{"volume_24h": 12000000, "turnover_acceleration_1h": 2.8, "turnover_acceleration_4h": 1.6}',
derivatives_context_json='{"funding_rate": 0.0008, "top_trader_long_pct": 62.5, "top_trader_long_short_ratio": 1.7}',
sector_context_json='{"sectors": ["AI"], "hot_sectors": ["AI"], "leader_symbol": "WLD/USDT", "leader_move_pct": 9.6}',
)
_insert_recommendation(
conn,
symbol="AI/USDT",
sector="AI,Gaming",
force_reason="纯板块联动降级",
base_state="加速",
sector_signal_count=2,
market_context_json='{"volume_24h": 20000000, "turnover_acceleration_1h": 3.2, "turnover_acceleration_4h": 2.1}',
derivatives_context_json='{"funding_rate": 0.0012, "top_trader_long_pct": 66.0, "top_trader_long_short_ratio": 1.9}',
sector_context_json='{"sectors": ["AI", "Gaming"], "hot_sectors": ["AI", "Gaming"], "leader_symbol": "TAO/USDT", "leader_move_pct": 12.4}',
)
conn.commit()
conn.close()
stats = altcoin_db.get_stats()
market = stats["market_context_overview"]
assert market["actionable_sample_count"] == 2
assert market["avg_turnover_acceleration_1h"] == 3.0
assert market["avg_funding_rate"] == 0.001
assert market["avg_top_trader_long_pct"] == 64.2
assert market["top_hot_sectors"][0] == {"sector": "AI", "count": 2}
assert market["top_hot_sectors"][1] == {"sector": "Gaming", "count": 1}
def test_stats_api_returns_market_context_overview(temp_db):
conn = altcoin_db.get_conn()
_require_columns(conn, ["market_context_json", "derivatives_context_json", "sector_context_json"])
_insert_recommendation(
conn,
symbol="PNT/USDT",
force_reason="静K蓄力旁路",
base_state="蓄力",
sector_signal_count=2,
market_context_json='{"volume_24h": 12000000, "turnover_acceleration_1h": 2.8, "turnover_acceleration_4h": 1.6}',
derivatives_context_json='{"funding_rate": 0.0008, "top_trader_long_pct": 62.5, "top_trader_long_short_ratio": 1.7}',
sector_context_json='{"sectors": ["AI"], "hot_sectors": ["AI"], "leader_symbol": "WLD/USDT", "leader_move_pct": 9.6}',
)
conn.commit()
conn.close()
client = TestClient(web_server.app)
resp = client.get("/api/stats")
assert resp.status_code == 200
data = resp.json()
assert "market_context_overview" in data
overview = data["market_context_overview"]
assert overview["actionable_sample_count"] == 1
assert overview["top_hot_sectors"][0]["sector"] == "AI"
def test_recommendations_api_and_page_expose_context_fields(temp_db):
conn = altcoin_db.get_conn()
_insert_recommendation(
conn,
symbol="PNT/USDT",
force_reason="静K蓄力旁路",
base_state="蓄力",
sector_signal_count=2,
market_context_json='{"volume_24h": 12000000, "turnover_acceleration_1h": 2.8, "turnover_acceleration_4h": 1.6, "change_24h": 8.2}',
derivatives_context_json='{"funding_rate": 0.0008, "open_interest_change_24h": 14.5, "top_trader_long_pct": 62.5, "top_trader_long_short_ratio": 1.7}',
sector_context_json='{"sectors": ["AI", "Infra"], "hot_sectors": ["AI"], "leader_symbol": "WLD/USDT", "leader_move_pct": 9.6}',
)
conn.commit()
conn.close()
client = TestClient(web_server.app)
api_resp = client.get("/api/recommendations/active?actionable_only=false")
assert api_resp.status_code == 200
rows = api_resp.json()
assert rows[0]["market_context"]["turnover_acceleration_1h"] == 2.8
assert rows[0]["derivatives_context"]["open_interest_change_24h"] == 14.5
assert rows[0]["sector_context"]["leader_symbol"] == "WLD/USDT"