股票模拟账户怎么记账:按策略信号维护现金与持仓
股票模拟账户需要资产账,而不只是一串买卖标签。规则可以请求买入却没有足够现金,卖出量可以超过持股,价格更新也可以在没有任何现金流时改变净值。应将这些事件分别记录,让每笔被接受的虚拟交易都能从前一个状态重建,而不是只看最后一行余额是否合理。
本指南用单只美元股票、整股、独立初始现金及明确费用处理合成事件,不访问真实账户,不下载行情,不连接券商。持续虚拟账属于外部研究扩展,本例运行成功不代表 CoTrading 或参考批次影子回测已经维护了长期更新的账户,更不代表任何真实收益。
确定开场资产与事件契约
账户起点是现金 1000、零股。这是独立实验假设,不从盈利日志或真实持仓市值推算。应一起冻结规则身份、固定篮子、币种、开始边界和执行约定。程序接收已经准备好的事件请求,没有生成策略,也没有决定何时调用模型。来源记录是否达到五赢家条件,与本账有多少可花现金是不同问题。
每个事件包含唯一身份、方向、正整数股数、有限正价格、有限非负费用及确认字段。夹具只接受买卖方向以及声明的 filled、unfilled 输入;未知方向和确认值会报错,而不是自动变成零交易。函数复制前状态,失败时能够核查原现金股数,避免先改一半资产、再因另一字段错误留下无法恢复的中间态。
这里输入中的 filled 是夹具提出的虚拟确认,仍需通过资金和库存可行性后才提交,不是券商成交证明。真实券商已经确认的成交不能因本地模型现金不足就被忽略,应在模拟范围之外做账户勾稽。适配研究日志时必须保留这一层区别,不能把为了演示提供的确认标签当成真实账户权威记录。
现金、股数与估值分开
买入 q 股、价格 p、费用 f,候选现金为原现金减 q*p 再减 f,股数增加 q。卖出使用相反的带符号数量,但费用仍然扣除。只有候选现金与股数均非负才提交。当前例子没有借款、做空、拆分成交或未结算余额,也没有把可融资额度当作普通现金。
按某个价格估值时,净值为现金加股数乘价格,不等于可买金额。持有六股从 100 涨到 110,净值可以增加,但账户仍只有原来的 399 现金;相反,支付费用会降低净值,即使报价不变。完整系统还应分别保存估值时间和交易时间,避免把一次旧报价当作多个时点共同真值。
Python Decimal 文档支持从十进制字符串运算与有限数检查。Decimal 本身仍能表示 NaN 和无穷,所以程序在比较正负前显式拒绝。采用金额类型不代表无效报价自动安全,缺失费用也不能被悄悄变成真实世界的零成本。未提供的字段应先补齐或保持未知。
在同一个价格上勾稽每次变动
先用事件价格计算交易前资产,再用同一价格计算交易后资产。成功提交的差异必须恰好为负费用;未成交或被拒绝事件差异必须为零。这样把记账身份与期间行情移动分开,现金正负号即使暂时被后来的价格涨跌掩盖,也能在出错那一笔直接被断言抓住。
行与行之间的价格变化仍属于估值。不能把上一行按 105 算的净值,与下一行按 90 算的净值直接相减,再把全部差额归给一张根本没有成交的单。追踪账应展示新标价、交易前资产、接受的执行与交易后资产,它们相关但含义不同,尤其不能把估值损失写成现金已经支出。
SEC 费用说明解释成本会减少组合价值,并建议核对账户与成交确认。本文每笔费用一单位是为了清楚展示算术而自设,不是佣金报价、不含完整税项,也不表示不同市场收费相同。真实费用应按实际来源和币种单独取得。
跟随四条主例事件
E1 请求按 100 买六股,费用一,资金足够,现金变为 399,股数六。该成交价下净值为 999,正好是起始 1000 减费用。断言同时检查资产和状态;只打印金额而没有核对并不能证明资金移动方向正确,更不能说明后续所有输入都通过了验证。
E2 在价格 105 时提出另一买入但没有成交,现金保持 399、股数六,净值 1029 仅来自估值。E3 提议按 90 买五股加费用一,需要现金 451,账户只有 399,所以状态为 insufficient_cash。现金和股数不变,新价格对应净值 939,不能因此误写发生了一笔亏损卖出。
E4 从六股中按 110 卖两股,费用一,现金增加 219 至 618,余四股,同价净值为 1058。交易前该价格下价值 1059,相差一就是费用。最后把四股按 105 估值,期末净值为 1038,没有额外交易或转账;现金仍是 618,不能把净值变化再加进现金。
这四条记录没有使用后来的卖出款提前支持 E3。若一个真实执行模型允许先卖后买,也必须按真实确认顺序推进状态,再检查新资金是否可用。当前顺序已经固定,不能为了让被拒绝买入成功,事后交换事件位置,否则就是另一个实验,而不是修正同一份账。
不执行的原因也要保留
未成交请求与资金不足的虚拟成交提案是不同结果,两者都不改变股数。保留身份和理由,而不是压成一笔数量为零的成功交易,之后才能判断规则没有行动、账户不够钱,还是没有确认成交。单一成交次数无法回答这些问题,也不能把缺少确认的机会全部解释成策略主动放弃。
不同机制反例是在只余四股时尝试卖五股。结果为 insufficient_shares,现金 618 和四股不变,问题是库存而非 E3 的资金约束。程序还拒绝重复 E1 身份及带 NaN 价格的新请求,防止重复或无效记录被当成另一笔正常移动。重复执行和真实修订必须分别处理,不能仅靠金额看起来相似判断。
这里的重复检测是报错,不是完整幂等恢复协议。恢复系统还要比较重复事件有效载荷,并把回执与账户状态共同持久化。不能不看内容就忽略所有相同身份,否则修正过的数量或冲突成交价可能被隐藏。本文先验证资产算术,恢复方法由独立教程处理,不把局部守恒扩大成完整后台服务保证。
验收交接应把资产账与展示分开:先核对记录的现金和数量,再按各行标价复算显示净值。图表尺度改变不能修复错误股数,期末净值恰好一致也不能证明中间每步正确。被拒绝请求应与成功事件相邻保留,让复核者能够勾稽总事件数,不需要猜测少掉的行到底没有信号还是日志漏记。
展示端如果只想显示成功交易,可以筛选视图,但原事件账不能因此删除拒绝原因。任何汇总都应说明分母是全部请求还是已提交成交,费用合计也只能来自实际提交行。这样同一组输入在详细账与概览中能够互相追溯,不会因一层省略而生成另一套交易历史。
运行样例并保留边界
下载完整示例,保存 R641.py,使用 Python 3.12 或更新版本执行 python R641.py。只依赖标准库与合成事件。断言覆盖四个主状态、每步同价勾稽、终值 1038、超卖、重复身份和非有限金额;输出同时包含每行现金、股数与净值,便于独立复算。
整股、单币种、即时结算约定必须随结果交付。真实手数、结算、部分成交、分红及公司行动需要额外契约与独立输入检查,不能静默套用玩具账,更不能把终值展示成历史策略回报。本例只证明在声明假设下资产如何变化,未证明这些假设适合任何一个实际市场。
持续观察交接需保留最后可信状态、全部请求、确认结果、费用与估值。输入修正后,应从已知较早状态按新登记版本重放,不仅编辑末余额。行情缺失时不能完整报告净值,也不能拿未来价格补齐;现金和股数可以仍然已知,估值则明确未知。状态知识的不同层次应分别展示,避免为了输出一条平滑曲线牺牲可追查性。
常见问题
买入信号一出现就扣现金吗?
只有资金可行且提交的虚拟成交改变本账,未成交请求保持现金与股数不变。
净值上涨就能自动多买股票吗?
不能。股票市值与现金分开,本例不抵押借款,也不预支未确认卖款。