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:
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user