系统设计:股票交易记账系统
介绍
日常消费都需要记账,股票交易更加需要把帐记清楚。在本文中,构思一个股票交易记账系统。这也是一个典型的带有统计功能的 CRUD 模块。
税费
在交易过程中,还会收取各种税费。
项目 | 比例 | 备注 |
---|---|---|
印花税 | 0.1% | 卖出收取,买进的时候不收。 |
佣金 | 0.2%~0.3% | 具体根据证券公司决定,佣金有最低收取标准,最低收取5元。 |
过户费 | 每一千股收1元。 | 仅限于沪市。 |
通讯费 | 上海、深圳本地交易收取1元,其它地区5元。 |
数据结构
交易会话 StockSession
通常情况下,股票交易都不是一锤子买卖,不是只有一次买入、一次卖出,而是会有多次买入、多次卖出。
实际场景是:开仓买入后,根据股价的变化,连续进行加仓或减仓,直到最后清仓离场。
即以开仓为开始,以清仓为结束,这整个过程算一次会话。
因此抽象出交易会话这个实体。具体字段如下:
项目 | 名称 | 类型 | 示例 | 备注 |
---|---|---|---|---|
操作品种 | symbol | String | 000000.SH | |
开仓日期 | start | Date | 2022-02-02 | 首次买入日期 |
清仓日期 | end | Date | 2022-03-02 | 清仓日期 |
是否完结 | finish | bool | False | 是否还在交易当中 |
最终买入成本 | final_cost_buy | float | 222222 | 会话结束后,累计买入成本 |
最终手续费 | final_cost_fee | float | 222 | 会话结束后,累计手续费成本 |
最终盈亏金额 | final_earn | float | 2222 | 会话结束后,最终盈利金额 |
最终盈亏比例 | final_earn_pct | float | 0.5 | 会话结束后,最终盈利比例 |
其中:
- 对于盈利核算,在完结前需要动态核算,因为依赖于当日股价,在完结后,可以计算一个终值进行保存
交易操作 StockAction
记录单次买卖交易,每个交易都关联到一个 StockSession。
并且:
- 如果是首次开仓,需要先创建一个 StockSession,再创建 StockAction 并关联到 Session 上
- 如果是最终清仓,保存完 StockAction 后,需要更新 StockSession 状态,并且完成 final_ 字段的核算
项目 | 名称 | 类型 | 示例 | 备注 |
---|---|---|---|---|
关联会话 | session | foreign key of StockSession | id: 1 | |
操作品种 | symbol | String | 000000.SH | |
交易日期 | date | Date | 2022-02-02 | |
交易类型 | type | String | "buy" "sell" | |
成交价 | price_deal | float | 20.22 | |
成交股数 | count | int | 200 | 按照股数存,需要把“手”换成“股” |
手续费 | fee | float | 10.5 |
其中:
- 该字段除了用户核算记账之外,还会用在 K 线图当中,在 K 线图中标记买卖 Marker
计算公式
总盈亏计算
<math>盈亏 = 持仓市值 + 累计卖出金额 - 累计买入金额 - 交易费用</math>
其中:
- 持仓市值:以最近结算日收盘价计算
- 累计入账金额:卖出
- 累计出账金额:买入,也包含交易费用
- 交易费用:各种税费
<math>盈亏比例 = \frac{盈亏}{累计出账金额} = \frac{持仓市值+累计入账金额}{累计出账金额} - 1</math>
模块设计
StockSession 和 StockAction 的 Model 前面已经给出,不再重复。
StockBookKeeper
对外暴露的股票记账类。对外提供 API:
接口 | 功能 | 实现 | 备注 |
---|---|---|---|
addBuyAction(
symbol: Symbol,
date: date,
price_deal: float,
count: int,
fee: float)
|
添加买入操作 | 透传调用 _addAction | |
addSellAction(
symbol: Symbol,
date: date,
price_deal: float,
count: int,
fee: float)
|
添加卖出操作 | 透传调用 _addAction | |
_addAction(
symbol: Symbol,
date: date,
type: str,
price_deal: float,
count: int,
fee: float)
|
添加一笔交易 | StockSession 操作在内部完成,不对外暴露 | |
findSessionsBySymbol | 根据 Symbol 查询 Session | ||
findActionsBySessions | 根据 Sessions 查询 Actions | ||
findSessionsBetweenData | 查询日期间的 Sessions | ||
findActionsBetweenData | 查询日期间的 Actions |