From 0cdee9704fc64250c40ac1b4ea812455da471fae Mon Sep 17 00:00:00 2001 From: linqi Date: Mon, 29 Jun 2026 20:31:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AF=B9=E9=BD=90=20daec=20=E5=88=86?= =?UTF-8?q?=E6=97=B6/K=E7=BA=BF=E4=B8=8E=E6=8C=87=E6=95=B0=E6=9D=83?= =?UTF-8?q?=E9=87=8D=E6=B1=87=E6=80=BB=E6=8E=A5=E5=8F=A3=20-=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=20SDK=20=EF=BC=8C=E5=AF=B9=E9=BD=90=20daec=20?= =?UTF-8?q?=EF=BC=8C=E6=94=AF=E6=8C=81=E5=8E=9F=E5=A7=8B=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E4=B8=8E=20-=20=E6=89=A9=E5=B1=95=20SDK=20=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=20=E3=80=81=E3=80=81=20-=20=E4=B8=BA=20daec=20?= =?UTF-8?q?=E5=88=86=E6=97=B6/K=E7=BA=BF=20SDK=20=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8F=82=E6=95=B0=E4=BA=92=E6=96=A5=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=EF=BC=8C=E9=81=BF=E5=85=8D=E5=8E=9F=E5=A7=8B=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E4=B8=8E=20v2=20=E5=85=BC=E5=AE=B9=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E6=B7=B7=E7=94=A8=20-=20=E6=9B=B4=E6=96=B0=20=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=8C=89=20=20=E6=9F=A5=E8=AF=A2=E6=8C=87?= =?UTF-8?q?=E6=95=B0=E6=9D=83=E9=87=8D=E6=B1=87=E6=80=BB=20-=20=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E6=9B=B4=E6=96=B0=20API=20Reference=20=E4=B8=8E?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/API_REFERENCE.md | 60 ++++++++++++++-- src/ftshare/apis/index.py | 4 +- src/ftshare/apis/stock.py | 124 ++++++++++++++++++++++++++++++++- src/ftshare/endpoints/index.py | 2 +- src/ftshare/endpoints/stock.py | 9 ++- tests/test_client.py | 61 +++++++++++++++- 6 files changed, 247 insertions(+), 13 deletions(-) diff --git a/docs/API_REFERENCE.md b/docs/API_REFERENCE.md index 88c38e0..d059c30 100644 --- a/docs/API_REFERENCE.md +++ b/docs/API_REFERENCE.md @@ -6,7 +6,7 @@ | 指标 | 数量 | |---|---:| -| SDK 方法总数 | 179 | +| SDK 方法总数 | 180 | ## 专题分布 @@ -14,7 +14,7 @@ | ftshare-doc 专题 | SDK 方法数 | API mixin 模块 | Endpoint 模块 | |---|---:|---|---| -| 股票数据 | 95 | `ftshare.apis.stock` | `ftshare.endpoints.stock` | +| 股票数据 | 96 | `ftshare.apis.stock` | `ftshare.endpoints.stock` | | 港股数据 | 14 | `ftshare.apis.hk` | `ftshare.endpoints.hk` | | 美股数据 | 9 | `ftshare.apis.us` | `ftshare.endpoints.us` | | 指数专题 | 8 | `ftshare.apis.index` | `ftshare.endpoints.index` | @@ -126,7 +126,8 @@ df = market.baidu_financial_calendar( | [`stock_pledge_detail`](#api-stock-pledge-detail) | 股权质押明细 | `GET` | `api/v1/market/data/pledge/pledge-detail` | `stock_code`, `is_last`, `page`, `page_size` | `股权质押明细.md` | | [`stock_pledge_summary`](#api-stock-pledge-summary) | 股权质押汇总 | `GET` | `api/v1/market/data/pledge/pledge-summary` | `page`, `page_size` | `股权质押汇总.md` | | [`stock_prev_close`](#api-stock-prev-close) | 标的昨收价 | `GET` | `api/v1/market/data/daec/history/prev-closes` | `symbol`, `since`, `until` | `标的昨收价.md` | -| [`stock_intraday_prices`](#api-stock-intraday-prices) | 标的分时数据 | `GET` | `api/v1/market/data/daec/history/prices` | `symbol`, `range`, `days`, `ts_ms` | `标得分时数据.md` | +| [`stock_intraday_prices`](#api-stock-intraday-prices) | 标的分时数据 | `GET` | `api/v1/market/data/daec/history/prices` | `symbol`, `range`, `days`, `ts_ms`, `compat`, `since`, `since_ts_ms` | `标得分时数据.md` | +| [`stock_ohlcs`](#api-stock-ohlcs) | 标的K线数据 | `GET` | `api/v1/market/data/daec/history/ohlcs` | `symbol`, `since`, `until`, `interval`, `adjust`, `compat`, `span`, `limit`, `until_ts_ms` | `标的K线数据.md` | | [`stock_rating_top5`](#api-stock-rating-top5) | 飞兔股票评级Top5 | `GET` | `api/v1/market/data/feitu/stock-rating-top5` | `date`, `variant`, `type` | `飞兔股票评级Top5.md` | | [`stock_share`](#api-stock-share) | 股本 | `GET` | `api/v1/market/data/share/get-stock-share` | `stock_code`, `date` | `股本.md` | | [`stock_share_chg`](#api-stock-share-chg) | 股东增减持 | `GET` | `api/v1/market/data/holder/stock-share-chg` | `stock_code`, `is_last`, `page`, `page_size` | `股东增减持.md` | @@ -185,7 +186,7 @@ df = market.baidu_financial_calendar( | [`index_description_all`](#api-index-description-all) | 指数基础信息 | `GET` | `api/v1/market/data/index-description-all` | - | `指数基础信息.md` | | [`index_description_list`](#api-index-description-list) | 中证指数描述列表 | `GET` | `api/v1/market/data/index/index_description` | `page`, `page_size` | `中证指数描述列表.md` | | [`index_weight_list`](#api-index-weight-list) | 指数权重列表 | `GET` | `api/v1/market/data/index/index_weight` | `index_code`, `date`, `page`, `page_size` | `指数权重列表.md` | -| [`index_weight_summary`](#api-index-weight-summary) | 指数权重汇总 | `GET` | `api/v1/market/data/index/index_weight_summary` | `page`, `page_size` | `指数权重汇总.md` | +| [`index_weight_summary`](#api-index-weight-summary) | 指数权重汇总 | `GET` | `api/v1/market/data/index/index_weight_summary` | `index_code`, `page`, `page_size` | `指数权重汇总.md` | | [`sw_industry_constituent_history`](#api-sw-industry-constituent-history) | 申万行业成份股历史 | `GET` | `api/v1/market/data/sw-industry/constituent-history` | `industry_code` | `申万行业成份股历史.md` | | [`sw_industry_daily_metrics`](#api-sw-industry-daily-metrics) | 申万行业日度指标 | `GET` | `api/v1/market/data/sw-industry/daily-metrics` | `level`, `start_date`, `end_date`, `industry_code`, `page`, `page_size` | `申万行业日度指标.md` | | [`sw_industry_overview`](#api-sw-industry-overview) | 申万行业总览 | `GET` | `api/v1/market/data/sw-industry/overview` | `date`, `level`, `page`, `page_size` | `申万行业总览.md` | @@ -1983,7 +1984,7 @@ Returns: - 接口名称:千股千评 - HTTP:`GET` - Path:`api/v1/market/data/stock-comment/index` -- 参数:`page`, `page_size` +- 参数:`index_code`, `page`, `page_size` - 来源文档:`千股千评.md` - 原始接口:`stock_comment_em` @@ -1995,6 +1996,7 @@ Method: ``GET``. Documented endpoint: ``stock_comment_em``. Args: + index_code: 指数代码,如 `000300` (type: string; required: Y). page: Page number, starting from 1. If omitted, the server default is used unless ``limit`` or ``all_pages`` is set. page_size: Rows per page. The SDK validates this against the endpoint-specific maximum. limit: Maximum number of rows to return. The SDK may fetch multiple pages to satisfy this limit. @@ -2844,7 +2846,7 @@ Returns: - 接口名称:标的分时数据 - HTTP:`GET` - Path:`api/v1/market/data/daec/history/prices` -- 参数:`symbol`, `range`, `days`, `ts_ms` +- 参数:`symbol`, `range`, `days`, `ts_ms`, `compat`, `since`, `since_ts_ms` - 来源文档:`标得分时数据.md` - 原始接口:`stock_intraday_prices` @@ -2860,6 +2862,9 @@ Args: range: 预置时间区间:Today / FiveDays (type: string; required: N). days: 近 N 个交易日至今 (type: uint32; required: N). ts_ms: 起始毫秒时间戳 (type: int64; required: N). + compat: 兼容模式。传 v2 时启用旧 v2 响应结构 (type: string; required: N). + since: v2 兼容模式参数。可选 TODAY / FIVE_DAYS_AGO / TRADE_DAYS_AGO(n) (type: string; required: N). + since_ts_ms: v2 兼容模式参数。按起始毫秒时间戳取数,优先级高于 since (type: int64; required: N). raw: Return the decoded JSON payload without tabular extraction. fields: Optional field list or comma-separated field string applied after extraction. as_dataframe: Return a pandas ``DataFrame`` by default; set to ``False`` for Python rows. @@ -2869,6 +2874,49 @@ Returns: A pandas ``DataFrame`` by default, Python rows when ``as_dataframe=False``, raw JSON when ``raw=True``, or raw page payloads when multi-page fetching is used with ``raw=True``. + +Raises: + ValueError: If original-mode and ``compat='v2'`` time controls are mixed. +``` + +

stock_ohlcs

+ +- 接口名称:标的K线数据 +- HTTP:`GET` +- Path:`api/v1/market/data/daec/history/ohlcs` +- 参数:`symbol`, `since`, `until`, `interval`, `adjust`, `compat`, `span`, `limit`, `until_ts_ms` +- 来源文档:`标的K线数据.md` +- 原始接口:`stock_ohlcs` + +```text +标的K线数据. + +Endpoint: ``api/v1/market/data/daec/history/ohlcs``. +Method: ``GET``. +Documented endpoint: ``stock_ohlcs``. + +Args: + symbol: 标的代码,如 600000.XSHG (type: string; required: Y). + since: 起始日期,格式 YYYYMMDD;v2 兼容模式不传时会根据 limit 估算回溯窗口 (type: string; required: 原始模式 Y / v2 兼容模式 N). + until: 结束日期,格式 YYYYMMDD;v2 兼容模式不传时默认当天 (type: string; required: 原始模式 Y / v2 兼容模式 N). + interval: 原始模式参数。周期:Minute / Day / Week / Month,默认 Day (type: string; required: N). + adjust: 复权方式:None / Forward / Backward;v2 兼容模式默认 Forward (type: string; required: N). + compat: 兼容模式。传 v2 时启用旧 v2 响应结构 (type: string; required: N). + span: v2 兼容模式参数。周期:DAY1 / WEEK1 / MONTH1;不支持 YEAR1 (type: string; required: N). + limit: v2 兼容模式参数。返回最近 N 根 K 线 (type: int; required: N). + until_ts_ms: v2 兼容模式参数。旧 v2 风格结束毫秒时间戳,会按北京时间转换为 until 日期 (type: int64; required: N). + raw: Return the decoded JSON payload without tabular extraction. + fields: Optional field list or comma-separated field string applied after extraction. + as_dataframe: Return a pandas ``DataFrame`` by default; set to ``False`` for Python rows. + **kwargs: Extra request parameters forwarded unchanged. Useful when the service adds parameters before the SDK is regenerated. + +Returns: + A pandas ``DataFrame`` by default, Python rows when + ``as_dataframe=False``, raw JSON when ``raw=True``, or raw page + payloads when multi-page fetching is used with ``raw=True``. + +Raises: + ValueError: If original-mode and ``compat='v2'`` controls are mixed. ```

stock_rating_top5

diff --git a/src/ftshare/apis/index.py b/src/ftshare/apis/index.py index a5cdf4a..52fd952 100644 --- a/src/ftshare/apis/index.py +++ b/src/ftshare/apis/index.py @@ -162,6 +162,7 @@ def index_weight_list( def index_weight_summary( self, + index_code: Any | None = None, page: int | None = None, page_size: int | None = None, limit: int | None = None, @@ -180,6 +181,7 @@ def index_weight_summary( Documented endpoint: ``index_weight_summary_handler``. Args: + index_code: 指数代码,如 `000300` (type: string; required: Y). page: Page number, starting from 1. If omitted, the server default is used unless ``limit`` or ``all_pages`` is set. page_size: Rows per page. The SDK validates this against the endpoint-specific maximum. limit: Maximum number of rows to return. The SDK may fetch multiple pages to satisfy this limit. @@ -195,7 +197,7 @@ def index_weight_summary( ``as_dataframe=False``, raw JSON when ``raw=True``, or raw page payloads when multi-page fetching is used with ``raw=True``. """ - request_params = {} + request_params = {'index_code': index_code} request_params.update(kwargs) path = ENDPOINTS['index_weight_summary'].path return self.get_paginated( diff --git a/src/ftshare/apis/stock.py b/src/ftshare/apis/stock.py index aee8557..48960d7 100644 --- a/src/ftshare/apis/stock.py +++ b/src/ftshare/apis/stock.py @@ -8,6 +8,10 @@ from ..endpoints import ENDPOINTS +def _present_params(params: dict[str, Any]) -> list[str]: + return [name for name, value in params.items() if value is not None] + + class StockApiMixin: """Endpoint methods for the stock ftshare-doc topic.""" @@ -4238,6 +4242,9 @@ def stock_intraday_prices( range: Any | None = None, days: Any | None = None, ts_ms: Any | None = None, + compat: Any | None = None, + since: Any | None = None, + since_ts_ms: Any | None = None, *, raw: bool = False, fields: Sequence[str] | str | None = None, @@ -4255,6 +4262,9 @@ def stock_intraday_prices( range: 预置时间区间:Today / FiveDays (type: string; required: N). days: 近 N 个交易日至今 (type: uint32; required: N). ts_ms: 起始毫秒时间戳 (type: int64; required: N). + compat: 兼容模式。传 v2 时启用旧 v2 响应结构 (type: string; required: N). + since: v2 兼容模式参数。可选 TODAY / FIVE_DAYS_AGO / TRADE_DAYS_AGO(n) (type: string; required: N). + since_ts_ms: v2 兼容模式参数。按起始毫秒时间戳取数,优先级高于 since (type: int64; required: N). raw: Return the decoded JSON payload without tabular extraction. fields: Optional field list or comma-separated field string applied after extraction. as_dataframe: Return a pandas ``DataFrame`` by default; set to ``False`` for Python rows. @@ -4264,8 +4274,45 @@ def stock_intraday_prices( A pandas ``DataFrame`` by default, Python rows when ``as_dataframe=False``, raw JSON when ``raw=True``, or raw page payloads when multi-page fetching is used with ``raw=True``. + + Raises: + ValueError: If original-mode and ``compat='v2'`` time controls are mixed. """ - request_params = {'symbol': symbol, 'range': range, 'days': days, 'ts_ms': ts_ms} + if compat is not None and compat != 'v2': + raise ValueError("compat must be 'v2' when provided") + raw_time_params = _present_params({'range': range, 'days': days, 'ts_ms': ts_ms}) + v2_time_params = _present_params({'since': since, 'since_ts_ms': since_ts_ms}) + if compat == 'v2': + if raw_time_params: + raise ValueError( + "stock_intraday_prices compat='v2' cannot be combined with raw time parameters: " + + ', '.join(raw_time_params) + ) + if len(v2_time_params) > 1: + raise ValueError( + "stock_intraday_prices v2 time parameters are mutually exclusive: " + + ', '.join(v2_time_params) + ) + elif v2_time_params: + raise ValueError( + "stock_intraday_prices v2 time parameters require compat='v2': " + + ', '.join(v2_time_params) + ) + elif len(raw_time_params) > 1: + raise ValueError( + "stock_intraday_prices raw time parameters are mutually exclusive: " + + ', '.join(raw_time_params) + ) + + request_params = { + 'symbol': symbol, + 'range': range, + 'days': days, + 'ts_ms': ts_ms, + 'compat': compat, + 'since': since, + 'since_ts_ms': since_ts_ms, + } request_params.update(kwargs) return self._call_endpoint( 'stock_intraday_prices', @@ -4275,6 +4322,81 @@ def stock_intraday_prices( **request_params, ) + def stock_ohlcs( + self, + symbol: Any | None = None, + since: Any | None = None, + until: Any | None = None, + interval: Any | None = None, + adjust: Any | None = None, + compat: Any | None = None, + span: Any | None = None, + limit: Any | None = None, + until_ts_ms: Any | None = None, + *, + raw: bool = False, + fields: Sequence[str] | str | None = None, + as_dataframe: bool = True, + **kwargs: Any, + ) -> Any: + """标的K线数据. + + Endpoint: ``api/v1/market/data/daec/history/ohlcs``. + Method: ``GET``. + Documented endpoint: ``stock_ohlcs``. + + Args: + symbol: 标的代码,如 600000.XSHG (type: string; required: Y). + since: 起始日期,格式 YYYYMMDD;v2 兼容模式不传时会根据 limit 估算回溯窗口 (type: string; required: 原始模式 Y / v2 兼容模式 N). + until: 结束日期,格式 YYYYMMDD;v2 兼容模式不传时默认当天 (type: string; required: 原始模式 Y / v2 兼容模式 N). + interval: 原始模式参数。周期:Minute / Day / Week / Month,默认 Day (type: string; required: N). + adjust: 复权方式:None / Forward / Backward;v2 兼容模式默认 Forward (type: string; required: N). + compat: 兼容模式。传 v2 时启用旧 v2 响应结构 (type: string; required: N). + span: v2 兼容模式参数。周期:DAY1 / WEEK1 / MONTH1;不支持 YEAR1 (type: string; required: N). + limit: v2 兼容模式参数。返回最近 N 根 K 线 (type: int; required: N). + until_ts_ms: v2 兼容模式参数。旧 v2 风格结束毫秒时间戳,会按北京时间转换为 until 日期 (type: int64; required: N). + raw: Return the decoded JSON payload without tabular extraction. + fields: Optional field list or comma-separated field string applied after extraction. + as_dataframe: Return a pandas ``DataFrame`` by default; set to ``False`` for Python rows. + **kwargs: Extra request parameters forwarded unchanged. Useful when the service adds parameters before the SDK is regenerated. + + Returns: + A pandas ``DataFrame`` by default, Python rows when + ``as_dataframe=False``, raw JSON when ``raw=True``, or raw page + payloads when multi-page fetching is used with ``raw=True``. + + Raises: + ValueError: If original-mode and ``compat='v2'`` controls are mixed. + """ + if compat is not None and compat != 'v2': + raise ValueError("compat must be 'v2' when provided") + v2_params = _present_params({'span': span, 'limit': limit, 'until_ts_ms': until_ts_ms}) + if compat == 'v2': + if interval is not None: + raise ValueError("stock_ohlcs compat='v2' uses span instead of interval") + elif v2_params: + raise ValueError("stock_ohlcs v2 parameters require compat='v2': " + ', '.join(v2_params)) + + request_params = { + 'symbol': symbol, + 'since': since, + 'until': until, + 'interval': interval, + 'adjust': adjust, + 'compat': compat, + 'span': span, + 'limit': limit, + 'until_ts_ms': until_ts_ms, + } + request_params.update(kwargs) + return self._call_endpoint( + 'stock_ohlcs', + raw=raw, + fields=fields, + as_dataframe=as_dataframe, + **request_params, + ) + def stock_rating_top5( self, date: Any | None = None, diff --git a/src/ftshare/endpoints/index.py b/src/ftshare/endpoints/index.py index 3773329..f8999d8 100644 --- a/src/ftshare/endpoints/index.py +++ b/src/ftshare/endpoints/index.py @@ -40,7 +40,7 @@ 'title': '指数权重汇总', 'doc_file': '指数权重汇总.md', 'original_api': 'index_weight_summary_handler', - 'params': ('page', 'page_size'), + 'params': ('index_code', 'page', 'page_size'), 'max_page_size': 100, }, 'sw_industry_constituent_history': { diff --git a/src/ftshare/endpoints/stock.py b/src/ftshare/endpoints/stock.py index 31c8b3e..4dddcee 100644 --- a/src/ftshare/endpoints/stock.py +++ b/src/ftshare/endpoints/stock.py @@ -558,7 +558,14 @@ 'title': '标的分时数据', 'doc_file': '标得分时数据.md', 'original_api': 'stock_intraday_prices', - 'params': ('symbol', 'range', 'days', 'ts_ms'), + 'params': ('symbol', 'range', 'days', 'ts_ms', 'compat', 'since', 'since_ts_ms'), + }, + 'stock_ohlcs': { + 'path': 'api/v1/market/data/daec/history/ohlcs', + 'title': '标的K线数据', + 'doc_file': '标的K线数据.md', + 'original_api': 'stock_ohlcs', + 'params': ('symbol', 'since', 'until', 'interval', 'adjust', 'compat', 'span', 'limit', 'until_ts_ms'), }, 'stock_rating_top5': { 'path': 'api/v1/market/data/feitu/stock-rating-top5', diff --git a/tests/test_client.py b/tests/test_client.py index 29d7efe..fb65dc5 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -150,6 +150,7 @@ def test_get_raw_true_returns_full_payload(): ("stock_ggmx_buy_ranking", {}), ("stock_ggmx", {}), ("pledge_summary", {}), + ("index_weight_summary", {"index_code": "000300"}), ], ) def test_endpoint_methods_map_to_expected_paths(method_name, kwargs): @@ -163,7 +164,7 @@ def test_endpoint_methods_map_to_expected_paths(method_name, kwargs): def test_all_documented_endpoints_are_available_as_client_methods(): client = FtshareClient(session=FakeSession([])) - assert len(ENDPOINTS) >= 179 + assert len(ENDPOINTS) >= 180 missing = [ name @@ -279,9 +280,15 @@ def test_confirmed_todo_endpoints_map_to_public_server_paths(): ), ( "stock_intraday_prices", - {"symbol": "600000.XSHG", "range": "Today"}, + {"symbol": "600000.XSHG", "compat": "v2", "since": "TODAY"}, "api/v1/market/data/daec/history/prices", - {"symbol": "600000.XSHG", "range": "Today"}, + {"symbol": "600000.XSHG", "compat": "v2", "since": "TODAY"}, + ), + ( + "stock_ohlcs", + {"symbol": "600000.XSHG", "compat": "v2", "span": "DAY1", "limit": 5}, + "api/v1/market/data/daec/history/ohlcs", + {"symbol": "600000.XSHG", "compat": "v2", "span": "DAY1", "limit": 5}, ), ] @@ -295,6 +302,54 @@ def test_confirmed_todo_endpoints_map_to_public_server_paths(): assert session.calls[0]["params"] == expected_params +@pytest.mark.parametrize( + ("kwargs", "message"), + [ + ( + {"symbol": "600000.XSHG", "compat": "v2", "range": "Today", "since": "TODAY"}, + "cannot be combined with raw time parameters", + ), + ( + {"symbol": "600000.XSHG", "since": "TODAY"}, + "require compat='v2'", + ), + ( + {"symbol": "600000.XSHG", "range": "Today", "days": 5}, + "raw time parameters are mutually exclusive", + ), + ( + {"symbol": "600000.XSHG", "compat": "v2", "since": "TODAY", "since_ts_ms": 1782696600000}, + "v2 time parameters are mutually exclusive", + ), + ], +) +def test_stock_intraday_prices_rejects_mixed_daec_time_modes(kwargs, message): + client = FtshareClient(session=FakeSession([])) + + with pytest.raises(ValueError, match=message): + client.stock_intraday_prices(**kwargs) + + +@pytest.mark.parametrize( + ("kwargs", "message"), + [ + ( + {"symbol": "600000.XSHG", "span": "DAY1", "limit": 5}, + "require compat='v2'", + ), + ( + {"symbol": "600000.XSHG", "compat": "v2", "span": "DAY1", "interval": "Day"}, + "uses span instead of interval", + ), + ], +) +def test_stock_ohlcs_rejects_mixed_daec_modes(kwargs, message): + client = FtshareClient(session=FakeSession([])) + + with pytest.raises(ValueError, match=message): + client.stock_ohlcs(**kwargs) + + def test_stock_market_list_families_format_board_path_parameters(): cases = [ (