diff --git a/app/services/trade_parser.py b/app/services/trade_parser.py index d8c1245..5ac9229 100644 --- a/app/services/trade_parser.py +++ b/app/services/trade_parser.py @@ -6,7 +6,7 @@ import re import uuid import json import logging -from datetime import datetime +from datetime import datetime, timedelta from collections import defaultdict from pathlib import Path @@ -194,6 +194,48 @@ def _normalize_time(time_val) -> str: return s +def recalculate_trade_date(trade_date: str, trade_time: str) -> str: + """ + 根据交易时间重新计算交易日 + + 期货交易规则: + - 前一交易日 21:00 之后到当前交易日 15:00 之前的交易,算作当天交易 + - 周五 21:00 之后的夜盘交易,算作下周一的交易数据 + + :param trade_date: 原始交易日 (YYYY-MM-DD) + :param trade_time: 交易时间 (HH:MM:SS) + :return: 重新计算后的交易日 + """ + if not trade_date or not trade_time: + return trade_date + + try: + # 解析日期和时间 + dt = datetime.strptime(trade_date, '%Y-%m-%d') + time_parts = trade_time.split(':') + hour = int(time_parts[0]) + + # 判断是否在夜盘时段 (21:00 之后) + if hour >= 21: + # 夜盘交易,交易日归属下一天 + next_day = dt + timedelta(days=1) + # 如果是周五 (weekday=4),下一天是周六,需要跳到下周一 + if dt.weekday() == 4: # 周五 + next_day = dt + timedelta(days=3) # 跳到下周一 + # 如果是周六 (weekday=5),跳到下周一 + elif dt.weekday() == 5: + next_day = dt + timedelta(days=2) + # 如果是周日 (weekday=6),跳到下周一 + elif dt.weekday() == 6: + next_day = dt + timedelta(days=1) + return next_day.strftime('%Y-%m-%d') + else: + # 日盘交易,保持原交易日 + return trade_date + except (ValueError, IndexError): + return trade_date + + def _build_dedup_key(trade_date: str, variety: str, trade_time: str, price: float) -> str: """构建去重键:交易日+品种+交易时间+价格(价格保留2位小数避免浮点精度问题)""" return f"{trade_date}|{variety}|{trade_time}|{round(price, 2)}" @@ -280,8 +322,10 @@ def save_to_db( continue futures_parsed += 1 variety = extract_variety(contract) - trade_date = _normalize_date(row.get('实际成交日期', '')) + raw_date = _normalize_date(row.get('实际成交日期', '')) trade_time = _normalize_time(row.get('成交时间', '')) + # 根据交易时间重新计算交易日(夜盘归属下一天) + trade_date = recalculate_trade_date(raw_date, trade_time) bs_flag = str(row.get('买/卖', '')).strip() oc_flag = str(row.get('开/平', '')).strip() price = safe_float(row.get('成交价')) @@ -300,7 +344,7 @@ def save_to_db( # 记录前几条的去重键用于调试 if futures_parsed <= 3: - logger.info(f"[去重校验] 期货记录 #{futures_parsed}: date={trade_date!r}, variety={variety!r}, time={trade_time!r}, price={price!r} => key={dedup_key!r}") + logger.info(f"[去重校验] 期货记录 #{futures_parsed}: raw_date={raw_date!r}, trade_date={trade_date!r}, variety={variety!r}, time={trade_time!r}, price={price!r} => key={dedup_key!r}") new_keys_in_batch.add(dedup_key) rec = TradeRecord( @@ -331,8 +375,10 @@ def save_to_db( continue options_parsed += 1 variety = extract_variety(contract) - trade_date = _normalize_date(row.get('成交日期', '')) + raw_date = _normalize_date(row.get('成交日期', '')) trade_time = _normalize_time(row.get('成交时间', '')) + # 根据交易时间重新计算交易日(夜盘归属下一天) + trade_date = recalculate_trade_date(raw_date, trade_time) bs_flag = str(row.get('买/卖', '')).strip() price = safe_float(row.get('权利金单价')) diff --git a/openspec/changes/trading-day-redefinition/design.md b/openspec/changes/trading-day-redefinition/design.md new file mode 100644 index 0000000..fcfa587 --- /dev/null +++ b/openspec/changes/trading-day-redefinition/design.md @@ -0,0 +1,19 @@ +# 设计文档 + +## 实现方案 + +### 交易日重计算函数 `recalculate_trade_date` + +输入:原始交易日 (YYYY-MM-DD) + 交易时间 (HH:MM:SS) +输出:重新计算后的交易日 + +逻辑: +1. 解析交易时间,判断是否在 21:00 之后 +2. 如果在 21:00 之后(夜盘): + - 如果原始交易日是周五,则交易日 = 下周一 + - 否则交易日 = 原始交易日 + 1 天 +3. 如果在 15:00 之前且不在夜盘时段,保持原交易日 +4. 其他情况保持原交易日 + +### 应用位置 +在 `save_to_db` 中处理每条记录时,调用 `recalculate_trade_date` 重新计算交易日。 diff --git a/openspec/changes/trading-day-redefinition/proposal.md b/openspec/changes/trading-day-redefinition/proposal.md new file mode 100644 index 0000000..aeb31ec --- /dev/null +++ b/openspec/changes/trading-day-redefinition/proposal.md @@ -0,0 +1,14 @@ +# 交易日定义重新定义 + +## 动机 +当前交易日直接使用结算单中的"实际成交日期",但期货交易有夜盘,需要重新定义交易日归属: +- 前一交易日 21:00 之后到当前交易日 15:00 之前的交易,算作当天交易 +- 周五 21:00 之后的夜盘交易,算作下周一的交易数据 + +## 目标 +1. 在导入时根据交易时间重新计算交易日 +2. 正确处理周五夜盘归属下周一的情况 + +## 范围 +- `app/services/trade_parser.py` — 新增交易日重计算函数 +- 导入时应用新的交易日规则 diff --git a/openspec/changes/trading-day-redefinition/tasks.md b/openspec/changes/trading-day-redefinition/tasks.md new file mode 100644 index 0000000..5c79e7b --- /dev/null +++ b/openspec/changes/trading-day-redefinition/tasks.md @@ -0,0 +1,5 @@ +# 任务列表 + +- [x] 1. 新增交易日重计算函数 `recalculate_trade_date` +- [x] 2. 在 `save_to_db` 中应用新的交易日规则 +- [x] 3. 测试验证交易日计算逻辑