ab9987e4c7
Co-authored-by: Cursor <cursoragent@cursor.com>
92 lines
3.4 KiB
Python
92 lines
3.4 KiB
Python
# Copyright (c) 2025-2026 马建军. All rights reserved.
|
||
# 专有软件 — 未经授权禁止复制、传播、转售。
|
||
# 严禁用于:带单/代客理财、向他人推荐期货品种或买卖建议、融资配资等业务。
|
||
# 详见 LICENSE.zh-CN.txt 与 docs/软件购买与使用协议.md
|
||
|
||
"""从第三方(AKShare)同步交易所参考手续费,并按倍率写入本地表。"""
|
||
import re
|
||
from typing import Any, Optional
|
||
|
||
from contract_specs import get_contract_spec
|
||
from fee_specs import get_fee_multiplier, upsert_fee_rate
|
||
|
||
|
||
def _to_float(val: Any) -> float:
|
||
if val is None:
|
||
return 0.0
|
||
s = str(val).strip().replace(",", "")
|
||
if not s or s in ("-", "None", "nan"):
|
||
return 0.0
|
||
try:
|
||
return float(s)
|
||
except ValueError:
|
||
return 0.0
|
||
|
||
|
||
def _parse_akshare_row(row: dict, multiplier: float) -> Optional[dict]:
|
||
code = str(row.get("合约代码") or row.get("代码") or "").strip()
|
||
if not code:
|
||
return None
|
||
m = re.match(r"^([A-Za-z]+)", code)
|
||
if not m:
|
||
return None
|
||
product = m.group(1).lower()
|
||
|
||
open_ratio = _to_float(row.get("手续费标准-开仓-万分之")) / 10000.0
|
||
open_fixed = _to_float(row.get("手续费标准-开仓-元"))
|
||
if open_fixed == 0 and row.get("开仓"):
|
||
open_fixed = _to_float(row.get("开仓"))
|
||
close_y_ratio = _to_float(row.get("手续费标准-平昨-万分之")) / 10000.0
|
||
close_y_fixed = _to_float(row.get("手续费标准-平昨-元"))
|
||
if close_y_fixed == 0 and row.get("平昨"):
|
||
close_y_fixed = _to_float(row.get("平昨"))
|
||
close_t_ratio = _to_float(row.get("手续费标准-平今-万分之")) / 10000.0
|
||
close_t_fixed = _to_float(row.get("手续费标准-平今-元"))
|
||
if close_t_fixed == 0 and row.get("平今"):
|
||
close_t_fixed = _to_float(row.get("平今"))
|
||
|
||
mult = int(get_contract_spec(code)["mult"])
|
||
exchange = str(row.get("交易所名称") or row.get("交易所") or "").strip()
|
||
|
||
return {
|
||
"product": product,
|
||
"exchange": exchange,
|
||
"mult": mult,
|
||
"open_fixed": round(open_fixed * multiplier, 6),
|
||
"open_ratio": round(open_ratio * multiplier, 8),
|
||
"close_yesterday_fixed": round(close_y_fixed * multiplier, 6),
|
||
"close_yesterday_ratio": round(close_y_ratio * multiplier, 8),
|
||
"close_today_fixed": round(close_t_fixed * multiplier, 6),
|
||
"close_today_ratio": round(close_t_ratio * multiplier, 8),
|
||
"source": "akshare",
|
||
}
|
||
|
||
|
||
def sync_fees_from_akshare(multiplier: Optional[float] = None) -> tuple[int, str]:
|
||
multiplier = multiplier if multiplier is not None else get_fee_multiplier()
|
||
try:
|
||
import akshare as ak
|
||
except ImportError:
|
||
return 0, "未安装 akshare,请执行 pip install akshare 后重试,或使用默认费率表"
|
||
|
||
try:
|
||
df = ak.futures_comm_info(symbol="所有")
|
||
except Exception as exc:
|
||
return 0, f"拉取第三方数据失败: {exc}"
|
||
|
||
if df is None or df.empty:
|
||
return 0, "第三方返回空数据"
|
||
|
||
seen: set[str] = set()
|
||
count = 0
|
||
for _, series in df.iterrows():
|
||
row = series.to_dict()
|
||
parsed = _parse_akshare_row(row, multiplier)
|
||
if not parsed or parsed["product"] in seen:
|
||
continue
|
||
seen.add(parsed["product"])
|
||
upsert_fee_rate(parsed["product"], parsed)
|
||
count += 1
|
||
|
||
return count, f"已同步 {count} 个品种(标准费率 × {multiplier})"
|