feat: AI 自然语言选股(GLM)+ 全市场数据管道 + 远程 PostgreSQL
- 首页双入口(智能选股/策略回测):引入 vue-router,顶部导航 - 智能选股:自然语言 -> LLM 解析结构化条件(智谱 GLM,OpenAI 兼容,/v4 兼容)-> SQL 快照预筛 + pandas 指标过滤(复用 indicators 单一事实源) - 条件模型:指标 vs 常数/指标(value_indicator,如 DIF>DEA、close<布林下轨)、lookback+match 表达连续N天/近N天任一天、市值/PE/PB/换手率快照条件、默认排除 ST/退市/北交所 - 全市场数据同步:按 trade_date 批量拉取未复权日线(与回测 candles qfq 隔离),交易日历/股票列表本地缓存,daily_basic 仅最新截面,Tushare 限频兜底(分钟级重试/小时级降级) - 存储:DATABASE_URL 切远程 PostgreSQL(cirry.cn/stock),本地 SQLite 已移除 - .env 入库(私有仓库);smoke_test 扩展选股链路 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,13 @@
|
||||
import type { BacktestRequest, BacktestResponse, SyncRequest, SyncResponse } from './types';
|
||||
import type {
|
||||
BacktestRequest,
|
||||
BacktestResponse,
|
||||
ScreenerRunRequest,
|
||||
ScreenerRunResponse,
|
||||
ScreenerSyncRequest,
|
||||
ScreenerSyncStatus,
|
||||
SyncRequest,
|
||||
SyncResponse,
|
||||
} from './types';
|
||||
|
||||
// dev 用 Vite 代理(/api -> :8000);生产构建设 VITE_API_BASE 指向后端地址。
|
||||
const BASE = import.meta.env.VITE_API_BASE ?? '';
|
||||
@@ -27,3 +36,47 @@ export async function syncData(req: SyncRequest): Promise<SyncResponse> {
|
||||
return (await res.json()) as SyncResponse;
|
||||
}
|
||||
|
||||
// ---------- 智能选股 ----------
|
||||
export async function runScreener(req: ScreenerRunRequest): Promise<ScreenerRunResponse> {
|
||||
const res = await fetch(`${BASE}/api/screener/run`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(req),
|
||||
});
|
||||
if (!res.ok) {
|
||||
// 后端 detail 字段携带可读中文原因(503 未配 key / 409 未同步 / 502 LLM 错)
|
||||
let detail = '';
|
||||
try {
|
||||
detail = (await res.json())?.detail ?? '';
|
||||
} catch {
|
||||
detail = await res.text();
|
||||
}
|
||||
throw new Error(detail || `选股失败 (HTTP ${res.status})`);
|
||||
}
|
||||
return (await res.json()) as ScreenerRunResponse;
|
||||
}
|
||||
|
||||
export async function startScreenerSync(req: ScreenerSyncRequest = {}): Promise<ScreenerSyncStatus> {
|
||||
const res = await fetch(`${BASE}/api/screener/sync`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(req),
|
||||
});
|
||||
if (!res.ok) {
|
||||
let detail = '';
|
||||
try {
|
||||
detail = (await res.json())?.detail ?? '';
|
||||
} catch {
|
||||
detail = await res.text();
|
||||
}
|
||||
throw new Error(detail || `启动同步失败 (HTTP ${res.status})`);
|
||||
}
|
||||
return (await res.json()) as ScreenerSyncStatus;
|
||||
}
|
||||
|
||||
export async function getScreenerSyncStatus(): Promise<ScreenerSyncStatus> {
|
||||
const res = await fetch(`${BASE}/api/screener/sync/status`);
|
||||
if (!res.ok) throw new Error(`获取同步状态失败 (HTTP ${res.status})`);
|
||||
return (await res.json()) as ScreenerSyncStatus;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user