|
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
"""
|
|
|
|
|
|
期货结算单数据处理脚本
|
|
|
|
|
|
读取期货结算单Excel,提取期货和期权交易数据,计算盈亏和持仓,合并输出
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
import glob
|
|
|
|
|
|
import re
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
from collections import defaultdict
|
|
|
|
|
|
|
|
|
|
|
|
import xlrd
|
|
|
|
|
|
import pandas as pd
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def find_header(sheet, keywords):
|
|
|
|
|
|
"""
|
|
|
|
|
|
动态识别表头行
|
|
|
|
|
|
:param sheet: xlrd sheet对象
|
|
|
|
|
|
:param keywords: 关键词列表,如["合约", "品种合约"]
|
|
|
|
|
|
:return: 表头行索引
|
|
|
|
|
|
"""
|
|
|
|
|
|
for row_idx in range(min(20, sheet.nrows)): # 只扫描前20行
|
|
|
|
|
|
row_values = [str(sheet.cell_value(row_idx, col_idx)).strip()
|
|
|
|
|
|
for col_idx in range(sheet.ncols)]
|
|
|
|
|
|
row_text = ' '.join(row_values)
|
|
|
|
|
|
|
|
|
|
|
|
# 检查是否包含关键词
|
|
|
|
|
|
has_keyword = any(kw in row_text for kw in keywords)
|
|
|
|
|
|
# 检查是否包含辅助关键词
|
|
|
|
|
|
has_aux = any(aux in row_text for aux in ["买/卖", "成交价", "权利金"])
|
|
|
|
|
|
|
|
|
|
|
|
if has_keyword and has_aux:
|
|
|
|
|
|
return row_idx
|
|
|
|
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def read_sheet(xls_file, sheet_name, trade_type):
|
|
|
|
|
|
"""
|
|
|
|
|
|
读取单个Sheet的数据
|
|
|
|
|
|
:param xls_file: Excel文件路径
|
|
|
|
|
|
:param sheet_name: Sheet名称
|
|
|
|
|
|
:param trade_type: 交易类型(期货/期权)
|
|
|
|
|
|
:return: DataFrame
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
wb = xlrd.open_workbook(xls_file)
|
|
|
|
|
|
if sheet_name not in wb.sheet_names():
|
|
|
|
|
|
print(f" 警告: {xls_file} 中未找到 {sheet_name}")
|
|
|
|
|
|
return pd.DataFrame()
|
|
|
|
|
|
|
|
|
|
|
|
sheet = wb.sheet_by_name(sheet_name)
|
|
|
|
|
|
if sheet.nrows == 0:
|
|
|
|
|
|
return pd.DataFrame()
|
|
|
|
|
|
|
|
|
|
|
|
# 根据类型确定关键词
|
|
|
|
|
|
if trade_type == '期货':
|
|
|
|
|
|
keywords = ["合约"]
|
|
|
|
|
|
else: # 期权
|
|
|
|
|
|
keywords = ["品种合约"]
|
|
|
|
|
|
|
|
|
|
|
|
# 查找表头行
|
|
|
|
|
|
header_row = find_header(sheet, keywords)
|
|
|
|
|
|
if header_row is None:
|
|
|
|
|
|
print(f" 警告: {sheet_name} 中未找到表头")
|
|
|
|
|
|
return pd.DataFrame()
|
|
|
|
|
|
|
|
|
|
|
|
# 读取表头
|
|
|
|
|
|
headers = [str(sheet.cell_value(header_row, col_idx)).strip()
|
|
|
|
|
|
for col_idx in range(sheet.ncols)]
|
|
|
|
|
|
|
|
|
|
|
|
# 读取数据行
|
|
|
|
|
|
data = []
|
|
|
|
|
|
for row_idx in range(header_row + 1, sheet.nrows):
|
|
|
|
|
|
row_data = {}
|
|
|
|
|
|
first_cell = str(sheet.cell_value(row_idx, 0)).strip()
|
|
|
|
|
|
|
|
|
|
|
|
# 跳过合计行
|
|
|
|
|
|
if first_cell == '合计' or first_cell == '':
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
for col_idx, header in enumerate(headers):
|
|
|
|
|
|
if col_idx < sheet.ncols:
|
|
|
|
|
|
value = sheet.cell_value(row_idx, col_idx)
|
|
|
|
|
|
# 处理"--"
|
|
|
|
|
|
if str(value).strip() == '--':
|
|
|
|
|
|
value = None
|
|
|
|
|
|
row_data[header] = value
|
|
|
|
|
|
|
|
|
|
|
|
# 添加类型标签
|
|
|
|
|
|
row_data['类型'] = trade_type
|
|
|
|
|
|
data.append(row_data)
|
|
|
|
|
|
|
|
|
|
|
|
df = pd.DataFrame(data)
|
|
|
|
|
|
print(f" 读取 {sheet_name}: {len(df)} 条记录")
|
|
|
|
|
|
return df
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f" 错误: 读取 {sheet_name} 失败 - {e}")
|
|
|
|
|
|
return pd.DataFrame()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def extract_variety(contract):
|
|
|
|
|
|
"""
|
|
|
|
|
|
提取品种代码(所有前导字母)
|
|
|
|
|
|
:param contract: 合约代码
|
|
|
|
|
|
:return: 品种代码
|
|
|
|
|
|
"""
|
|
|
|
|
|
if pd.isna(contract) or not contract:
|
|
|
|
|
|
return ''
|
|
|
|
|
|
contract = str(contract).strip()
|
|
|
|
|
|
match = re.match(r'^([A-Za-z]+)', contract)
|
|
|
|
|
|
if match:
|
|
|
|
|
|
return match.group(1).upper()
|
|
|
|
|
|
return contract[:2].upper()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def safe_float(value, default=0.0):
|
|
|
|
|
|
"""
|
|
|
|
|
|
安全转换为float
|
|
|
|
|
|
"""
|
|
|
|
|
|
if pd.isna(value) or value is None or value == '':
|
|
|
|
|
|
return default
|
|
|
|
|
|
try:
|
|
|
|
|
|
return float(value)
|
|
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
|
|
return default
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def calc_pnl_futures(df):
|
|
|
|
|
|
"""
|
|
|
|
|
|
计算期货盈亏
|
|
|
|
|
|
"""
|
|
|
|
|
|
if df.empty:
|
|
|
|
|
|
return {'平仓盈亏': 0, '手续费': 0, '净盈亏': 0}
|
|
|
|
|
|
|
|
|
|
|
|
# 平仓盈亏
|
|
|
|
|
|
pnl_close = 0
|
|
|
|
|
|
if '平仓盈亏' in df.columns:
|
|
|
|
|
|
for val in df['平仓盈亏']:
|
|
|
|
|
|
if not pd.isna(val):
|
|
|
|
|
|
pnl_close += safe_float(val)
|
|
|
|
|
|
|
|
|
|
|
|
# 手续费
|
|
|
|
|
|
commission = 0
|
|
|
|
|
|
if '手续费' in df.columns:
|
|
|
|
|
|
for val in df['手续费']:
|
|
|
|
|
|
commission += safe_float(val)
|
|
|
|
|
|
|
|
|
|
|
|
net_pnl = pnl_close - commission
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
'平仓盈亏': pnl_close,
|
|
|
|
|
|
'手续费': commission,
|
|
|
|
|
|
'净盈亏': net_pnl
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def calc_pnl_options(df):
|
|
|
|
|
|
"""
|
|
|
|
|
|
计算期权盈亏
|
|
|
|
|
|
"""
|
|
|
|
|
|
if df.empty:
|
|
|
|
|
|
return {'权利金净收入': 0, '手续费': 0, '净盈亏': 0}
|
|
|
|
|
|
|
|
|
|
|
|
# 权利金(带方向)
|
|
|
|
|
|
premium_total = 0
|
|
|
|
|
|
if '权利金' in df.columns:
|
|
|
|
|
|
for val in df['权利金']:
|
|
|
|
|
|
premium_total += safe_float(val)
|
|
|
|
|
|
|
|
|
|
|
|
# 手续费
|
|
|
|
|
|
commission = 0
|
|
|
|
|
|
if '手续费' in df.columns:
|
|
|
|
|
|
for val in df['手续费']:
|
|
|
|
|
|
commission += safe_float(val)
|
|
|
|
|
|
|
|
|
|
|
|
net_pnl = premium_total - commission
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
'权利金净收入': premium_total,
|
|
|
|
|
|
'手续费': commission,
|
|
|
|
|
|
'净盈亏': net_pnl
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def calc_position_futures(df):
|
|
|
|
|
|
"""
|
|
|
|
|
|
计算期货持仓
|
|
|
|
|
|
"""
|
|
|
|
|
|
positions = defaultdict(float)
|
|
|
|
|
|
|
|
|
|
|
|
if df.empty:
|
|
|
|
|
|
return positions
|
|
|
|
|
|
|
|
|
|
|
|
for _, row in df.iterrows():
|
|
|
|
|
|
contract = str(row.get('合约', '')).strip()
|
|
|
|
|
|
if not contract or contract == '合计':
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
bs_flag = str(row.get('买/卖', '')).strip()
|
|
|
|
|
|
oc_flag = str(row.get('开/平', '')).strip()
|
|
|
|
|
|
volume = safe_float(row.get('手数', 0))
|
|
|
|
|
|
|
|
|
|
|
|
# 买+开 → +手数;卖+开 → -手数;买+平 → -手数;卖+平 → +手数
|
|
|
|
|
|
if '买' in bs_flag and '开' in oc_flag:
|
|
|
|
|
|
positions[contract] += volume
|
|
|
|
|
|
elif '卖' in bs_flag and '开' in oc_flag:
|
|
|
|
|
|
positions[contract] -= volume
|
|
|
|
|
|
elif '买' in bs_flag and '平' in oc_flag:
|
|
|
|
|
|
positions[contract] -= volume
|
|
|
|
|
|
elif '卖' in bs_flag and '平' in oc_flag:
|
|
|
|
|
|
positions[contract] += volume
|
|
|
|
|
|
|
|
|
|
|
|
# 过滤0持仓
|
|
|
|
|
|
return {k: v for k, v in positions.items() if abs(v) > 0.001}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def calc_position_options(df):
|
|
|
|
|
|
"""
|
|
|
|
|
|
计算期权持仓
|
|
|
|
|
|
"""
|
|
|
|
|
|
positions = defaultdict(float)
|
|
|
|
|
|
|
|
|
|
|
|
if df.empty:
|
|
|
|
|
|
return positions
|
|
|
|
|
|
|
|
|
|
|
|
for _, row in df.iterrows():
|
|
|
|
|
|
contract = str(row.get('品种合约', '')).strip()
|
|
|
|
|
|
if not contract or contract == '合计':
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
bs_flag = str(row.get('买/卖', '')).strip()
|
|
|
|
|
|
volume = safe_float(row.get('成交量', 0))
|
|
|
|
|
|
|
|
|
|
|
|
if '买' in bs_flag:
|
|
|
|
|
|
positions[contract] += volume
|
|
|
|
|
|
elif '卖' in bs_flag:
|
|
|
|
|
|
positions[contract] -= volume
|
|
|
|
|
|
|
|
|
|
|
|
# 过滤0持仓
|
|
|
|
|
|
return {k: v for k, v in positions.items() if abs(v) > 0.001}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def merge_trades(df_futures, df_options):
|
|
|
|
|
|
"""
|
|
|
|
|
|
合并期货和期权交易记录
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 统一字段
|
|
|
|
|
|
columns_map_futures = {
|
|
|
|
|
|
'合约': '合约代码',
|
|
|
|
|
|
'买/卖': '买卖',
|
|
|
|
|
|
'成交价': '价格/权利金单价',
|
|
|
|
|
|
'手数': '手数/成交量',
|
|
|
|
|
|
'成交额': '金额/权利金',
|
|
|
|
|
|
'实际成交日期': '实际成交日期',
|
|
|
|
|
|
'成交时间': '成交时间'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
columns_map_options = {
|
|
|
|
|
|
'品种合约': '合约代码',
|
|
|
|
|
|
'买/卖': '买卖',
|
|
|
|
|
|
'权利金单价': '价格/权利金单价',
|
|
|
|
|
|
'成交量': '手数/成交量',
|
|
|
|
|
|
'权利金': '金额/权利金',
|
|
|
|
|
|
'成交日期': '实际成交日期',
|
|
|
|
|
|
'成交时间': '成交时间'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# 处理期货数据
|
|
|
|
|
|
if not df_futures.empty:
|
|
|
|
|
|
df_f = df_futures.rename(columns=columns_map_futures).copy()
|
|
|
|
|
|
df_f['品种'] = df_f['合约代码'].apply(extract_variety)
|
|
|
|
|
|
df_f['方向'] = df_f['买卖'].apply(lambda x: '买' if '买' in str(x) else '卖')
|
|
|
|
|
|
df_f['开平仓'] = df_f.get('开/平', '')
|
|
|
|
|
|
|
|
|
|
|
|
# 确保必要字段存在
|
|
|
|
|
|
for col in ['平仓盈亏', '手续费']:
|
|
|
|
|
|
if col not in df_f.columns:
|
|
|
|
|
|
df_f[col] = 0
|
|
|
|
|
|
else:
|
|
|
|
|
|
df_f = pd.DataFrame()
|
|
|
|
|
|
|
|
|
|
|
|
# 处理期权数据
|
|
|
|
|
|
if not df_options.empty:
|
|
|
|
|
|
df_o = df_options.rename(columns=columns_map_options).copy()
|
|
|
|
|
|
df_o['品种'] = df_o['合约代码'].apply(extract_variety)
|
|
|
|
|
|
df_o['方向'] = df_o['买卖'].apply(lambda x: '买' if '买' in str(x) else '卖')
|
|
|
|
|
|
df_o['开平仓'] = ''
|
|
|
|
|
|
df_o['平仓盈亏'] = 0
|
|
|
|
|
|
|
|
|
|
|
|
# 确保必要字段存在
|
|
|
|
|
|
for col in ['手续费']:
|
|
|
|
|
|
if col not in df_o.columns:
|
|
|
|
|
|
df_o[col] = 0
|
|
|
|
|
|
else:
|
|
|
|
|
|
df_o = pd.DataFrame()
|
|
|
|
|
|
|
|
|
|
|
|
# 合并
|
|
|
|
|
|
common_cols = ['品种', '合约代码', '类型', '买卖', '方向', '成交时间',
|
|
|
|
|
|
'实际成交日期', '价格/权利金单价', '手数/成交量', '金额/权利金',
|
|
|
|
|
|
'手续费', '平仓盈亏', '开平仓']
|
|
|
|
|
|
|
|
|
|
|
|
if not df_f.empty:
|
|
|
|
|
|
df_f = df_f[[c for c in common_cols if c in df_f.columns]]
|
|
|
|
|
|
if not df_o.empty:
|
|
|
|
|
|
df_o = df_o[[c for c in common_cols if c in df_o.columns]]
|
|
|
|
|
|
|
|
|
|
|
|
if not df_f.empty and not df_o.empty:
|
|
|
|
|
|
return pd.concat([df_f, df_o], ignore_index=True)
|
|
|
|
|
|
elif not df_f.empty:
|
|
|
|
|
|
return df_f
|
|
|
|
|
|
elif not df_o.empty:
|
|
|
|
|
|
return df_o
|
|
|
|
|
|
else:
|
|
|
|
|
|
return pd.DataFrame()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def save_output(trades_df, df_futures, df_options, output_dir):
|
|
|
|
|
|
"""
|
|
|
|
|
|
保存输出文件
|
|
|
|
|
|
"""
|
|
|
|
|
|
os.makedirs(output_dir, exist_ok=True)
|
|
|
|
|
|
|
|
|
|
|
|
# 文件1:trades_merged.csv
|
|
|
|
|
|
csv_path = os.path.join(output_dir, 'trades_merged.csv')
|
|
|
|
|
|
if not trades_df.empty:
|
|
|
|
|
|
trades_df.to_csv(csv_path, index=False, encoding='utf-8-sig')
|
|
|
|
|
|
print(f"已保存交易明细: {csv_path}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
print("警告: 无交易数据")
|
|
|
|
|
|
|
|
|
|
|
|
# 计算分析数据
|
|
|
|
|
|
analysis_data = {}
|
|
|
|
|
|
|
|
|
|
|
|
# Sheet1: 品种盈亏汇总
|
|
|
|
|
|
variety_summary = []
|
|
|
|
|
|
|
|
|
|
|
|
# 期货品种汇总
|
|
|
|
|
|
if not df_futures.empty:
|
|
|
|
|
|
df_f = df_futures.copy()
|
|
|
|
|
|
df_f['品种'] = df_f['合约'].apply(extract_variety)
|
|
|
|
|
|
|
|
|
|
|
|
for variety, group in df_f.groupby('品种'):
|
|
|
|
|
|
pnl = calc_pnl_futures(group)
|
|
|
|
|
|
total_volume = sum(safe_float(v) for v in group.get('手数', []))
|
|
|
|
|
|
total_amount = sum(safe_float(v) for v in group.get('成交额', []))
|
|
|
|
|
|
buy_count = sum(1 for v in group.get('买/卖', []) if '买' in str(v))
|
|
|
|
|
|
sell_count = sum(1 for v in group.get('买/卖', []) if '卖' in str(v))
|
|
|
|
|
|
|
|
|
|
|
|
variety_summary.append({
|
|
|
|
|
|
'品种': variety,
|
|
|
|
|
|
'类型': '期货',
|
|
|
|
|
|
'总手数': total_volume,
|
|
|
|
|
|
'总成交额': total_amount,
|
|
|
|
|
|
'总手续费': pnl['手续费'],
|
|
|
|
|
|
'平仓盈亏': pnl['平仓盈亏'],
|
|
|
|
|
|
'净盈亏': pnl['净盈亏'],
|
|
|
|
|
|
'买入次数': buy_count,
|
|
|
|
|
|
'卖出次数': sell_count
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
# 期权品种汇总
|
|
|
|
|
|
if not df_options.empty:
|
|
|
|
|
|
df_o = df_options.copy()
|
|
|
|
|
|
df_o['品种'] = df_o['品种合约'].apply(extract_variety)
|
|
|
|
|
|
|
|
|
|
|
|
for variety, group in df_o.groupby('品种'):
|
|
|
|
|
|
pnl = calc_pnl_options(group)
|
|
|
|
|
|
total_volume = sum(safe_float(v) for v in group.get('成交量', []))
|
|
|
|
|
|
total_premium = sum(abs(safe_float(v)) for v in group.get('权利金', []))
|
|
|
|
|
|
buy_count = sum(1 for v in group.get('买/卖', []) if '买' in str(v))
|
|
|
|
|
|
sell_count = sum(1 for v in group.get('买/卖', []) if '卖' in str(v))
|
|
|
|
|
|
|
|
|
|
|
|
variety_summary.append({
|
|
|
|
|
|
'品种': variety,
|
|
|
|
|
|
'类型': '期权',
|
|
|
|
|
|
'总手数': total_volume,
|
|
|
|
|
|
'总成交额/权利金': total_premium,
|
|
|
|
|
|
'总手续费': pnl['手续费'],
|
|
|
|
|
|
'平仓盈亏': 0,
|
|
|
|
|
|
'净盈亏': pnl['净盈亏'],
|
|
|
|
|
|
'买入次数': buy_count,
|
|
|
|
|
|
'卖出次数': sell_count
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
df_variety = pd.DataFrame(variety_summary)
|
|
|
|
|
|
|
|
|
|
|
|
# Sheet2: 期货持仓
|
|
|
|
|
|
futures_positions = calc_position_futures(df_futures)
|
|
|
|
|
|
futures_pos_data = []
|
|
|
|
|
|
for contract, pos in futures_positions.items():
|
|
|
|
|
|
futures_pos_data.append({
|
|
|
|
|
|
'合约': contract,
|
|
|
|
|
|
'净持仓': pos,
|
|
|
|
|
|
'方向': '多' if pos > 0 else '空'
|
|
|
|
|
|
})
|
|
|
|
|
|
df_futures_pos = pd.DataFrame(futures_pos_data)
|
|
|
|
|
|
|
|
|
|
|
|
# Sheet3: 期权持仓
|
|
|
|
|
|
options_positions = calc_position_options(df_options)
|
|
|
|
|
|
options_pos_data = []
|
|
|
|
|
|
for contract, pos in options_positions.items():
|
|
|
|
|
|
options_pos_data.append({
|
|
|
|
|
|
'品种合约': contract,
|
|
|
|
|
|
'净持仓': pos,
|
|
|
|
|
|
'方向': '权利' if pos > 0 else '义务'
|
|
|
|
|
|
})
|
|
|
|
|
|
df_options_pos = pd.DataFrame(options_pos_data)
|
|
|
|
|
|
|
|
|
|
|
|
# 文件2:analysis.xlsx
|
|
|
|
|
|
xlsx_path = os.path.join(output_dir, 'analysis.xlsx')
|
|
|
|
|
|
with pd.ExcelWriter(xlsx_path, engine='openpyxl') as writer:
|
|
|
|
|
|
if not df_variety.empty:
|
|
|
|
|
|
df_variety.to_excel(writer, sheet_name='品种盈亏汇总', index=False)
|
|
|
|
|
|
if not df_futures_pos.empty:
|
|
|
|
|
|
df_futures_pos.to_excel(writer, sheet_name='期货持仓', index=False)
|
|
|
|
|
|
if not df_options_pos.empty:
|
|
|
|
|
|
df_options_pos.to_excel(writer, sheet_name='期权持仓', index=False)
|
|
|
|
|
|
|
|
|
|
|
|
print(f"已保存分析结果: {xlsx_path}")
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
'variety_summary': variety_summary,
|
|
|
|
|
|
'futures_positions': futures_positions,
|
|
|
|
|
|
'options_positions': options_positions
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def print_summary(df_futures, df_options, results):
|
|
|
|
|
|
"""
|
|
|
|
|
|
打印摘要
|
|
|
|
|
|
"""
|
|
|
|
|
|
print("\n" + "="*50)
|
|
|
|
|
|
print("=== 交易摘要 ===")
|
|
|
|
|
|
|
|
|
|
|
|
# 期货统计
|
|
|
|
|
|
if not df_futures.empty:
|
|
|
|
|
|
pnl_f = calc_pnl_futures(df_futures)
|
|
|
|
|
|
varieties_f = df_futures['合约'].apply(extract_variety).nunique()
|
|
|
|
|
|
print(f"期货:{len(df_futures)}笔交易,{varieties_f}个品种")
|
|
|
|
|
|
print(f" 总盈亏:{pnl_f['净盈亏']:.2f}元(含手续费{pnl_f['手续费']:.2f}元)")
|
|
|
|
|
|
else:
|
|
|
|
|
|
print("期货:无交易")
|
|
|
|
|
|
|
|
|
|
|
|
# 期权统计
|
|
|
|
|
|
if not df_options.empty:
|
|
|
|
|
|
pnl_o = calc_pnl_options(df_options)
|
|
|
|
|
|
varieties_o = df_options['品种合约'].apply(extract_variety).nunique()
|
|
|
|
|
|
print(f"期权:{len(df_options)}笔交易,{varieties_o}个品种")
|
|
|
|
|
|
print(f" 总盈亏:{pnl_o['净盈亏']:.2f}元(含手续费{pnl_o['手续费']:.2f}元)")
|
|
|
|
|
|
else:
|
|
|
|
|
|
print("期权:无交易")
|
|
|
|
|
|
|
|
|
|
|
|
# 持仓统计
|
|
|
|
|
|
print("\n=== 收盘持仓 ===")
|
|
|
|
|
|
|
|
|
|
|
|
futures_pos = results['futures_positions']
|
|
|
|
|
|
if futures_pos:
|
|
|
|
|
|
for contract, pos in sorted(futures_pos.items()):
|
|
|
|
|
|
direction = '多' if pos > 0 else '空'
|
|
|
|
|
|
print(f"期货:{contract} {direction} {abs(pos):.0f}手")
|
|
|
|
|
|
else:
|
|
|
|
|
|
print("期货:无持仓")
|
|
|
|
|
|
|
|
|
|
|
|
options_pos = results['options_positions']
|
|
|
|
|
|
if options_pos:
|
|
|
|
|
|
for contract, pos in sorted(options_pos.items()):
|
|
|
|
|
|
direction = '权利' if pos > 0 else '义务'
|
|
|
|
|
|
print(f"期权:{contract} {direction} {abs(pos):.0f}手")
|
|
|
|
|
|
else:
|
|
|
|
|
|
print("期权:无持仓")
|
|
|
|
|
|
|
|
|
|
|
|
print("="*50 + "\n")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
|
"""
|
|
|
|
|
|
主函数
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 数据目录
|
|
|
|
|
|
data_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data')
|
|
|
|
|
|
output_dir = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
|
|
|
|
|
|
|
|
print(f"数据目录: {data_dir}")
|
|
|
|
|
|
print(f"输出目录: {output_dir}\n")
|
|
|
|
|
|
|
|
|
|
|
|
# 查找所有xls文件
|
|
|
|
|
|
xls_files = glob.glob(os.path.join(data_dir, '*.xls'))
|
|
|
|
|
|
if not xls_files:
|
|
|
|
|
|
print(f"错误: 在 {data_dir} 中未找到 .xls 文件")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
print(f"找到 {len(xls_files)} 个结算单文件\n")
|
|
|
|
|
|
|
|
|
|
|
|
# 读取所有文件
|
|
|
|
|
|
all_futures = []
|
|
|
|
|
|
all_options = []
|
|
|
|
|
|
|
|
|
|
|
|
for xls_file in sorted(xls_files):
|
|
|
|
|
|
filename = os.path.basename(xls_file)
|
|
|
|
|
|
print(f"处理: {filename}")
|
|
|
|
|
|
|
|
|
|
|
|
# 读取期货成交明细
|
|
|
|
|
|
df_f = read_sheet(xls_file, '成交明细', '期货')
|
|
|
|
|
|
if not df_f.empty:
|
|
|
|
|
|
all_futures.append(df_f)
|
|
|
|
|
|
|
|
|
|
|
|
# 读取期权成交明细
|
|
|
|
|
|
df_o = read_sheet(xls_file, '期权成交明细', '期权')
|
|
|
|
|
|
if not df_o.empty:
|
|
|
|
|
|
all_options.append(df_o)
|
|
|
|
|
|
|
|
|
|
|
|
# 合并数据
|
|
|
|
|
|
df_futures = pd.concat(all_futures, ignore_index=True) if all_futures else pd.DataFrame()
|
|
|
|
|
|
df_options = pd.concat(all_options, ignore_index=True) if all_options else pd.DataFrame()
|
|
|
|
|
|
|
|
|
|
|
|
print(f"\n汇总: 期货 {len(df_futures)} 条, 期权 {len(df_options)} 条\n")
|
|
|
|
|
|
|
|
|
|
|
|
# 合并交易记录
|
|
|
|
|
|
trades_merged = merge_trades(df_futures, df_options)
|
|
|
|
|
|
|
|
|
|
|
|
# 保存输出
|
|
|
|
|
|
results = save_output(trades_merged, df_futures, df_options, output_dir)
|
|
|
|
|
|
|
|
|
|
|
|
# 打印摘要
|
|
|
|
|
|
print_summary(df_futures, df_options, results)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
|
main()
|