提交
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
import type {
|
||||
AdjustMode,
|
||||
BacktestRequest,
|
||||
BacktestResponse,
|
||||
Candle,
|
||||
CurrentUser,
|
||||
EventBacktestRequest,
|
||||
@@ -11,6 +9,9 @@ import type {
|
||||
GlobalIndexList,
|
||||
IndexDetail,
|
||||
IndexWeights,
|
||||
LimitBoard,
|
||||
ThsBoardList,
|
||||
ThsBoardMembers,
|
||||
LoginRequest,
|
||||
LoginResponse,
|
||||
MarketOverview,
|
||||
@@ -27,8 +28,6 @@ import type {
|
||||
StockFinanceOut,
|
||||
StockReferenceOut,
|
||||
StockListResponse,
|
||||
SyncRequest,
|
||||
SyncResponse,
|
||||
Timeframe,
|
||||
TradesClearResponse,
|
||||
TradesImportResponse,
|
||||
@@ -96,18 +95,6 @@ export async function logout(): Promise<void> {
|
||||
if (!res.ok && res.status !== 401) throw new ApiError(await readError(res, '退出登录失败'), res.status);
|
||||
}
|
||||
|
||||
export async function postBacktest(req: BacktestRequest): Promise<BacktestResponse> {
|
||||
const res = await apiFetch('/api/backtest', { method: 'POST', body: JSON.stringify(req) });
|
||||
if (!res.ok) throw new ApiError(`回测请求失败 (HTTP ${res.status}): ${await res.text()}`, res.status);
|
||||
return (await res.json()) as BacktestResponse;
|
||||
}
|
||||
|
||||
export async function syncData(req: SyncRequest): Promise<SyncResponse> {
|
||||
const res = await apiFetch('/api/data/sync', { method: 'POST', body: JSON.stringify(req) });
|
||||
if (!res.ok) throw new ApiError(`数据拉取失败 (HTTP ${res.status}): ${await res.text()}`, res.status);
|
||||
return (await res.json()) as SyncResponse;
|
||||
}
|
||||
|
||||
/** 自然语言事件回测。全市场扫描较慢,timeout 放宽到 15 分钟。 */
|
||||
export async function postEventBacktest(req: EventBacktestRequest): Promise<EventBacktestResponse> {
|
||||
const ctrl = new AbortController();
|
||||
@@ -172,7 +159,7 @@ export async function startScreenerSync(req: ScreenerSyncRequest = {}): Promise<
|
||||
|
||||
export async function getScreenerSyncStatus(): Promise<ScreenerSyncStatus> {
|
||||
const res = await apiFetch('/api/screener/sync/status');
|
||||
if (!res.ok) throw new ApiError(`获取同步状态失败 (HTTP ${res.status})`, res.status);
|
||||
if (!res.ok) throw new ApiError(await readError(res, `获取同步状态失败 (HTTP ${res.status})`), res.status);
|
||||
return (await res.json()) as ScreenerSyncStatus;
|
||||
}
|
||||
|
||||
@@ -185,9 +172,10 @@ export async function getStockPreview(
|
||||
mas?: number[];
|
||||
/** 向前翻页:返回该日期(不含)之前的 limit 根 K 线 + 预热好的指标 */
|
||||
end?: string;
|
||||
signal?: AbortSignal;
|
||||
} = {},
|
||||
): Promise<PreviewResponse> {
|
||||
const { limit = 500, adjust = 'qfq', timeframe = '1d', mas, end } = opts;
|
||||
const { limit = 500, adjust = 'qfq', timeframe = '1d', mas, end, signal } = opts;
|
||||
const q = new URLSearchParams({
|
||||
limit: String(limit),
|
||||
adjust,
|
||||
@@ -195,7 +183,7 @@ export async function getStockPreview(
|
||||
...(mas?.length ? { mas: mas.join(',') } : {}),
|
||||
...(end ? { end } : {}),
|
||||
});
|
||||
const res = await apiFetch(`/api/screener/preview/${encodeURIComponent(tsCode)}?${q.toString()}`);
|
||||
const res = await apiFetch(`/api/screener/preview/${encodeURIComponent(tsCode)}?${q.toString()}`, { signal });
|
||||
if (!res.ok) throw new ApiError(await readError(res, `获取个股详情失败 (HTTP ${res.status})`), res.status);
|
||||
return (await res.json()) as PreviewResponse;
|
||||
}
|
||||
@@ -235,6 +223,26 @@ export async function getMarketOverview(): Promise<MarketOverview> {
|
||||
return (await res.json()) as MarketOverview;
|
||||
}
|
||||
|
||||
// ---------- 打板专题(主页) ----------
|
||||
export async function getLimitBoard(): Promise<LimitBoard> {
|
||||
const res = await apiFetch('/api/market/limit-board');
|
||||
if (!res.ok) throw new ApiError(await readError(res, '获取打板数据失败'), res.status);
|
||||
return (await res.json()) as LimitBoard;
|
||||
}
|
||||
|
||||
// ---------- 概念板块(THS) ----------
|
||||
export async function getThsBoards(opts: { signal?: AbortSignal } = {}): Promise<ThsBoardList> {
|
||||
const res = await apiFetch('/api/market/boards', { signal: opts.signal });
|
||||
if (!res.ok) throw new ApiError(await readError(res, '获取板块列表失败'), res.status);
|
||||
return (await res.json()) as ThsBoardList;
|
||||
}
|
||||
|
||||
export async function getThsBoardMembers(code: string, opts: { signal?: AbortSignal } = {}): Promise<ThsBoardMembers> {
|
||||
const res = await apiFetch(`/api/market/boards/${encodeURIComponent(code)}/members`, { signal: opts.signal });
|
||||
if (!res.ok) throw new ApiError(await readError(res, '获取板块成分失败'), res.status);
|
||||
return (await res.json()) as ThsBoardMembers;
|
||||
}
|
||||
|
||||
/** 上证指数全量 K 线(日线基底聚合到目标周期;收盘口径) */
|
||||
export async function getIndexCandles(timeframe: Timeframe): Promise<Candle[]> {
|
||||
const res = await apiFetch(`/api/market/index-candles?timeframe=${encodeURIComponent(timeframe)}`);
|
||||
@@ -256,9 +264,10 @@ export async function getIndexDetail(code: string): Promise<IndexDetail> {
|
||||
}
|
||||
|
||||
/** 白名单指数全量 K 线(国内 index_daily / 国际 index_global,日线基底聚合) */
|
||||
export async function getAnyIndexCandles(code: string, timeframe: Timeframe): Promise<Candle[]> {
|
||||
export async function getAnyIndexCandles(code: string, timeframe: Timeframe, opts: { signal?: AbortSignal } = {}): Promise<Candle[]> {
|
||||
const res = await apiFetch(
|
||||
`/api/market/indexes/${encodeURIComponent(code)}/candles?timeframe=${encodeURIComponent(timeframe)}`,
|
||||
{ signal: opts.signal },
|
||||
);
|
||||
if (!res.ok) throw new ApiError(await readError(res, `获取指数K线失败 (HTTP ${res.status})`), res.status);
|
||||
return (await res.json()) as Candle[];
|
||||
@@ -283,6 +292,7 @@ export async function getStocks(params: {
|
||||
order?: 'asc' | 'desc';
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
signal?: AbortSignal;
|
||||
}): Promise<StockListResponse> {
|
||||
const q = new URLSearchParams();
|
||||
if (params.search) q.set('search', params.search);
|
||||
@@ -294,7 +304,7 @@ export async function getStocks(params: {
|
||||
if (params.order) q.set('order', params.order);
|
||||
q.set('limit', String(params.limit ?? 100));
|
||||
q.set('offset', String(params.offset ?? 0));
|
||||
const res = await apiFetch(`/api/stocks?${q.toString()}`);
|
||||
const res = await apiFetch(`/api/stocks?${q.toString()}`, { signal: params.signal });
|
||||
if (!res.ok) throw new ApiError(await readError(res, `获取股票列表失败 (HTTP ${res.status})`), res.status);
|
||||
return (await res.json()) as StockListResponse;
|
||||
}
|
||||
@@ -314,6 +324,7 @@ export async function getEtfs(params: {
|
||||
order?: 'asc' | 'desc';
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
signal?: AbortSignal;
|
||||
}): Promise<EtfListResponse> {
|
||||
const q = new URLSearchParams();
|
||||
if (params.search) q.set('search', params.search);
|
||||
@@ -323,7 +334,7 @@ export async function getEtfs(params: {
|
||||
if (params.order) q.set('order', params.order);
|
||||
q.set('limit', String(params.limit ?? 100));
|
||||
q.set('offset', String(params.offset ?? 0));
|
||||
const res = await apiFetch(`/api/etfs?${q.toString()}`);
|
||||
const res = await apiFetch(`/api/etfs?${q.toString()}`, { signal: params.signal });
|
||||
if (!res.ok) throw new ApiError(await readError(res, `获取 ETF 列表失败 (HTTP ${res.status})`), res.status);
|
||||
return (await res.json()) as EtfListResponse;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user