Files
stock/backend/tests/test_trades_parser.py
2026-09-09 15:07:58 +08:00

102 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""交割单解析器:四类真实导出格式 + 边界(转账/配号/利息跳过、费用合计去重、日期多格式)。
移植自 scripts/test_trades_parser.py已删不碰数据库直接调 app.trades.parse_statement。
"""
from __future__ import annotations
import io
from datetime import datetime as dt
import pytest
from fastapi import HTTPException
from openpyxl import Workbook
from app.trades import parse_statement
def test_tdx_gbk_tabs():
"""通达信式GBK + 制表符 + 标题行在前;跳过银行转存/利息归本。"""
tdx = (
"交割单\n"
"股东账号: A123456789 起始日期: 20240102 终止日期: 20240105 币种: 人民币\n"
"\t交割日期\t业务名称\t证券代码\t证券名称\t成交价格\t成交数量\t成交金额\t手续费\t印花税\t过户费\t发生金额\t资金余额\t合同号\n"
"\t20240102\t证券买入\t600519\t贵州茅台\t1680.00\t100\t168000.00\t5.00\t0.00\t1.68\t-168006.68\t200000.00\t1000001\n"
"\t20240102\t银行转存\t\t\t\t\t\t\t\t\t50000.00\t250000.00\t\n"
"\t20240103\t证券卖出\t600519\t贵州茅台\t1700.50\t100\t170050.00\t5.00\t170.05\t1.70\t169873.25\t419873.25\t1000002\n"
"\t20240105\t利息归本\t\t\t\t\t\t\t\t\t1.25\t419874.50\t\n"
)
r = parse_statement(tdx.encode("gbk"), "交割单.txt")
assert len(r.trades) == 2
assert r.skipped_other == 2
t0, t1 = r.trades[0], r.trades[1]
assert (t0.trade_date.isoformat(), t0.ts_code) == ("2024-01-02", "600519.SH")
assert t0.direction == "buy" and abs(t0.fee - 6.68) < 1e-9
assert t1.direction == "sell" and abs(t1.fee - 176.75) < 1e-9
assert t0.amount == 168000.0
def test_hengsheng_csv_fee_total():
"""恒生柜台式UTF-8 CSV费用合计列不与其他费用列重复累加。"""
hs = (
"序号,交收日期,证券代码,证券名称,交易类别,成交价格,成交数量,证券余额,成交金额,资金发生数,资金余额,流水序号,业务标志,业务名称,发生金额,后资金额,货币类别,费用合计,净佣金,规费,印花税,过户费,合同号\n"
"1,2024-06-07,000858,五粮液,证券买入,132.50,200,200,26500.00,-26505.80,73494.20,1,0101,证券买入,-26505.80,73494.20,人民币,5.80,4.20,1.60,0.00,0.00,66778001\n"
"2,2024-06-07,,,\t,,,,5120.00,78614.20,2,2041,银行转存,5120.00,78614.20,人民币,0,0,0,0,0,\n"
"3,2024-06-10,000858,五粮液,证券卖出,135.00,200,0,27000.00,26975.30,105589.50,3,0102,证券卖出,26975.30,105589.50,人民币,24.70,4.20,1.60,18.90,0.00,66779001\n"
)
r = parse_statement(hs.encode("utf-8"), "hsi.csv")
assert len(r.trades) == 2
assert abs(r.trades[1].fee - 24.70) < 1e-9
assert r.trades[0].ts_code == "000858.SZ"
assert r.trades[0].trade_date.isoformat() == "2024-06-07"
def test_html_pseudo_xls():
"""HTML 伪 .xls同花顺导出常见真身千分位金额、斜杠日期、创业板后缀。"""
html = """<html><head><meta charset="gbk"></head><body>
<table>
<tr><td>客户姓名</td><td>测试</td></tr>
<tr><td>成交日期</td><td>业务名称</td><td>证券代码</td><td>证券名称</td><td>成交价格</td><td>成交数量</td><td>成交金额</td><td>手续费</td></tr>
<tr><td>2024/03/15</td><td>证券买入</td><td>300750</td><td>宁德时代</td><td>182.30</td><td>300</td><td>54,690.00</td><td>16.41</td></tr>
<tr><td>2024/03/18</td><td>证券卖出</td><td>300750</td><td>宁德时代</td><td>185.00</td><td>300</td><td>55,500.00</td><td>5.55</td></tr>
</table></body></html>"""
r = parse_statement(html.encode("gbk"), "jiaogedan.xls")
assert len(r.trades) == 2
assert r.trades[0].amount == 54690.0
assert r.trades[0].ts_code == "300750.SZ"
assert r.trades[1].trade_date.isoformat() == "2024-03-18"
def test_amount_sign_direction():
"""无业务名称列(招商式):发生金额正负判方向。"""
zh = (
"证券名称,成交日期,成交价格,成交数量,发生金额,资金余额,合同编号\n"
"贵州茅台,20240102,1680.00,100,-168005.00,200000.00,SZ1000001\n"
"贵州茅台,20240103,1700.50,100,170049.50,370049.50,SZ1000002\n"
)
r = parse_statement(zh.encode("utf-8"), "zszs.csv")
assert len(r.trades) == 2
assert (r.trades[0].direction, r.trades[1].direction) == ("buy", "sell")
def test_xlsx_openpyxl():
"""xlsxopenpyxl 内存构造datetime 日期、科创板后缀、佣金+过户费合计。"""
wb = Workbook()
ws = wb.active
ws.append(["对账单", None, None])
ws.append(["成交日期", "业务名称", "证券代码", "证券名称", "成交均价", "成交股数", "成交金额", "佣金", "过户费"])
ws.append([dt(2024, 2, 28, 14, 35, 0), "证券买入", "688981", "中芯国际", 52.80, 200, 10560.00, 2.50, 1.06])
ws.append([dt(2024, 3, 1, 9, 31, 0), "证券卖出", "688981", "中芯国际", 54.10, 200, 10820.00, 2.50, 1.06])
buf = io.BytesIO()
wb.save(buf)
r = parse_statement(buf.getvalue(), "sm.xlsx")
assert len(r.trades) == 2
assert r.trades[0].trade_date.isoformat() == "2024-02-28"
assert r.trades[0].ts_code == "688981.SH"
assert abs(r.trades[0].fee - 3.56) < 1e-9
def test_garbage_input_422():
with pytest.raises(HTTPException) as ei:
parse_statement("随便一串不是交割单的文字,1,2,3".encode("utf-8"), "x.csv")
assert ei.value.status_code == 422