Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dd1833c68e | |||
| 2883402b96 |
@@ -14,6 +14,60 @@ AI 协助分析交易数据和回测的仓库。主要用来做策略回测、
|
|||||||
- 无可用镜像时才允许拉取,默认拉取 `python:3.11-slim`
|
- 无可用镜像时才允许拉取,默认拉取 `python:3.11-slim`
|
||||||
- 示例: `docker images | grep python` 查看已有镜像,再用已有镜像执行
|
- 示例: `docker images | grep python` 查看已有镜像,再用已有镜像执行
|
||||||
|
|
||||||
|
## 环境搭建(首次使用)
|
||||||
|
|
||||||
|
### 1. 创建容器
|
||||||
|
|
||||||
|
检查是否已有 `finance-talk` 容器(名称固定,在终端中运行):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker ps -a --filter "name=finance-talk" --format "{{.Names}}"
|
||||||
|
```
|
||||||
|
|
||||||
|
若无输出,则按以下步骤创建:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 查看已有 Python 镜像,优先复用
|
||||||
|
docker images | grep python
|
||||||
|
|
||||||
|
# 无可用镜像时才拉取(默认 python:3.11-slim)
|
||||||
|
docker pull python:3.11-slim
|
||||||
|
|
||||||
|
# 创建并启动容器(映射当前目录到 /workspace)
|
||||||
|
docker run -d \
|
||||||
|
--name finance-talk \
|
||||||
|
-v $(pwd):/workspace \
|
||||||
|
-w /workspace \
|
||||||
|
python:3.13.11-slim \
|
||||||
|
sleep infinity
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 安装依赖包
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec finance-talk pip install tushare akshare
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 验证
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec finance-talk python -c "import tushare; import akshare; print('OK')"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 日常使用
|
||||||
|
|
||||||
|
Python 脚本通过 `docker exec` 在容器中执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec finance-talk python your_script.py
|
||||||
|
```
|
||||||
|
|
||||||
|
若容器已存在但未运行,先启动:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker start finance-talk
|
||||||
|
```
|
||||||
|
|
||||||
## Data Sources
|
## Data Sources
|
||||||
|
|
||||||
按优先级排序:
|
按优先级排序:
|
||||||
|
|||||||
@@ -14,6 +14,60 @@ AI 协助分析交易数据和回测的仓库。主要用来做策略回测、
|
|||||||
- 无可用镜像时才允许拉取,默认拉取 `python:3.11-slim`
|
- 无可用镜像时才允许拉取,默认拉取 `python:3.11-slim`
|
||||||
- 示例: `docker images | grep python` 查看已有镜像,再用已有镜像执行
|
- 示例: `docker images | grep python` 查看已有镜像,再用已有镜像执行
|
||||||
|
|
||||||
|
## 环境搭建(首次使用)
|
||||||
|
|
||||||
|
### 1. 创建容器
|
||||||
|
|
||||||
|
检查是否已有 `finance-talk` 容器(名称固定,在终端中运行):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker ps -a --filter "name=finance-talk" --format "{{.Names}}"
|
||||||
|
```
|
||||||
|
|
||||||
|
若无输出,则按以下步骤创建:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 查看已有 Python 镜像,优先复用
|
||||||
|
docker images | grep python
|
||||||
|
|
||||||
|
# 无可用镜像时才拉取(默认 python:3.11-slim)
|
||||||
|
docker pull python:3.11-slim
|
||||||
|
|
||||||
|
# 创建并启动容器(映射当前目录到 /workspace)
|
||||||
|
docker run -d \
|
||||||
|
--name finance-talk \
|
||||||
|
-v $(pwd):/workspace \
|
||||||
|
-w /workspace \
|
||||||
|
python:3.13.11-slim \
|
||||||
|
sleep infinity
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 安装依赖包
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec finance-talk pip install tushare akshare
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 验证
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec finance-talk python -c "import tushare; import akshare; print('OK')"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 日常使用
|
||||||
|
|
||||||
|
Python 脚本通过 `docker exec` 在容器中执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec finance-talk python your_script.py
|
||||||
|
```
|
||||||
|
|
||||||
|
若容器已存在但未运行,先启动:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker start finance-talk
|
||||||
|
```
|
||||||
|
|
||||||
## Data Sources
|
## Data Sources
|
||||||
|
|
||||||
按优先级排序:
|
按优先级排序:
|
||||||
|
|||||||
@@ -0,0 +1,392 @@
|
|||||||
|
"""五大期货交易所前20会员成交持仓排名数据获取工具。
|
||||||
|
|
||||||
|
交易所对照:
|
||||||
|
- CZCE 郑商所 → get_rank_table_czce (玻璃FG、纯碱SA、甲醇MA等)
|
||||||
|
- SHFE 上期所 → get_shfe_rank_table (铜CU、铝AL、螺纹RB、原油SC等)
|
||||||
|
- DCE 大商所 → get_dce_rank_table (铁矿石I、豆粕M、棕榈P等)
|
||||||
|
- CFFEX 中金所 → get_cffex_rank_table (IF、IC、IH、T等)
|
||||||
|
- GFEX 广期所 → futures_gfex_position_rank (工业硅SI、碳酸锂LC等)
|
||||||
|
|
||||||
|
交易所公布规则:
|
||||||
|
- CZCE: 同时公布品种汇总 + 各合约明细
|
||||||
|
- SHFE/CFFEX: 只公布合约明细,品种汇总由函数自行加总
|
||||||
|
- DCE: 只公布品种排名
|
||||||
|
- GFEX: 公布合约明细
|
||||||
|
"""
|
||||||
|
|
||||||
|
import akshare as ak
|
||||||
|
import pandas as pd
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
EXCHANGE_CODE = {
|
||||||
|
"CZCE": "CZCE",
|
||||||
|
"SHFE": "SHFE",
|
||||||
|
"DCE": "DCE",
|
||||||
|
"CFFEX": "CFFEX",
|
||||||
|
"GFEX": "GFEX",
|
||||||
|
}
|
||||||
|
|
||||||
|
# 常见品种 -> 交易所映射
|
||||||
|
VARIETY_EXCHANGE = {
|
||||||
|
# 郑商所 CZCE
|
||||||
|
"FG": "CZCE", "SA": "CZCE", "MA": "CZCE", "TA": "CZCE",
|
||||||
|
"RM": "CZCE", "OI": "CZCE", "ZC": "CZCE", "SF": "CZCE",
|
||||||
|
"SM": "CZCE", "UR": "CZCE", "PF": "CZCE", "PX": "CZCE",
|
||||||
|
"SH": "CZCE", "PR": "CZCE", "PL": "CZCE", "AP": "CZCE",
|
||||||
|
"CJ": "CZCE", "CY": "CZCE", "PK": "CZCE", "CF": "CZCE",
|
||||||
|
"SR": "CZCE",
|
||||||
|
# 上期所 SHFE
|
||||||
|
"CU": "SHFE", "AL": "SHFE", "ZN": "SHFE", "PB": "SHFE",
|
||||||
|
"NI": "SHFE", "SN": "SHFE", "AU": "SHFE", "AG": "SHFE",
|
||||||
|
"RB": "SHFE", "HC": "SHFE", "WR": "SHFE", "FU": "SHFE",
|
||||||
|
"BU": "SHFE", "RU": "SHFE", "SC": "SHFE", "NR": "SHFE",
|
||||||
|
"SP": "SHFE", "SS": "SHFE", "LU": "SHFE", "BC": "SHFE",
|
||||||
|
"AO": "SHFE", "BR": "SHFE", "EC": "SHFE",
|
||||||
|
# 大商所 DCE
|
||||||
|
"C": "DCE", "CS": "DCE", "A": "DCE", "B": "DCE",
|
||||||
|
"M": "DCE", "Y": "DCE", "P": "DCE", "FB": "DCE",
|
||||||
|
"BB": "DCE", "JD": "DCE", "L": "DCE", "V": "DCE",
|
||||||
|
"PP": "DCE", "J": "DCE", "JM": "DCE", "I": "DCE",
|
||||||
|
"EG": "DCE", "RR": "DCE", "EB": "DCE", "PG": "DCE",
|
||||||
|
"LH": "DCE", "LG": "DCE", "BZ": "DCE",
|
||||||
|
# 中金所 CFFEX
|
||||||
|
"IF": "CFFEX", "IC": "CFFEX", "IH": "CFFEX", "IM": "CFFEX",
|
||||||
|
"T": "CFFEX", "TF": "CFFEX", "TS": "CFFEX", "TL": "CFFEX",
|
||||||
|
# 广期所 GFEX
|
||||||
|
"SI": "GFEX", "LC": "GFEX", "PS": "GFEX",
|
||||||
|
}
|
||||||
|
|
||||||
|
# 各交易所的品种代码列表(用于 get_rank_sum)
|
||||||
|
ALL_VARS_CZCE = ["WH","PM","CF","SR","TA","OI","RI","MA","FG","RS","RM","ZC",
|
||||||
|
"JR","LR","SF","SM","WT","TC","GN","RO","ER","SRX","SRY",
|
||||||
|
"WSX","WSY","CY","AP","UR","CJ","SA","PK","PF","PX","SH","PR","PL"]
|
||||||
|
ALL_VARS_DCE = ["C","CS","A","B","M","Y","P","FB","BB","JD","L","V","PP",
|
||||||
|
"J","JM","I","EG","RR","EB","PG","LH","LG","BZ"]
|
||||||
|
ALL_VARS_SHFE = ["CU","AL","ZN","PB","NI","SN","AU","AG","RB","WR","HC",
|
||||||
|
"FU","BU","RU","SC","NR","SP","SS","LU","BC","AO","BR","EC"]
|
||||||
|
ALL_VARS_CFFEX = ["IF","IC","IM","IH","T","TF","TS","TL"]
|
||||||
|
ALL_VARS_GFEX = ["SI","LC","PS"]
|
||||||
|
|
||||||
|
# 没有统一字段名映射时用的 to_int 辅助
|
||||||
|
def _to_int(x):
|
||||||
|
if isinstance(x, str):
|
||||||
|
return int(x.replace(",", ""))
|
||||||
|
return int(x)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 底层接口,按交易所获取当日前20排名
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def fetch_top20(date: str = None, exchange: str = None, variety: str = None) -> pd.DataFrame:
|
||||||
|
"""获取指定交易日该品种的前20会员成交持仓排名。
|
||||||
|
|
||||||
|
主力合约检测调用 :func:`main_contract`,本函数自动定位。
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
date : str or None
|
||||||
|
交易日,格式 YYYY-MM-DD 或 YYYYMMDD。None 表示最新。
|
||||||
|
exchange : str or None
|
||||||
|
交易所代码(CZCE/SHFE/DCE/CFFEX/GFEX)。None 时从 variety 推算。
|
||||||
|
variety : str
|
||||||
|
品种代码(如 "FG", "RB", "I", "IF")。如果为 None 则返回全品种。
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
pd.DataFrame
|
||||||
|
前20会员排名数据。
|
||||||
|
列:rank, party_name, vol, vol_chg,
|
||||||
|
long_party_name, long_open_interest, long_open_interest_chg,
|
||||||
|
short_party_name, short_open_interest, short_open_interest_chg,
|
||||||
|
symbol, exchange
|
||||||
|
"""
|
||||||
|
if exchange is None and variety is not None:
|
||||||
|
exchange = VARIETY_EXCHANGE.get(variety.upper())
|
||||||
|
if exchange is None:
|
||||||
|
raise ValueError(f"未知品种 {variety},请指定 exchange 参数")
|
||||||
|
|
||||||
|
if exchange == "CZCE":
|
||||||
|
return _fetch_czce(date, variety)
|
||||||
|
elif exchange == "SHFE":
|
||||||
|
return _fetch_shfe(date, variety)
|
||||||
|
elif exchange == "DCE":
|
||||||
|
return _fetch_dce(date, variety)
|
||||||
|
elif exchange == "CFFEX":
|
||||||
|
return _fetch_cffex(date, variety)
|
||||||
|
elif exchange == "GFEX":
|
||||||
|
return _fetch_gfex(date, variety)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"不支持的交易所: {exchange}")
|
||||||
|
|
||||||
|
|
||||||
|
def _fetch_czce(date=None, variety=None):
|
||||||
|
rank = ak.get_rank_table_czce(date=date)
|
||||||
|
if variety:
|
||||||
|
variety = variety.upper()
|
||||||
|
if variety in rank:
|
||||||
|
# 品种级别汇总
|
||||||
|
return _normalize_czce(rank[variety], variety, "CZCE")
|
||||||
|
# 合约级别: 找到该品种的所有合约
|
||||||
|
contract_keys = [k for k in rank if k.startswith(variety) and k != variety]
|
||||||
|
if not contract_keys:
|
||||||
|
raise ValueError(f"未找到品种 {variety} 的数据")
|
||||||
|
# 返回汇总版
|
||||||
|
return _normalize_czce(rank.get(variety, pd.DataFrame()), variety, "CZCE")
|
||||||
|
else:
|
||||||
|
# 全部品种 - 返回汇总
|
||||||
|
return rank # dict, 调用方自己处理
|
||||||
|
|
||||||
|
|
||||||
|
def _fetch_shfe(date=None, variety=None):
|
||||||
|
vars_list = [variety.upper()] if variety else None
|
||||||
|
rank = ak.get_shfe_rank_table(date=date, vars_list=vars_list)
|
||||||
|
# SHFE 返回 dict: {合约代码: DataFrame}
|
||||||
|
rows = []
|
||||||
|
for symbol, df in rank.items():
|
||||||
|
for _, r in df.iterrows():
|
||||||
|
rows.append({
|
||||||
|
"rank": int(r["名次"]),
|
||||||
|
"party_name": r["会员简称"],
|
||||||
|
"vol": r["成交量"],
|
||||||
|
"vol_chg": r["成交量增减"],
|
||||||
|
"long_party_name": r["持买单量-会员简称"],
|
||||||
|
"long_open_interest": r["持买单量-合计"],
|
||||||
|
"long_open_interest_chg": r["持买单量-增减"],
|
||||||
|
"short_party_name": r["持卖单量-会员简称"],
|
||||||
|
"short_open_interest": r["持卖单量-合计"],
|
||||||
|
"short_open_interest_chg": r["持卖单量-增减"],
|
||||||
|
"symbol": symbol,
|
||||||
|
})
|
||||||
|
df = pd.DataFrame(rows)
|
||||||
|
if variety:
|
||||||
|
df = df[df["symbol"].str.startswith(variety.upper())]
|
||||||
|
return df
|
||||||
|
|
||||||
|
|
||||||
|
def _fetch_dce(date=None, variety=None):
|
||||||
|
vars_list = [variety.upper()] if variety else None
|
||||||
|
rank = ak.get_dce_rank_table(date=date, vars_list=vars_list)
|
||||||
|
rows = []
|
||||||
|
for symbol, df in rank.items():
|
||||||
|
for _, r in df.iterrows():
|
||||||
|
rows.append({
|
||||||
|
"rank": int(r["名次"]),
|
||||||
|
"party_name": r["会员简称"],
|
||||||
|
"vol": r["成交量"],
|
||||||
|
"vol_chg": r["成交量增减"],
|
||||||
|
"long_party_name": r["持买单-会员简称"],
|
||||||
|
"long_open_interest": r["持买单"],
|
||||||
|
"long_open_interest_chg": r["持买单增减"],
|
||||||
|
"short_party_name": r["持卖单-会员简称"],
|
||||||
|
"short_open_interest": r["持卖单"],
|
||||||
|
"short_open_interest_chg": r["持卖单增减"],
|
||||||
|
"symbol": symbol,
|
||||||
|
})
|
||||||
|
return pd.DataFrame(rows)
|
||||||
|
|
||||||
|
|
||||||
|
def _fetch_cffex(date=None, variety=None):
|
||||||
|
vars_list = [variety.upper()] if variety else None
|
||||||
|
rank = ak.get_cffex_rank_table(date=date, vars_list=vars_list)
|
||||||
|
rows = []
|
||||||
|
for symbol, df in rank.items():
|
||||||
|
for _, r in df.iterrows():
|
||||||
|
rows.append({
|
||||||
|
"rank": int(r["名次"]),
|
||||||
|
"party_name": r["会员简称"],
|
||||||
|
"vol": r["成交量"],
|
||||||
|
"vol_chg": r["成交量增减"],
|
||||||
|
"long_party_name": r["持买单-会员简称"],
|
||||||
|
"long_open_interest": r["持买单-合计"],
|
||||||
|
"long_open_interest_chg": r["持买单-增减"],
|
||||||
|
"short_party_name": r["持卖单-会员简称"],
|
||||||
|
"short_open_interest": r["持卖单-合计"],
|
||||||
|
"short_open_interest_chg": r["持卖单-增减"],
|
||||||
|
"symbol": symbol,
|
||||||
|
})
|
||||||
|
return pd.DataFrame(rows)
|
||||||
|
|
||||||
|
|
||||||
|
def _fetch_gfex(date=None, variety=None):
|
||||||
|
vars_list = [variety.upper()] if variety else None
|
||||||
|
rank = ak.futures_gfex_position_rank(date=date, vars_list=vars_list)
|
||||||
|
rows = []
|
||||||
|
if isinstance(rank, dict):
|
||||||
|
for symbol, df in rank.items():
|
||||||
|
for _, r in df.iterrows():
|
||||||
|
rows.append({
|
||||||
|
"rank": int(r["名次"]),
|
||||||
|
"party_name": r["会员简称"],
|
||||||
|
"vol": r["成交量"],
|
||||||
|
"vol_chg": r["成交量增减"],
|
||||||
|
"long_party_name": r["持买单-会员简称"],
|
||||||
|
"long_open_interest": r["持买单-合计"],
|
||||||
|
"long_open_interest_chg": r["持买单-增减"],
|
||||||
|
"short_party_name": r["持卖单-会员简称"],
|
||||||
|
"short_open_interest": r["持卖单-合计"],
|
||||||
|
"short_open_interest_chg": r["持卖单-增减"],
|
||||||
|
"symbol": symbol,
|
||||||
|
})
|
||||||
|
return pd.DataFrame(rows)
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_czce(df, variety, exchange):
|
||||||
|
"""标准化 CZCE 品种汇总 DataFrame 字段名"""
|
||||||
|
df = df.copy()
|
||||||
|
df["symbol"] = df.get("symbol", variety)
|
||||||
|
df["exchange"] = exchange
|
||||||
|
return df
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 主力合约检测
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def main_contract(variety: str, date: str = None) -> str:
|
||||||
|
"""检测该品种在指定交易日的主力合约代码。
|
||||||
|
|
||||||
|
逻辑:成交量/持仓量最大的合约即为主力。
|
||||||
|
"""
|
||||||
|
variety = variety.upper()
|
||||||
|
exchange = VARIETY_EXCHANGE.get(variety)
|
||||||
|
if exchange is None:
|
||||||
|
raise ValueError(f"未知品种 {variety}")
|
||||||
|
|
||||||
|
rank = ak.get_rank_table_czce(date=date) if exchange == "CZCE" else {}
|
||||||
|
if exchange == "CZCE":
|
||||||
|
# CZCE: 合约级别的 key 是 品种+年月 (FG609), 品种级是 FG
|
||||||
|
contract_keys = [k for k in rank if k.startswith(variety) and k != variety]
|
||||||
|
if not contract_keys:
|
||||||
|
raise ValueError(f"未找到品种 {variety} 的合约数据")
|
||||||
|
vols = {}
|
||||||
|
for ck in contract_keys:
|
||||||
|
df = rank[ck]
|
||||||
|
vol_sum = df["vol"].apply(_to_int).sum()
|
||||||
|
vols[ck] = vol_sum
|
||||||
|
return max(vols, key=vols.get)
|
||||||
|
else:
|
||||||
|
# 其他交易所:通过成交额最大的标的判断
|
||||||
|
raise NotImplementedError(f"{exchange} 主力合约检测待实现")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 品种汇总对比(多交易所多品种)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def rank_summary(date: str = None, varieties: list = None) -> pd.DataFrame:
|
||||||
|
"""多品种前N会员成交量/持仓汇总对比(仅支持 CZCE 品种)。
|
||||||
|
|
||||||
|
使用 get_rank_table_czce 的品种级别汇总数据,
|
||||||
|
返回各品种前5/10/15/20成交量和多空持仓总和。
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
date : str
|
||||||
|
交易日。
|
||||||
|
varieties : list
|
||||||
|
品种代码列表。默认 FG, SA, MA, CF, TA, UR, PF, SM。
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
pd.DataFrame
|
||||||
|
每行一个品种,含品种级前N成交量和多空持仓总和。
|
||||||
|
"""
|
||||||
|
if varieties is None:
|
||||||
|
varieties = ["FG", "SA", "MA", "CF", "TA", "UR", "PF", "SM"]
|
||||||
|
rank = ak.get_rank_table_czce(date=date)
|
||||||
|
rows = []
|
||||||
|
for var in varieties:
|
||||||
|
var = var.upper()
|
||||||
|
if var not in rank:
|
||||||
|
continue
|
||||||
|
df = rank[var]
|
||||||
|
df["vol_int"] = df["vol"].apply(_to_int)
|
||||||
|
df["long_int"] = df["long_open_interest"].apply(_to_int)
|
||||||
|
df["short_int"] = df["short_open_interest"].apply(_to_int)
|
||||||
|
total_vol = df["vol_int"].sum()
|
||||||
|
vol_5 = df.head(5)["vol_int"].sum()
|
||||||
|
vol_10 = df.head(10)["vol_int"].sum()
|
||||||
|
long_sum = df["long_int"].sum()
|
||||||
|
short_sum = df["short_int"].sum()
|
||||||
|
rows.append({
|
||||||
|
"var": var,
|
||||||
|
"vol_total": total_vol,
|
||||||
|
"vol_top5": vol_5,
|
||||||
|
"vol_top10": vol_10,
|
||||||
|
"vol_top20": df["vol_int"].sum(),
|
||||||
|
"long_sum": long_sum,
|
||||||
|
"short_sum": short_sum,
|
||||||
|
"net": long_sum - short_sum,
|
||||||
|
})
|
||||||
|
return pd.DataFrame(rows).sort_values("vol_total", ascending=False)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 主力合约前20成交量
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def main_top20(variety: str, date: str = None) -> pd.DataFrame:
|
||||||
|
"""获取品种主力合约的前20会员成交量排名。
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
variety : str
|
||||||
|
品种代码,如 "FG"。
|
||||||
|
date : str
|
||||||
|
交易日。None 表示最新。
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
pd.DataFrame
|
||||||
|
主力合约前20会员成交量排名 + 多空持仓排名。
|
||||||
|
"""
|
||||||
|
exchange = VARIETY_EXCHANGE.get(variety.upper())
|
||||||
|
if exchange != "CZCE":
|
||||||
|
raise NotImplementedError(f"目前仅支持 CZCE 品种的主力合约前20查询")
|
||||||
|
|
||||||
|
rank = ak.get_rank_table_czce(date=date)
|
||||||
|
contract_keys = [k for k in rank if k.startswith(variety.upper()) and k != variety.upper()]
|
||||||
|
vols = {ck: rank[ck]["vol"].apply(_to_int).sum() for ck in contract_keys}
|
||||||
|
main_code = max(vols, key=vols.get)
|
||||||
|
|
||||||
|
df = rank[main_code].copy()
|
||||||
|
df["symbol"] = main_code
|
||||||
|
# 数值列处理(CZCE 返回字符串含逗号)
|
||||||
|
for col in ["vol", "vol_chg", "long_open_interest", "long_open_interest_chg",
|
||||||
|
"short_open_interest", "short_open_interest_chg"]:
|
||||||
|
df[col] = df[col].apply(_to_int)
|
||||||
|
return df.sort_values("rank")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 输出格式化
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def print_top20(df: pd.DataFrame, title: str = ""):
|
||||||
|
"""打印前20成交量排名 + 多空前10。"""
|
||||||
|
if title:
|
||||||
|
print(f"\n{'='*60}")
|
||||||
|
print(f" {title}")
|
||||||
|
print(f"{'='*60}")
|
||||||
|
|
||||||
|
print(f"\n【成交量前20】")
|
||||||
|
print(f"{'排名':>4} {'会员':<20} {'成交量':>10} {'变化':>10}")
|
||||||
|
print("-" * 50)
|
||||||
|
for _, r in df.iterrows():
|
||||||
|
print(f"{int(r['rank']):>4} {str(r['vol_party_name']):<20} "
|
||||||
|
f"{int(r['vol']):>10,} {int(r['vol_chg']):>10,}")
|
||||||
|
|
||||||
|
print(f"\n【持多单前10】")
|
||||||
|
print(f"{'排名':>4} {'会员':<20} {'多单':>10} {'变化':>10}")
|
||||||
|
print("-" * 50)
|
||||||
|
for _, r in df.head(10).iterrows():
|
||||||
|
print(f"{int(r['rank']):>4} {str(r['long_party_name']):<20} "
|
||||||
|
f"{int(r['long_open_interest']):>10,} {int(r['long_open_interest_chg']):>10,}")
|
||||||
|
|
||||||
|
print(f"\n【持空单前10】")
|
||||||
|
print(f"{'排名':>4} {'会员':<20} {'空单':>10} {'变化':>10}")
|
||||||
|
print("-" * 50)
|
||||||
|
for _, r in df.head(10).iterrows():
|
||||||
|
print(f"{int(r['rank']):>4} {str(r['short_party_name']):<20} "
|
||||||
|
f"{int(r['short_open_interest']):>10,} {int(r['short_open_interest_chg']):>10,}")
|
||||||
Reference in New Issue
Block a user