2026-01-31 03:04:51 +08:00
|
|
|
|
"""Test for daily market data API.
|
|
|
|
|
|
|
|
|
|
|
|
Tests the daily interface implementation against api.md requirements:
|
|
|
|
|
|
- A股日线行情所有输出字段
|
|
|
|
|
|
- tor 换手率
|
|
|
|
|
|
- vr 量比
|
|
|
|
|
|
"""
|
2026-02-21 03:43:30 +08:00
|
|
|
|
|
2026-01-31 03:04:51 +08:00
|
|
|
|
import pytest
|
|
|
|
|
|
import pandas as pd
|
2026-02-21 03:43:30 +08:00
|
|
|
|
from src.data.api_wrappers import get_daily
|
2026-01-31 03:04:51 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Expected output fields according to api.md
|
|
|
|
|
|
EXPECTED_BASE_FIELDS = [
|
2026-02-21 03:43:30 +08:00
|
|
|
|
"ts_code", # 股票代码
|
|
|
|
|
|
"trade_date", # 交易日期
|
|
|
|
|
|
"open", # 开盘价
|
|
|
|
|
|
"high", # 最高价
|
|
|
|
|
|
"low", # 最低价
|
|
|
|
|
|
"close", # 收盘价
|
|
|
|
|
|
"pre_close", # 昨收价
|
|
|
|
|
|
"change", # 涨跌额
|
|
|
|
|
|
"pct_chg", # 涨跌幅
|
|
|
|
|
|
"vol", # 成交量
|
|
|
|
|
|
"amount", # 成交额
|
2026-01-31 03:04:51 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
EXPECTED_FACTOR_FIELDS = [
|
2026-02-21 03:43:30 +08:00
|
|
|
|
"turnover_rate", # 换手率 (tor)
|
|
|
|
|
|
"volume_ratio", # 量比 (vr)
|
2026-01-31 03:04:51 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestGetDaily:
|
2026-01-31 04:30:29 +08:00
|
|
|
|
"""Test cases for get_daily function with real API calls."""
|
2026-01-31 03:04:51 +08:00
|
|
|
|
|
|
|
|
|
|
def test_fetch_basic(self):
|
2026-01-31 04:30:29 +08:00
|
|
|
|
"""Test basic daily data fetch with real API."""
|
2026-02-21 03:43:30 +08:00
|
|
|
|
result = get_daily("000001.SZ", start_date="20240101", end_date="20240131")
|
2026-01-31 03:04:51 +08:00
|
|
|
|
|
|
|
|
|
|
assert isinstance(result, pd.DataFrame)
|
2026-01-31 04:30:29 +08:00
|
|
|
|
assert len(result) >= 1
|
2026-02-21 03:43:30 +08:00
|
|
|
|
assert result["ts_code"].iloc[0] == "000001.SZ"
|
2026-01-31 03:04:51 +08:00
|
|
|
|
|
|
|
|
|
|
def test_fetch_with_factors(self):
|
|
|
|
|
|
"""Test fetch with tor and vr factors."""
|
2026-01-31 04:30:29 +08:00
|
|
|
|
result = get_daily(
|
2026-02-21 03:43:30 +08:00
|
|
|
|
"000001.SZ",
|
|
|
|
|
|
start_date="20240101",
|
|
|
|
|
|
end_date="20240131",
|
|
|
|
|
|
factors=["tor", "vr"],
|
2026-01-31 04:30:29 +08:00
|
|
|
|
)
|
2026-01-31 03:04:51 +08:00
|
|
|
|
|
|
|
|
|
|
assert isinstance(result, pd.DataFrame)
|
|
|
|
|
|
# Check all base fields are present
|
|
|
|
|
|
for field in EXPECTED_BASE_FIELDS:
|
|
|
|
|
|
assert field in result.columns, f"Missing base field: {field}"
|
|
|
|
|
|
# Check factor fields are present
|
|
|
|
|
|
for field in EXPECTED_FACTOR_FIELDS:
|
|
|
|
|
|
assert field in result.columns, f"Missing factor field: {field}"
|
|
|
|
|
|
|
|
|
|
|
|
def test_output_fields_completeness(self):
|
|
|
|
|
|
"""Verify all required output fields are returned."""
|
2026-02-21 03:43:30 +08:00
|
|
|
|
result = get_daily("600000.SH")
|
2026-01-31 03:04:51 +08:00
|
|
|
|
|
|
|
|
|
|
# Verify all base fields are present
|
2026-02-21 03:43:30 +08:00
|
|
|
|
assert set(EXPECTED_BASE_FIELDS).issubset(result.columns.tolist()), (
|
2026-01-31 03:04:51 +08:00
|
|
|
|
f"Missing fields: {set(EXPECTED_BASE_FIELDS) - set(result.columns)}"
|
2026-02-21 03:43:30 +08:00
|
|
|
|
)
|
2026-01-31 03:04:51 +08:00
|
|
|
|
|
|
|
|
|
|
def test_empty_result(self):
|
|
|
|
|
|
"""Test handling of empty results."""
|
2026-01-31 04:30:29 +08:00
|
|
|
|
# 使用真实 API 测试无效股票代码的空结果
|
2026-02-21 03:43:30 +08:00
|
|
|
|
result = get_daily("INVALID.SZ")
|
2026-01-31 04:30:29 +08:00
|
|
|
|
assert isinstance(result, pd.DataFrame)
|
2026-01-31 03:04:51 +08:00
|
|
|
|
assert result.empty
|
|
|
|
|
|
|
|
|
|
|
|
def test_date_range_query(self):
|
|
|
|
|
|
"""Test query with date range."""
|
2026-01-31 04:30:29 +08:00
|
|
|
|
result = get_daily(
|
2026-02-21 03:43:30 +08:00
|
|
|
|
"000001.SZ",
|
|
|
|
|
|
start_date="20240101",
|
|
|
|
|
|
end_date="20240131",
|
2026-01-31 04:30:29 +08:00
|
|
|
|
)
|
2026-01-31 03:04:51 +08:00
|
|
|
|
|
2026-01-31 04:30:29 +08:00
|
|
|
|
assert isinstance(result, pd.DataFrame)
|
|
|
|
|
|
assert len(result) >= 1
|
2026-01-31 03:04:51 +08:00
|
|
|
|
|
|
|
|
|
|
def test_with_adj(self):
|
|
|
|
|
|
"""Test fetch with adjustment type."""
|
2026-02-21 03:43:30 +08:00
|
|
|
|
result = get_daily("000001.SZ", adj="qfq")
|
2026-01-31 03:04:51 +08:00
|
|
|
|
|
|
|
|
|
|
assert isinstance(result, pd.DataFrame)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_integration():
|
|
|
|
|
|
"""Integration test with real Tushare API (requires valid token)."""
|
|
|
|
|
|
import os
|
2026-02-21 03:43:30 +08:00
|
|
|
|
|
|
|
|
|
|
token = os.environ.get("TUSHARE_TOKEN")
|
2026-01-31 03:04:51 +08:00
|
|
|
|
if not token:
|
|
|
|
|
|
pytest.skip("TUSHARE_TOKEN not configured")
|
|
|
|
|
|
|
2026-02-21 03:43:30 +08:00
|
|
|
|
result = get_daily(
|
|
|
|
|
|
"000001.SZ", start_date="20240101", end_date="20240131", factors=["tor", "vr"]
|
|
|
|
|
|
)
|
2026-01-31 03:04:51 +08:00
|
|
|
|
|
|
|
|
|
|
# Verify structure
|
|
|
|
|
|
assert isinstance(result, pd.DataFrame)
|
|
|
|
|
|
if not result.empty:
|
|
|
|
|
|
# Check base fields
|
|
|
|
|
|
for field in EXPECTED_BASE_FIELDS:
|
|
|
|
|
|
assert field in result.columns, f"Missing base field: {field}"
|
|
|
|
|
|
# Check factor fields
|
|
|
|
|
|
for field in EXPECTED_FACTOR_FIELDS:
|
|
|
|
|
|
assert field in result.columns, f"Missing factor field: {field}"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-02-21 03:43:30 +08:00
|
|
|
|
if __name__ == "__main__":
|
2026-01-31 04:30:29 +08:00
|
|
|
|
# 运行 pytest 单元测试(真实API调用)
|
2026-02-21 03:43:30 +08:00
|
|
|
|
pytest.main([__file__, "-v"])
|