fix(mac): 板块列表涨速恒 0——值槽实为排序键列值,暴露 sort_column(issue #53)

抓包+对值锚定确认:0x1231 响应中 price 与 pre_close 之间的 float 是
"当前排序列的值"(板块与领涨股各一份),并非固定涨速;此前硬编码
sort_column=0(涨跌幅,仅排序键、值槽恒 0),故该列永远全 0。

排序列映射(实测):0=涨跌幅(值槽恒0) 1=涨速 2=3日 3=20日 4=60日
5=年初至今 6=5日 7=10日。

- 新增 BoardSortColumn 枚举并公开导出;get_board_list(sync/async)
  新增 sort_column 参数,取涨速传 SPEED,默认涨跌幅排序行为不变
- BoardInfo 字段更名 rise_speed→sort_value、symbol_rise_speed→
  symbol_sort_value(旧名语义错误且恒 0,属破坏性更名)
- Web /board-mac/list 新增 sort_column 参数;CLI board-list 新增 --sort
- 新增 9 个测试;README 示例更新
This commit is contained in:
GitHub
2026-08-26 15:21:35 +08:00
parent 8c35a864e8
commit fd53ec4c90
13 changed files with 331 additions and 22 deletions
+15
View File
@@ -2,6 +2,21 @@
本文件记录 easy-tdx 的版本变更。格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/)。
## [1.20.10] — 2026-08-26
**`get_board_list` 板块涨速列恒为 0**Issue #53)——用户反馈板块列表的涨速列存在但全是 0。逆向核实(0x1231 抓包 + 与 `SymbolQuotesCmd` 字段逐一对值锚定)发现根因:响应中 price 与 pre_close 之间的那个 float **不是固定的"涨速",而是"当前排序列的值"**(板块与领涨股各一份)——请求里的 sort_column 此前硬编码为 0(涨跌幅),而涨跌幅列仅作排序键、值槽恒 0(客户端可由 price/pre_close 计算),所以永远拿到 0。实测锚定排序列映射:**0=涨跌幅(值槽恒 0)、1=涨速%、2=3日涨幅、3=20日涨幅、4=60日涨幅、5=年初至今、6=5日涨幅、7=10日涨幅**。
### 修复
- **`get_board_list` 暴露 `sort_column` 参数**`MacClient` / `AsyncMacClient`)—— 新增 `BoardSortColumn` 枚举(公开导出),取涨速传 `BoardSortColumn.SPEED`,此时按涨速降序返回、`sort_value` 列即涨速%;默认仍按涨跌幅降序(行为不变)。分页请求全程透传同一排序键。
- **字段更名(破坏性)**`BoardInfo.rise_speed → sort_value``symbol_rise_speed → symbol_sort_value``src/easy_tdx/mac/models.py``commands/board_list.py`)—— 旧名在语义上是错的(该值槽只有按涨速排序时才是涨速),且从未返回过正确数据(恒 0),更名比留着一个撒谎的列名更安全。
- **Web 端点 `/board-mac/list` 新增 `sort_column` 查询参数**`web/convert.py` 新增 `board_sort_from_str`)—— 如 `?sort_column=SPEED`CLI `easy-tdx board-list` 新增 `--sort` 选项(`CHANGE_PCT/SPEED/CHANGE_3D/CHANGE_5D/CHANGE_10D/CHANGE_20D/CHANGE_60D/YTD`)。
- README 板块示例补 `sort_column=BoardSortColumn.SPEED` 用法。
### 测试
- 新增 `tests/unit/test_board_list.py`9 例):sort_column 请求字节打包位置断言(帧偏移 16);排序列枚举值锚定;合成 160 字节记录解析(sort_value/symbol_sort_value);sync/async 客户端透传;`board_sort_from_str` 转换器;Web 端点 `?sort_column=SPEED` 端到端透传;记录长度 160 字节不变式;`_EXPECTED_KIND` 公共 API 契约补 `BoardSortColumn`。实测:涨速降序 top10(近期复牌 0.234%、教育培训 0.138%…)、3日/60日/年初至今等排序键数值与 `SymbolQuotesCmd` 同名字段逐一相等。全套 1035 个单测通过(`test_web_api.py` 2 个失败为基线已存在的环境问题)。
## [1.20.9] — 2026-08-26
**`get_history_fund_flow` 取不到历史主力净额**Issue #52)——用户反馈拿不到历史主力净额数据。排查发现三层根因(全部经 52 台已知服务器实测核实):其一,文档声称的"Category 22 直连资金流接口"是**虚构协议**——46 台可达服务器对该请求全部仅回 2 字节空包(0 条或 ret_count 撒谎),从未成功返回过数据,所谓"9 字节头 + 36 字节/条"响应格式系臆造(单测里的格式是 mock);其二,实际数据一直来自"日 K 线取日期 + 历史逐笔成交重算",但历史逐笔接口**当日数据要收盘清算后才有**,而日 K 盘中已包含当日 bar,导致 `start=0` 的最新一行(今天)恒为全 0;其三,`main_net_inflow`(主力净额)此前仅为 dataclass property`_to_df``asdict()` 静默丢弃,返回 DataFrame 里根本没有主力净额列。
+3 -2
View File
@@ -1348,10 +1348,11 @@ with MacClient.from_best_host() as c:
#### 板块
```python
from easy_tdx import BoardType
from easy_tdx import BoardSortColumn, BoardType
with MacClient.from_best_host() as c:
df = c.get_board_list(BoardType.GN) # 概念板块
df = c.get_board_list(sort_column=BoardSortColumn.SPEED) # 按涨速排序取涨速%
df = c.get_board_members("881001", sort_type=SortType.CHANGE_PCT)
df = c.get_belong_board(Market.SZ, "000001") # 个股所属板块
@@ -1773,7 +1774,7 @@ with MacClient.from_best_host() as client:
| `get_chart_sampling(market, code)` | 分时缩略采样 |
| `get_transactions(market, code, ...)` | 逐笔成交 |
| `get_symbol_info(market, code)` | 个股特征快照 |
| `get_board_list(board_type, ...)` | 板块列表 |
| `get_board_list(board_type, ..., sort_column)` | 板块列表(sort_value 列=排序键指标值) |
| `get_board_members(board_symbol, ...)` | 板块成分股报价 |
| `get_board_summary(board_symbol, ...)` | 板块汇总(成交额、主力净流入、涨跌家数) |
| `get_board_ranking(board_type, top_n, sort_by, ...)` | 板块涨跌幅排行榜(行业/概念排行) |
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "easy-tdx"
version = "1.20.9"
version = "1.20.10"
description = "通达信 TCP 协议行情数据客户端,支持在线行情、离线数据读取与写入同步"
readme = "README.md"
requires-python = ">=3.10"
+2
View File
@@ -29,6 +29,7 @@ from .exceptions import TdxCommandError, TdxConnectionError, TdxDecodeError, Tdx
from .mac.client import AsyncMacClient, MacClient
from .mac.enums import (
Adjust,
BoardSortColumn,
BoardType,
Category,
ExMarket,
@@ -73,6 +74,7 @@ __all__ = [
"Market",
"KlineCategory",
"Adjust",
"BoardSortColumn",
"BoardType",
"Category",
"ExMarket",
+28 -1
View File
@@ -4,15 +4,37 @@ from __future__ import annotations
import click
_SORT_CHOICES = [
"CHANGE_PCT",
"SPEED",
"CHANGE_3D",
"CHANGE_5D",
"CHANGE_10D",
"CHANGE_20D",
"CHANGE_60D",
"YTD",
]
@click.command("board-list")
@click.option("--type", "board_type", default="ALL", help="板块类型: ALL/HY/GN/FG/DQ/OTHER")
@click.option("--count", default=10000, type=int, help="请求数量")
@click.option(
"--sort",
"sort_column",
default="CHANGE_PCT",
type=click.Choice(_SORT_CHOICES),
help=(
"排序键,sort_value 列即该指标值;要取涨速传 SPEED"
"CHANGE_PCT 时该列恒 0,涨跌幅=price/pre_close-1"
),
)
@click.option("--table", "use_table", is_flag=True, help="表格输出")
@click.option("--output", "output_fmt", type=click.Choice(["json", "table", "csv"]), default="json")
def board_list(
board_type: str,
count: int,
sort_column: str,
use_table: bool,
output_fmt: str,
) -> None:
@@ -25,15 +47,20 @@ def board_list(
easy-tdx board-list --type GN --count 200
easy-tdx board-list --type HY
easy-tdx board-list --sort SPEED --table # 按涨速排序,sort_value 即涨速%
"""
from easy_tdx.mac.enums import BoardSortColumn
from .conn import get_mac_client
from .output import print_output
from .parsers import parse_board_type
fmt = "table" if use_table else output_fmt
bt = parse_board_type(board_type)
sc = BoardSortColumn[sort_column]
with get_mac_client() as client:
df = client.get_board_list(board_type=bt, count=count)
df = client.get_board_list(board_type=bt, count=count, sort_column=sc)
print_output(df, fmt)
+29 -6
View File
@@ -51,7 +51,16 @@ from .commands import (
from .commands.chart_sampling import ChartSamplingCmd
from .commands.file_query import FileDownloadCmd, FileListCmd
from .commands.goods_list import GoodsListCmd
from .enums import Adjust, BoardType, Category, FilterType, Period, SortOrder, SortType
from .enums import (
Adjust,
BoardSortColumn,
BoardType,
Category,
FilterType,
Period,
SortOrder,
SortType,
)
from .models import (
MacBar,
MacMultiTickChart,
@@ -704,20 +713,29 @@ class MacClient:
self,
board_type: BoardType = BoardType.ALL,
count: int = 10000,
sort_column: BoardSortColumn = BoardSortColumn.CHANGE_PCT,
) -> pd.DataFrame:
"""获取板块列表(自动分页)。
"""获取板块列表(自动分页,默认按涨跌幅降序)。
Args:
board_type: 板块类型。
count: 请求总数。
sort_column: 排序键(决定返回顺序与 ``sort_value`` 列的语义)。
可选:涨跌幅(默认)/涨速/3日/5日/10日/20日/60日/年初至今涨幅。
注意 ``sort_value`` 是"当前排序列的值"——要取涨速需传
``BoardSortColumn.SPEED``,此时按涨速降序返回;默认按涨跌幅
排序时该列恒 0(涨跌幅可由 price/pre_close 计算)。
Issue #53:此前该列被误标为"涨速"且恒为 0,根因即 sort_column
语义未实现。
"""
all_items = self._execute(BoardListCmd(board_type, 0, min(count, 150)))
all_items = self._execute(BoardListCmd(board_type, 0, min(count, 150), sort_column))
fetched = len(all_items)
offset = fetched
while fetched < count:
page_size = min(count - fetched, 150)
batch = self._execute(BoardListCmd(board_type, offset, page_size))
batch = self._execute(BoardListCmd(board_type, offset, page_size, sort_column))
if not batch:
break
all_items.extend(batch)
@@ -1690,14 +1708,19 @@ class AsyncMacClient(AsyncHeartbeatMixin):
self,
board_type: BoardType = BoardType.ALL,
count: int = 10000,
sort_column: BoardSortColumn = BoardSortColumn.CHANGE_PCT,
) -> pd.DataFrame:
all_items = await self._execute(BoardListCmd(board_type, 0, min(count, 150)))
"""获取板块列表(async,自动分页,默认按涨跌幅降序)。
``sort_column`` 语义与同步版一致(Issue #53)。
"""
all_items = await self._execute(BoardListCmd(board_type, 0, min(count, 150), sort_column))
fetched = len(all_items)
offset = fetched
while fetched < count:
page_size = min(count - fetched, 150)
batch = await self._execute(BoardListCmd(board_type, offset, page_size))
batch = await self._execute(BoardListCmd(board_type, offset, page_size, sort_column))
if not batch:
break
all_items.extend(batch)
+13 -7
View File
@@ -5,7 +5,7 @@ import struct
from ..._binary import unpack_from
from ...codec.mac_frame import build_mac_request
from ...commands.base import BaseCommand
from ..enums import BoardType
from ..enums import BoardSortColumn, BoardType
from ..models import BoardInfo
# 板板信息 + 领涨股信息,每组 160 字节
@@ -26,6 +26,10 @@ class BoardListCmd(BaseCommand[list[BoardInfo]]):
起始偏移量。
page_size : int
每页数量。
sort_column : BoardSortColumn
排序键。响应中 price 与 pre_close 之间的值槽返回的就是该列的值
(板块与领涨股各一份);CHANGE_PCT(0) 仅作排序键,值槽恒 0
Issue #53:此前硬编码 0 且把值槽误标为"涨速",导致永远全 0)。
"""
def __init__(
@@ -33,18 +37,20 @@ class BoardListCmd(BaseCommand[list[BoardInfo]]):
board_type: BoardType = BoardType.ALL,
start: int = 0,
page_size: int = 150,
sort_column: BoardSortColumn = BoardSortColumn.CHANGE_PCT,
) -> None:
self._board_type = board_type
self._start = start
self._page_size = page_size
self._sort_column = sort_column
def build_request(self) -> bytes:
# <HHBBHH8x: page_size, board_type, sort_col(0), sort_order(0), start, flag(1)
# <HHBBHH8x: page_size, board_type, sort_col, sort_order(0=降序), start, flag(1)
body = struct.pack(
"<HHBBHH8x",
self._page_size,
int(self._board_type),
0, # sort_column: 0 = rise_speed
int(self._sort_column),
0, # sort_order
self._start,
1, # flag
@@ -65,14 +71,14 @@ class BoardListCmd(BaseCommand[list[BoardInfo]]):
_pad1,
name_raw,
price,
rise_speed,
sort_value,
pre_close,
symbol_market,
symbol_code_raw,
_pad2,
symbol_name_raw,
symbol_price,
symbol_rise_speed,
symbol_sort_value,
symbol_pre_close,
) = unpack_from(_RECORD_FMT, body, offset, f"board_list record[{i}]")
@@ -82,13 +88,13 @@ class BoardListCmd(BaseCommand[list[BoardInfo]]):
code=code_raw.decode("gbk", errors="replace").rstrip("\x00"),
name=name_raw.decode("gbk", errors="replace").rstrip("\x00"),
price=price,
rise_speed=rise_speed,
sort_value=sort_value,
pre_close=pre_close,
symbol_market=symbol_market,
symbol_code=symbol_code_raw.decode("gbk", errors="replace").rstrip("\x00"),
symbol_name=symbol_name_raw.decode("gbk", errors="replace").rstrip("\x00"),
symbol_price=symbol_price,
symbol_rise_speed=symbol_rise_speed,
symbol_sort_value=symbol_sort_value,
symbol_pre_close=symbol_pre_close,
)
)
+18
View File
@@ -46,6 +46,24 @@ class BoardType(IntEnum):
ALL = 255 # 全部
class BoardSortColumn(IntEnum):
"""板块列表(0x1231)排序键;响应中 price 与 pre_close 之间的值槽
返回的就是当前排序列的值(Issue #53:原实现误标为固定"涨速"
硬编码 sort_column=0,该值恒为 0)。
实测锚定(2026-08-26,与 SymbolQuotesCmd 字段逐一对值):
"""
CHANGE_PCT = 0 # 涨跌幅%(仅排序键,值槽恒 0,可由 price/pre_close 计算)
SPEED = 1 # 涨速%
CHANGE_3D = 2 # 3日涨幅%
CHANGE_20D = 3 # 20日涨幅%
CHANGE_60D = 4 # 60日涨幅%
YTD = 5 # 年初至今%
CHANGE_5D = 6 # 5日涨幅%
CHANGE_10D = 7 # 10日涨幅%
class ExBoardType(IntEnum):
"""扩展市场板块类型。"""
+9 -3
View File
@@ -125,19 +125,25 @@ class MacTransaction:
@dataclass(frozen=True)
class BoardInfo:
"""板块信息。"""
"""板块信息。
``sort_value`` / ``symbol_sort_value`` 是"当前排序列的值"(板块与其领涨股
各一份),语义由请求的 ``BoardSortColumn`` 决定——如按涨速排序则为涨速%
按 3 日涨幅排序则为 3 日涨幅%。默认按涨跌幅排序时值槽恒 0(涨跌幅可由
price/pre_close 计算;Issue #53:此前误标为固定"涨速"且恒为 0)。
"""
market: int
code: str
name: str
price: float
rise_speed: float
sort_value: float
pre_close: float
symbol_market: int
symbol_code: str
symbol_name: str
symbol_price: float
symbol_rise_speed: float
symbol_sort_value: float
symbol_pre_close: float
+17
View File
@@ -68,6 +68,23 @@ def board_type_from_str(s: str) -> Any:
raise ValueError(f"无效板块类型 '{s}',可选值: {valid}") from None
def board_sort_from_str(s: str) -> Any:
"""将字符串转为 BoardSortColumn 枚举(CHANGE_PCT/SPEED/CHANGE_3D/...)。"""
from easy_tdx.mac.enums import BoardSortColumn
key = s.upper()
try:
return BoardSortColumn[key]
except KeyError:
pass
try:
return BoardSortColumn(int(key))
except (ValueError, TypeError):
pass
valid = ", ".join(m.name for m in BoardSortColumn)
raise ValueError(f"无效板块排序键 '{s}',可选值: {valid}") from None
def sort_type_from_str(s: str) -> Any:
"""将字符串转为 SortType 枚举(CHANGE_PCT/VOLUME/... 或 hex 数字)。"""
from easy_tdx.mac.enums import SortType
+15 -2
View File
@@ -7,6 +7,7 @@ from typing import Any
from fastapi import APIRouter, Depends, Query
from easy_tdx.web.convert import (
board_sort_from_str,
board_type_from_str,
market_value_from_str,
sort_order_from_str,
@@ -26,10 +27,22 @@ def _df_resp(df: Any) -> DataFrameResponse:
async def board_list(
board_type: str = Query("ALL", description="板块类型: ALL/HY/HY2/GN/FG/DQ"),
count: int = Query(500, ge=1, le=50000),
sort_column: str = Query(
"CHANGE_PCT",
description=(
"排序键: CHANGE_PCT/SPEED/CHANGE_3D/CHANGE_5D/CHANGE_10D/"
"CHANGE_20D/CHANGE_60D/YTDsort_value 列即该指标值"
"CHANGE_PCT 时恒 0,涨跌幅=price/pre_close-1"
),
),
client: Any = Depends(get_mac_client),
) -> DataFrameResponse:
"""获取板块列表。"""
df = await client.get_board_list(board_type=board_type_from_str(board_type), count=count)
"""获取板块列表(默认按涨跌幅降序;要取涨速传 sort_column=SPEED"""
df = await client.get_board_list(
board_type=board_type_from_str(board_type),
count=count,
sort_column=board_sort_from_str(sort_column),
)
return _df_resp(df)
+180
View File
@@ -0,0 +1,180 @@
"""板块列表(0x1231)排序键语义测试(Issue #53)。
背景实测(2026-08-26):响应中 price 与 pre_close 之间的值槽是
"当前排序列的值",板块与领涨股各一份;sort_column=0(涨跌幅)时
值槽恒 0,此前被误标为固定"涨速"且硬编码 0,导致整列恒 0。
"""
import struct
from unittest.mock import patch
import pytest
from easy_tdx.mac.client import MacClient
from easy_tdx.mac.commands.board_list import _RECORD_SIZE, BoardListCmd
from easy_tdx.mac.enums import BoardSortColumn, BoardType
from easy_tdx.mac.models import BoardInfo
# <HHBBHH8x> body: page_size(H), board_type(H), sort_col(B), order(B), start(H), flag(H)
# 帧 = 10 字节头 + 2 字节 msg_id + body → sort_col 位于 body[4] = 帧偏移 16。
_SORT_COL_POS = 10 + 2 + 4
class TestBoardListRequest:
def test_sort_column_packed_into_request(self):
"""sort_column 应写入请求第 3 个 body 字节(帧内偏移 15)。"""
req_default = BoardListCmd(BoardType.ALL, 0, 10).build_request()
assert req_default[_SORT_COL_POS] == int(BoardSortColumn.CHANGE_PCT)
req_speed = BoardListCmd(BoardType.ALL, 0, 10, BoardSortColumn.SPEED).build_request()
assert req_speed[_SORT_COL_POS] == int(BoardSortColumn.SPEED) == 1
req_ytd = BoardListCmd(BoardType.ALL, 0, 10, BoardSortColumn.YTD).build_request()
assert req_ytd[_SORT_COL_POS] == int(BoardSortColumn.YTD) == 5
# 只差 sort_col 一个字节,其余请求布局不变
assert len(req_default) == len(req_speed) == len(req_ytd)
def test_sort_column_enum_values(self):
"""实测锚定的排序列映射。"""
assert [c.value for c in BoardSortColumn] == [0, 1, 2, 3, 4, 5, 6, 7]
def _build_body(board_mid: float, symbol_mid: float) -> bytes:
"""构造一条"板块 + 领涨股"记录的响应 body。"""
board_half = struct.pack(
"<H6s16s44sfff",
1,
b"881247",
b"",
"医疗服务".encode("gbk"),
645.06, # price
board_mid, # sort_value(涨速等,语义随排序列)
653.76, # pre_close
)
symbol_half = struct.pack(
"<H6s16s44sfff",
0,
b"301235",
b"",
"领涨股".encode("gbk"),
12.34,
symbol_mid,
11.11,
)
return struct.pack("<HH", 2, 128) + board_half + symbol_half
class TestBoardListParse:
def test_parse_populates_sort_value(self):
cmd = BoardListCmd(BoardType.ALL, 0, 1, BoardSortColumn.SPEED)
rows = cmd.parse_response(_build_body(0.0791, 0.1587))
assert len(rows) == 1
r = rows[0]
assert isinstance(r, BoardInfo)
assert r.code == "881247"
assert r.name == "医疗服务"
assert r.price == pytest.approx(645.06)
assert r.pre_close == pytest.approx(653.76)
assert r.sort_value == pytest.approx(0.0791)
assert r.symbol_sort_value == pytest.approx(0.1587)
assert r.symbol_code == "301235"
def test_model_has_no_legacy_rise_speed_field(self):
"""旧字段名已移除(语义错误:值槽并非恒为涨速)。"""
assert not hasattr(
BoardInfo(
market=1,
code="1",
name="n",
price=0.0,
sort_value=0.0,
pre_close=0.0,
symbol_market=1,
symbol_code="2",
symbol_name="s",
symbol_price=0.0,
symbol_sort_value=0.0,
symbol_pre_close=0.0,
),
"rise_speed",
)
class TestGetBoardListPassThrough:
def test_client_forwards_sort_column(self):
"""get_board_list 应把 sort_column 透传给 BoardListCmd。"""
client = MacClient.__new__(MacClient)
seen: list[BoardListCmd] = []
def fake_execute(cmd):
seen.append(cmd)
return []
with patch.object(client, "_execute", side_effect=fake_execute):
df = client.get_board_list(BoardType.ALL, 10, BoardSortColumn.SPEED)
assert df.empty
assert seen and seen[0]._sort_column == BoardSortColumn.SPEED
def test_web_converter(self):
from easy_tdx.web.convert import board_sort_from_str
assert board_sort_from_str("speed") == BoardSortColumn.SPEED
assert board_sort_from_str("CHANGE_3D") == BoardSortColumn.CHANGE_3D
with pytest.raises(ValueError, match="无效板块排序键"):
board_sort_from_str("NOPE")
def test_record_size_unchanged(self):
"""响应记录仍为 160 字节(板块 + 领涨股各 80)。"""
assert _RECORD_SIZE == 160
class TestAsyncPassThrough:
def test_async_client_forwards_sort_column(self):
from easy_tdx.mac.client import AsyncMacClient
client = AsyncMacClient.__new__(AsyncMacClient)
seen: list[BoardListCmd] = []
async def fake_execute(cmd):
seen.append(cmd)
return []
with patch.object(client, "_execute", side_effect=fake_execute):
import asyncio
df = asyncio.run(client.get_board_list(BoardType.ALL, 10, BoardSortColumn.SPEED))
assert df.empty
assert seen and seen[0]._sort_column == BoardSortColumn.SPEED
class TestWebEndpoint:
def test_board_list_endpoint_accepts_sort_column(self):
"""Web /board-mac/list 端点应接受 sort_column 参数并透传。"""
pytest.importorskip("fastapi")
import pandas as pd
from fastapi import FastAPI
from fastapi.testclient import TestClient
from easy_tdx.web.routers import board_mac
app = FastAPI()
app.include_router(board_mac.router, prefix="/api/v1")
captured: dict = {}
class _FakeMacClient:
async def get_board_list(self, **kwargs):
captured.update(kwargs)
return pd.DataFrame()
app.state.mac_client = _FakeMacClient()
app.state.tdx_client = object()
with TestClient(app) as tc:
resp = tc.get("/api/v1/board-mac/list", params={"sort_column": "SPEED", "count": 5})
assert resp.status_code == 200
assert captured["sort_column"] == BoardSortColumn.SPEED
+1
View File
@@ -33,6 +33,7 @@ _EXPECTED_KIND: dict[str, str] = {
"Market": "class",
"KlineCategory": "class",
"Adjust": "class",
"BoardSortColumn": "class",
"BoardType": "class",
"Category": "class",
"ExMarket": "class",