feat(web): 完善选股详情图表与案例图
This commit is contained in:
@@ -4,7 +4,11 @@ const requestJson = vi.hoisted(() => vi.fn())
|
||||
|
||||
vi.mock("@/shared/api/request-json", () => ({ requestJson }))
|
||||
|
||||
import { getSelectionResults, getSelectionRun } from "./selection.api"
|
||||
import {
|
||||
getSelectionChart,
|
||||
getSelectionResults,
|
||||
getSelectionRun,
|
||||
} from "./selection.api"
|
||||
|
||||
describe("selection API adapters", () => {
|
||||
beforeEach(() => {
|
||||
@@ -47,4 +51,18 @@ describe("selection API adapters", () => {
|
||||
expect(params.has("category")).toBe(false)
|
||||
expect(params.has("sort")).toBe(false)
|
||||
})
|
||||
|
||||
it("requests a stock chart with an encoded code and target date", async () => {
|
||||
await getSelectionChart("600601.SH", "2026-08-08")
|
||||
|
||||
const [input, init] = requestJson.mock.calls[0] as [
|
||||
string,
|
||||
{ signal?: AbortSignal },
|
||||
]
|
||||
expect(input).toContain("/api/v1/selection/stocks/600601.SH/chart?")
|
||||
expect(
|
||||
new URL(input, "http://localhost").searchParams.get("target_trade_date"),
|
||||
).toBe("2026-08-08")
|
||||
expect(init).toEqual({ signal: undefined })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { requestJson } from "@/shared/api/request-json"
|
||||
|
||||
import type {
|
||||
SelectionChart,
|
||||
SelectionResults,
|
||||
SelectionResultsQuery,
|
||||
SelectionRunAccepted,
|
||||
@@ -8,6 +9,20 @@ import type {
|
||||
SelectionStrategy,
|
||||
} from "./selection.types"
|
||||
|
||||
export function getSelectionChart(
|
||||
tsCode: string,
|
||||
targetTradeDate: string,
|
||||
signal?: AbortSignal,
|
||||
) {
|
||||
const params = new URLSearchParams({
|
||||
target_trade_date: targetTradeDate,
|
||||
})
|
||||
return requestJson<SelectionChart>(
|
||||
`/api/v1/selection/stocks/${encodeURIComponent(tsCode)}/chart?${params.toString()}`,
|
||||
{ signal },
|
||||
)
|
||||
}
|
||||
|
||||
export function getSelectionResults(
|
||||
strategy: SelectionStrategy,
|
||||
targetTradeDate?: string,
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
import { useEffect } from "react"
|
||||
|
||||
import {
|
||||
getSelectionChart,
|
||||
getSelectionResults,
|
||||
getSelectionRun,
|
||||
triggerSelectionRun,
|
||||
@@ -17,6 +18,12 @@ import type {
|
||||
SelectionStrategy,
|
||||
} from "./selection.types"
|
||||
|
||||
export const selectionChartQueryKey = (
|
||||
tsCode?: string,
|
||||
targetTradeDate?: string,
|
||||
) =>
|
||||
["selection", "chart", tsCode ?? "none", targetTradeDate ?? "none"] as const
|
||||
|
||||
export const selectionResultsQueryKey = (
|
||||
strategy: SelectionStrategy,
|
||||
targetTradeDate?: string,
|
||||
@@ -61,6 +68,15 @@ export function useSelectionResults(
|
||||
})
|
||||
}
|
||||
|
||||
export function useSelectionChart(tsCode?: string, targetTradeDate?: string) {
|
||||
return useQuery({
|
||||
enabled: Boolean(tsCode && targetTradeDate),
|
||||
queryFn: ({ signal }) =>
|
||||
getSelectionChart(tsCode ?? "", targetTradeDate ?? "", signal),
|
||||
queryKey: selectionChartQueryKey(tsCode, targetTradeDate),
|
||||
})
|
||||
}
|
||||
|
||||
export function useSelectionRun(
|
||||
runId: string | null,
|
||||
resultQuery: SelectionResultsQuery = { page: 1, pageSize: 10 },
|
||||
|
||||
@@ -58,6 +58,26 @@ export interface SelectionStockResult {
|
||||
signals: SelectionSignal[]
|
||||
}
|
||||
|
||||
export interface SelectionChartPoint {
|
||||
trade_date: string
|
||||
open: number | null
|
||||
high: number | null
|
||||
low: number | null
|
||||
close: number | null
|
||||
volume: number | null
|
||||
k: number | null
|
||||
d: number | null
|
||||
j: number | null
|
||||
}
|
||||
|
||||
export interface SelectionChart {
|
||||
ts_code: string
|
||||
name: string
|
||||
target_trade_date: string
|
||||
source_adj: "qfq"
|
||||
points: SelectionChartPoint[]
|
||||
}
|
||||
|
||||
export interface SelectionPatternScore {
|
||||
status: "matched" | "below_threshold" | "failed"
|
||||
value: number | null
|
||||
|
||||
Reference in New Issue
Block a user