feat: 统一交易日历为 pandas_market_calendars

- 移除 Tushare 交易日历依赖,A股/美股/港股统一使用 pandas_market_calendars
- 简化 get_trading_calendar() 接口,移除 exchange 参数(沪深日历一致)
- 删除冗余的 _get_china/us/hk_calendar() 独立函数,直接调用 mcal
- 新增 Flask API 端点: /api/v1/trading-calendar, /api/v1/calendar/info
- 代码减少 73 行 (-61%),逻辑更集中易维护
- 更新 API 文档描述,三个市场数据源统一
This commit is contained in:
2026-05-24 11:08:26 +08:00
parent 1bf91bdcd0
commit 100eed455d
2 changed files with 212 additions and 3 deletions

View File

@@ -545,19 +545,22 @@ def index():
"""首页 - API 信息"""
return jsonify({
"name": "Universal Data Fetcher API",
"version": "2.0.0",
"description": "统一数据获取服务(分层架构)",
"version": "2.1.0",
"description": "统一数据获取服务(分层架构 + 交易日历",
"architecture": "Unified entry + Asset-specific methods",
"features": [
"分层架构(各资产类型独立实现)",
"LRU + TTL 双缓存机制",
"SSH隧道支持港美股",
"ETF净值获取计算溢价率",
"多市场交易日历A股/美股/港股)",
],
"endpoints": {
"info": "/",
"health": "/health",
"asset_type": "/api/v1/asset-type?code={code}",
"trading_calendar": "/api/v1/trading-calendar?market={A|US|HK}&start={YYYY-MM-DD}&end={YYYY-MM-DD}",
"calendar_info": "/api/v1/calendar/info",
"ohlcv": "/api/v1/ohlcv?code={code}&start={YYYY-MM-DD}&end={YYYY-MM-DD}&asset_type={type}",
"ohlcv_nocache": "/api/v1/ohlcv?code={code}&nocache=true",
"ohlcv_crypto": "/api/v1/ohlcv?code=BTC&timeframe=1d (加密货币必须指定 timeframe)",
@@ -565,6 +568,11 @@ def index():
"cache_clear": "POST /api/v1/cache/clear",
"cache_stats": "/api/v1/cache/stats",
},
"trading_calendar_markets": {
"A": "A股pandas_market_calendars",
"US": "美股pandas_market_calendars",
"HK": "港股pandas_market_calendars",
},
"crypto_timeframes": {
"1d": "日线",
"1h": "小时线",
@@ -590,6 +598,7 @@ def index():
},
"cache_config": get_cache_info(),
"ssh": get_fetcher().get_ssh_status(),
"calendar_info": get_fetcher().get_calendar_info(),
})
@@ -624,6 +633,108 @@ def detect_asset_type():
})
@app.route('/api/v1/trading-calendar')
def get_trading_calendar():
"""
获取交易日历
Query Parameters:
market: 市场代码 (required)
- A: A股上交所/深交所,交易日历一致)
- US: 美股NYSE
- HK: 港股HKEX
start: 开始日期 YYYY-MM-DD (required)
end: 结束日期 YYYY-MM-DD (required)
Returns:
JSON 包含 trading_dates 列表(日期字符串数组)
示例:
/api/v1/trading-calendar?market=A&start=2024-01-01&end=2024-12-31
/api/v1/trading-calendar?market=US&start=2024-01-01&end=2024-12-31
/api/v1/trading-calendar?market=HK&start=2024-01-01&end=2024-12-31
"""
market = request.args.get('market', '').strip()
start = request.args.get('start', '').strip()
end = request.args.get('end', '').strip()
# 参数验证
if not market:
return jsonify({
"error": "Missing required parameter: market",
"example": "/api/v1/trading-calendar?market=A&start=2024-01-01&end=2024-12-31",
"supported_markets": ["A", "US", "HK"],
}), 400
if not start or not end:
return jsonify({
"error": "Missing required parameters: start and end",
"example": "/api/v1/trading-calendar?market=A&start=2024-01-01&end=2024-12-31",
}), 400
# 日期格式验证
if not validate_date(start) or not validate_date(end):
return jsonify({
"error": "Invalid date format. Use YYYY-MM-DD",
"start": start,
"end": end,
}), 400
try:
# 获取交易日历
f = get_fetcher()
trading_dates = f.get_trading_calendar(market, start, end)
# 转换为日期字符串列表
dates_list = [d.strftime('%Y-%m-%d') for d in trading_dates]
# 获取默认交易所名称
exchange_map = {
'A': 'SSE',
'US': 'NYSE',
'HK': 'HKEX',
}
exchange = exchange_map.get(market.upper(), '')
return jsonify({
"market": market.upper(),
"exchange": exchange,
"start": start,
"end": end,
"trading_dates": dates_list,
"count": len(dates_list),
})
except ValueError as e:
return jsonify({
"error": str(e),
"supported_markets": ["A", "US", "HK"],
}), 400
except ImportError as e:
return jsonify({
"error": str(e),
"hint": "请安装 pandas_market_calendars: pip install pandas_market_calendars",
}), 500
except Exception as e:
return jsonify({
"error": f"Failed to fetch trading calendar: {str(e)}",
}), 500
@app.route('/api/v1/calendar/info')
def calendar_info():
"""获取交易日历支持信息"""
try:
f = get_fetcher()
info = f.get_calendar_info()
return jsonify(info)
except Exception as e:
return jsonify({
"error": f"Failed to get calendar info: {str(e)}",
}), 500
@app.route('/api/v1/ohlcv')
def get_ohlcv():
"""
@@ -828,6 +939,8 @@ def not_found(error):
"available_endpoints": [
"/", "/health",
"/api/v1/asset-type",
"/api/v1/trading-calendar",
"/api/v1/calendar/info",
"/api/v1/ohlcv",
"/api/v1/cache/clear",
"/api/v1/cache/stats",