遍历股票开平仓交易数组计算最终持仓状态的实现问题
股票交易持仓统计实现说明
需求背景
我们需要解析存储股票买卖记录的CSV文件,逐笔处理交易数据,最终输出剩余持仓的结构化记录。规则如下:
- 买入操作生成状态为
OPEN的开仓记录 - 全额卖出对应持仓时,原开仓记录状态更新为
CLOSED - 部分卖出对应持仓时,原开仓记录扣减对应卖出数量、状态保持
OPEN,同时生成一条卖出数量的CLOSED记录 - 加仓后全额卖出时,所有关联的持仓记录均标记为
CLOSED
输入示例(CSV解析后结构)
[ { Action: 'Buy', Symbol: 'SHOP', Quantity: '200', ... }, { Action: 'Sell', Symbol: 'SHOP', Quantity: '200', ... }, { Action: 'Buy', Symbol: 'SHOP', Quantity: '50', ... }, { Action: 'Sell', Symbol: 'SHOP', Quantity: '25', ... }, { Action: 'Sell', Symbol: 'SHOP', Quantity: '25', ... }, { Action: 'Buy', Symbol: 'SHOP', Quantity: '1', ... } ]
输出示例
[ { stockTicker: 'SHOP', positionStatus: 'CLOSED', quantity: 200, ... }, { stockTicker: 'SHOP', positionStatus: 'CLOSED', quantity: 25, ... }, { stockTicker: 'SHOP', positionStatus: 'CLOSED', quantity: 25, ... }, { stockTicker: 'SHOP', positionStatus: 'OPEN', quantity: 1, ... } ]
匹配终止条件判断
你不需要递归遍历多次,只要遵循股票持仓常用的FIFO(先进先出)匹配规则,单次遍历即可完成处理,终止条件非常明确:
- 所有交易记录已全部处理完成
- 或剩余未匹配的卖出委托数量为0
核心实现逻辑
这里提供更简单的迭代实现逻辑,比递归更易维护也不会出现栈溢出问题:
- 初始化两个队列:
openPositions存储未平仓的开仓记录,result存储最终输出的所有持仓记录 - 逐笔遍历所有交易记录:
- 遇到买入记录:直接转为
OPEN状态的持仓对象推入openPositions - 遇到卖出记录:取出当前待匹配的卖出数量,从
openPositions队首开始逐笔扣减:- 如果待卖出数量 ≥ 当前开仓记录的数量:将当前开仓记录状态改为
CLOSED推入result,待卖出数量扣减对应值,继续匹配下一笔开仓记录 - 如果待卖出数量 < 当前开仓记录的数量:拆分当前开仓记录,生成一条待卖出数量的
CLOSED记录推入result,原开仓记录扣减对应数量后留在openPositions队列中,清空待卖出数量结束本次卖出匹配
- 如果待卖出数量 ≥ 当前开仓记录的数量:将当前开仓记录状态改为
- 遇到买入记录:直接转为
- 所有交易处理完成后,将
openPositions中剩余的OPEN状态记录全部推入result,就是你要的最终结果
代码示例(JavaScript)
function calculatePositions(trades) { const openPositions = []; const result = []; for (const trade of trades) { const qty = Number(trade.Quantity); if (trade.Action === 'Buy') { openPositions.push({ stockTicker: trade.Symbol, positionStatus: 'OPEN', quantity: qty // 其他自定义字段自行补充 }); continue; } // 处理卖出逻辑 let remainSellQty = qty; while (remainSellQty > 0 && openPositions.length > 0) { const currentOpen = openPositions[0]; if (currentOpen.quantity <= remainSellQty) { // 全额平仓 currentOpen.positionStatus = 'CLOSED'; result.push(currentOpen); remainSellQty -= currentOpen.quantity; openPositions.shift(); } else { // 部分平仓 const closedPart = { ...currentOpen, positionStatus: 'CLOSED', quantity: remainSellQty }; result.push(closedPart); currentOpen.quantity -= remainSellQty; remainSellQty = 0; } } } // 剩余未平仓的记录加入最终结果 result.push(...openPositions); return result; }
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

