feat(selection): add Zhixing line indicators to selection chart and update related components
This commit is contained in:
@@ -70,6 +70,8 @@ export interface SelectionChartPoint {
|
||||
k: number | null
|
||||
d: number | null
|
||||
j: number | null
|
||||
trend_white: number | null
|
||||
trend_yellow: number | null
|
||||
}
|
||||
|
||||
export interface SelectionChart {
|
||||
|
||||
@@ -13,6 +13,8 @@ function point(index: number): SelectionChartPoint {
|
||||
low: index + 9,
|
||||
open: index + 10,
|
||||
trade_date: `2026-${String(Math.floor(index / 28) + 1).padStart(2, "0")}-${String((index % 28) + 1).padStart(2, "0")}`,
|
||||
trend_white: index + 10.2,
|
||||
trend_yellow: index + 10.4,
|
||||
volume: index + 1000,
|
||||
}
|
||||
}
|
||||
@@ -28,9 +30,11 @@ describe("selection chart option", () => {
|
||||
}>
|
||||
const series = option.series as Array<{
|
||||
data: unknown[]
|
||||
name: string
|
||||
xAxisIndex: number
|
||||
yAxisIndex: number
|
||||
}>
|
||||
const legends = option.legend as Array<{ data: string[] }>
|
||||
|
||||
expect(dataZoom).toHaveLength(2)
|
||||
expect(dataZoom[0]).toMatchObject({
|
||||
@@ -39,13 +43,26 @@ describe("selection chart option", () => {
|
||||
xAxisIndex: [0, 1, 2],
|
||||
})
|
||||
expect(dataZoom[1].xAxisIndex).toEqual([0, 1, 2])
|
||||
expect(series[0]).toMatchObject({ xAxisIndex: 0, yAxisIndex: 0 })
|
||||
expect(series[1]).toMatchObject({ xAxisIndex: 1, yAxisIndex: 1 })
|
||||
expect(series.slice(2)).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ xAxisIndex: 2, yAxisIndex: 2 }),
|
||||
]),
|
||||
)
|
||||
expect(legends[0]?.data).toEqual(["知行白线", "知行黄线"])
|
||||
expect(series.find((item) => item.name === "日线")).toMatchObject({
|
||||
xAxisIndex: 0,
|
||||
yAxisIndex: 0,
|
||||
})
|
||||
expect(series.find((item) => item.name === "知行白线")).toMatchObject({
|
||||
data: points.map((item) => item.trend_white),
|
||||
xAxisIndex: 0,
|
||||
yAxisIndex: 0,
|
||||
})
|
||||
expect(series.find((item) => item.name === "知行黄线")).toMatchObject({
|
||||
data: points.map((item) => item.trend_yellow),
|
||||
xAxisIndex: 0,
|
||||
yAxisIndex: 0,
|
||||
})
|
||||
expect(series.find((item) => item.name === "成交量")).toMatchObject({
|
||||
xAxisIndex: 1,
|
||||
yAxisIndex: 1,
|
||||
})
|
||||
expect(series.filter((item) => item.xAxisIndex === 2)).toHaveLength(3)
|
||||
expect(series[0].data[0]).toEqual([10, 10.5, 9, 11])
|
||||
})
|
||||
|
||||
@@ -57,20 +74,25 @@ describe("selection chart option", () => {
|
||||
high: null,
|
||||
j: null,
|
||||
k: null,
|
||||
trend_white: null,
|
||||
trend_yellow: null,
|
||||
volume: null,
|
||||
}
|
||||
const option = buildSelectionChartOption([incomplete])
|
||||
const series = option.series as Array<{
|
||||
connectNulls?: boolean
|
||||
data: unknown[]
|
||||
name: string
|
||||
}>
|
||||
const candle = series[0].data[0] as number[]
|
||||
const volume = series.find((item) => item.name === "成交量")
|
||||
const lines = series.filter((item) => item.connectNulls === false)
|
||||
|
||||
expect(candle).toHaveLength(4)
|
||||
expect(candle.every(Number.isNaN)).toBe(true)
|
||||
expect(series[1].data[0]).toMatchObject({ value: null })
|
||||
for (const indicator of series.slice(2)) {
|
||||
expect(indicator.connectNulls).toBe(false)
|
||||
expect(volume?.data[0]).toMatchObject({ value: null })
|
||||
expect(lines).toHaveLength(5)
|
||||
for (const indicator of lines) {
|
||||
expect(indicator.data).toEqual([null])
|
||||
}
|
||||
})
|
||||
|
||||
@@ -16,6 +16,11 @@ import type { SelectionChartPoint } from "../api/selection.types"
|
||||
|
||||
const DEFAULT_VISIBLE_POINTS = 120
|
||||
|
||||
const zhixingLineSeries = [
|
||||
{ color: "#a8a8a8", key: "trend_white", name: "知行白线" },
|
||||
{ color: "#eab308", key: "trend_yellow", name: "知行黄线" },
|
||||
] as const
|
||||
|
||||
export type SelectionChartOption = ComposeOption<
|
||||
| BarSeriesOption
|
||||
| CandlestickSeriesOption
|
||||
@@ -30,7 +35,7 @@ export type SelectionChartOption = ComposeOption<
|
||||
/**
|
||||
* Build the three-grid financial chart configuration from aligned API points.
|
||||
*
|
||||
* @param points Ascending qfq OHLCV and KDJ points from the selection API.
|
||||
* @param points Ascending qfq OHLCV, KDJ, and Zhixing-line points from the API.
|
||||
* @returns One ECharts option with synchronized axes and an exact 120-point window.
|
||||
*/
|
||||
export function buildSelectionChartOption(
|
||||
@@ -63,11 +68,18 @@ export function buildSelectionChartOption(
|
||||
{ bottom: "28%", height: "14%", left: 64, right: 24 },
|
||||
{ bottom: 48, height: "20%", left: 64, right: 24 },
|
||||
],
|
||||
legend: {
|
||||
data: ["K", "D", "J"],
|
||||
right: 20,
|
||||
top: "73%",
|
||||
},
|
||||
legend: [
|
||||
{
|
||||
data: zhixingLineSeries.map((line) => line.name),
|
||||
right: 20,
|
||||
top: 8,
|
||||
},
|
||||
{
|
||||
data: ["K", "D", "J"],
|
||||
right: 20,
|
||||
top: "73%",
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
data: points.map(toCandlestickValue),
|
||||
@@ -82,6 +94,21 @@ export function buildSelectionChartOption(
|
||||
xAxisIndex: 0,
|
||||
yAxisIndex: 0,
|
||||
},
|
||||
...zhixingLineSeries.map((line): LineSeriesOption => ({
|
||||
connectNulls: false,
|
||||
data: points.map((point) => point[line.key]),
|
||||
itemStyle: { color: line.color },
|
||||
lineStyle: {
|
||||
color: line.color,
|
||||
width: 1.5,
|
||||
},
|
||||
name: line.name,
|
||||
showSymbol: false,
|
||||
smooth: false,
|
||||
type: "line",
|
||||
xAxisIndex: 0,
|
||||
yAxisIndex: 0,
|
||||
})),
|
||||
{
|
||||
data: points.map((point) => ({
|
||||
itemStyle: {
|
||||
|
||||
@@ -46,6 +46,8 @@ const chart: SelectionChartData = {
|
||||
low: 9,
|
||||
open: 10,
|
||||
trade_date: "2026-08-08",
|
||||
trend_white: 10.2,
|
||||
trend_yellow: 10.4,
|
||||
volume: 1000,
|
||||
},
|
||||
],
|
||||
@@ -88,7 +90,7 @@ describe("SelectionChart", () => {
|
||||
|
||||
expect(
|
||||
screen.getByRole("img", {
|
||||
name: "药明康德 日线、成交量和 KDJ 图表",
|
||||
name: "药明康德 日线、知行黄白线、成交量和 KDJ 图表",
|
||||
}),
|
||||
).toBeInTheDocument()
|
||||
expect(chartMocks.init).toHaveBeenCalledTimes(1)
|
||||
|
||||
@@ -61,7 +61,7 @@ export function SelectionChart({ chart }: SelectionChartProps) {
|
||||
|
||||
return (
|
||||
<div
|
||||
aria-label={`${chart.name} 日线、成交量和 KDJ 图表`}
|
||||
aria-label={`${chart.name} 日线、知行黄白线、成交量和 KDJ 图表`}
|
||||
className="h-[620px] min-h-[520px] w-full"
|
||||
ref={containerRef}
|
||||
role="img"
|
||||
|
||||
Reference in New Issue
Block a user