Merge PR #714
This commit is contained in:
commit
96f7a9174c
2 files changed
+81
-2
No files matched your search
@@ -162,8 +162,22 @@ def _normalize_side(raw: Any) -> str:
|
||||
raise ValueError(f"Unsupported trade side: {raw!r}")
|
||||
|
||||
|
||||
def _is_empty_code(raw: Any) -> bool:
|
||||
"""True for None/NaN/blank securities codes from CSV/Excel cells."""
|
||||
if raw is None:
|
||||
return True
|
||||
try:
|
||||
if pd.isna(raw):
|
||||
return True
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
return not str(raw).strip()
|
||||
|
||||
|
||||
def _qualify_a_share(code: str) -> str:
|
||||
"""Append .SH/.SZ/.BJ suffix to a bare A-share ticker."""
|
||||
if _is_empty_code(code):
|
||||
raise ValueError("empty securities code")
|
||||
code = str(code).strip().zfill(6)
|
||||
if "." in code:
|
||||
return code.upper()
|
||||
@@ -193,13 +207,16 @@ def parse_tonghuashun(df: pd.DataFrame) -> list[TradeRecord]:
|
||||
"""
|
||||
records: list[TradeRecord] = []
|
||||
for _, row in df.iterrows():
|
||||
raw_code = row.get("证券代码", "")
|
||||
if _is_empty_code(raw_code):
|
||||
continue
|
||||
qty = _to_float(row.get("成交数量"))
|
||||
price = _to_float(row.get("成交价格"))
|
||||
amount = _to_float(row.get("成交金额")) or qty * price
|
||||
fee = _to_float(row.get("手续费")) + _to_float(row.get("印花税")) + _to_float(row.get("过户费"))
|
||||
records.append(TradeRecord(
|
||||
datetime=str(row.get("成交时间", "")).strip(),
|
||||
symbol=_qualify_a_share(row.get("证券代码", "")),
|
||||
symbol=_qualify_a_share(raw_code),
|
||||
name=str(row.get("证券名称", "")).strip(),
|
||||
side=_normalize_side(row.get("操作")),
|
||||
quantity=qty,
|
||||
@@ -219,6 +236,9 @@ def parse_eastmoney(df: pd.DataFrame) -> list[TradeRecord]:
|
||||
"""
|
||||
records: list[TradeRecord] = []
|
||||
for _, row in df.iterrows():
|
||||
raw_code = row.get("股票代码", "")
|
||||
if _is_empty_code(raw_code):
|
||||
continue
|
||||
raw_date = str(row.get("成交日期", "")).strip()
|
||||
raw_time = str(row.get("成交时间", "")).strip()
|
||||
if len(raw_date) == 8 and raw_date.isdigit():
|
||||
@@ -232,7 +252,7 @@ def parse_eastmoney(df: pd.DataFrame) -> list[TradeRecord]:
|
||||
fee = _to_float(row.get("佣金")) + _to_float(row.get("印花税"))
|
||||
records.append(TradeRecord(
|
||||
datetime=dt,
|
||||
symbol=_qualify_a_share(row.get("股票代码", "")),
|
||||
symbol=_qualify_a_share(raw_code),
|
||||
name=str(row.get("股票名称", "")).strip(),
|
||||
side=_normalize_side(row.get("买卖标志")),
|
||||
quantity=qty,
|
||||
|
||||
@@ -19,6 +19,8 @@ from src.tools.trade_journal_parsers import (
|
||||
_infer_market_from_symbol,
|
||||
_normalize_side,
|
||||
_qualify_a_share,
|
||||
parse_tonghuashun,
|
||||
parse_eastmoney,
|
||||
detect_format,
|
||||
parse_file,
|
||||
records_to_dataframe,
|
||||
@@ -506,3 +508,60 @@ def test_analyze_with_filter(allow_tmp: Path) -> None:
|
||||
assert result["status"] == "ok"
|
||||
assert result["total_records"] == 1
|
||||
assert result["filter_applied"] == "symbol=AAPL"
|
||||
|
||||
|
||||
def test_qualify_a_share_rejects_empty() -> None:
|
||||
with pytest.raises(ValueError, match="empty"):
|
||||
_qualify_a_share("")
|
||||
with pytest.raises(ValueError, match="empty"):
|
||||
_qualify_a_share(" ")
|
||||
|
||||
|
||||
def test_parse_tonghuashun_skips_blank_code_rows() -> None:
|
||||
df = pd.DataFrame([{
|
||||
"成交时间": "2024-01-01 10:00:00", "证券代码": "", "证券名称": "",
|
||||
"操作": "买入", "成交数量": 100, "成交价格": 10.0, "成交金额": 1000,
|
||||
"手续费": 0, "印花税": 0, "过户费": 0,
|
||||
}, {
|
||||
"成交时间": "2024-01-01 10:01:00", "证券代码": "600519", "证券名称": "茅台",
|
||||
"操作": "买入", "成交数量": 100, "成交价格": 10.0, "成交金额": 1000,
|
||||
"手续费": 0, "印花税": 0, "过户费": 0,
|
||||
}])
|
||||
rec = parse_tonghuashun(df)
|
||||
assert len(rec) == 1
|
||||
assert rec[0].symbol == "600519.SH"
|
||||
|
||||
|
||||
def test_qualify_a_share_rejects_nan() -> None:
|
||||
with pytest.raises(ValueError, match="empty"):
|
||||
_qualify_a_share(float("nan"))
|
||||
|
||||
|
||||
def test_parse_tonghuashun_skips_nan_code_rows() -> None:
|
||||
df = pd.DataFrame([{
|
||||
"成交时间": "2024-01-01 10:00:00", "证券代码": float("nan"), "证券名称": "",
|
||||
"操作": "买入", "成交数量": 100, "成交价格": 10.0, "成交金额": 1000,
|
||||
"手续费": 0, "印花税": 0, "过户费": 0,
|
||||
}, {
|
||||
"成交时间": "2024-01-01 10:01:00", "证券代码": "600519", "证券名称": "茅台",
|
||||
"操作": "买入", "成交数量": 100, "成交价格": 10.0, "成交金额": 1000,
|
||||
"手续费": 0, "印花税": 0, "过户费": 0,
|
||||
}])
|
||||
rec = parse_tonghuashun(df)
|
||||
assert len(rec) == 1
|
||||
assert rec[0].symbol == "600519.SH"
|
||||
|
||||
|
||||
def test_parse_eastmoney_skips_nan_code_rows() -> None:
|
||||
df = pd.DataFrame([{
|
||||
"成交日期": "20240101", "成交时间": "10:00:00", "股票代码": float("nan"),
|
||||
"股票名称": "", "买卖标志": "B", "成交数量": 100, "成交均价": 10.0,
|
||||
"成交金额": 1000, "佣金": 0, "印花税": 0,
|
||||
}, {
|
||||
"成交日期": "20240101", "成交时间": "10:01:00", "股票代码": "000001",
|
||||
"股票名称": "平安", "买卖标志": "B", "成交数量": 100, "成交均价": 10.0,
|
||||
"成交金额": 1000, "佣金": 0, "印花税": 0,
|
||||
}])
|
||||
rec = parse_eastmoney(df)
|
||||
assert len(rec) == 1
|
||||
assert rec[0].symbol == "000001.SZ"
|
||||
Reference in new issue
Block a user