You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Google Apps Script实现FIFO规则的股票已实现收益计算函数

Google Apps Script FIFO股票已实现收益计算实现

核心逻辑说明

按单只证券独立核算的需求可通过对象结构实现:

  • 用stockQueues对象存储每只股票的FIFO成本队列,键为股票代码,值为按买入顺序排列的每股成本数组
  • 用profitStats对象存储每只股票的收益统计数据,键为股票代码,值包含累计已实现收益、累计抵扣成本、收益率三个字段

完整可运行代码

const ss = SpreadsheetApp.getActiveSpreadsheet();
const historySheet = ss.getSheetByName('Blad1');
const outputSheet = ss.getSheetByName('output') || ss.insertSheet('output');

// 批量向数组推入指定数量的元素
function pushTimes(count, arr, item) {
  for (let i = 0; i < count; i++) {
    arr.push(item);
  }
}

// 批量从数组头部弹出指定数量的元素,返回弹出的元素数组
function shiftTimes(count, arr) {
  const result = [];
  for (let i = 0; i < count; i++) {
    if (arr.length === 0) break; // 避免卖空场景报错,可根据需求补充卖空逻辑
    result.push(arr.shift());
  }
  return result;
}

function calculateFIFOProfit() {
  // 读取交易数据:假设列顺序为 日期|股票代码|交易类型|数量|单价,从第3行开始读取(跳过表头)
  const rawData = historySheet.getRange(3, 1, historySheet.getLastRow() - 2, 5).getValues();
  const stockQueues = {};
  const profitStats = {};

  // 按日期升序排序交易记录,保证交易顺序正确
  const sortedTrades = rawData.sort((a, b) => new Date(a[0]) - new Date(b[0]));

  sortedTrades.forEach(row => {
    const ticker = row[1].toString().trim();
    const action = row[2].toString().trim();
    const quantity = Math.round(Number(row[3])); // 数量取整,避免零碎股问题
    const price = Number(row[4]);

    // 初始化当前股票的队列和统计数据
    if (!stockQueues[ticker]) stockQueues[ticker] = [];
    if (!profitStats[ticker]) profitStats[ticker] = { realized: 0, totalCost: 0, profitRate: 0 };

    if (action === 'Buy') {
      // 买入:将对应数量的成本价推入队列
      pushTimes(quantity, stockQueues[ticker], price);
    } else if (action === 'Sell') {
      // 卖出:从队列头部取出对应数量的成本
      const costList = shiftTimes(quantity, stockQueues[ticker]);
      const currentCost = costList.reduce((sum, cost) => sum + cost, 0);
      const currentSellIncome = quantity * price;
      const currentProfit = currentSellIncome - currentCost;

      // 更新统计数据
      profitStats[ticker].realized += currentProfit;
      profitStats[ticker].totalCost += currentCost;
      if (profitStats[ticker].totalCost > 0) {
        profitStats[ticker].profitRate = (profitStats[ticker].realized / profitStats[ticker].totalCost * 100).toFixed(2) + '%';
      }
    }
  });

  // 格式化输出数据到output表
  const outputHeaders = ['股票代码', '累计已实现收益', '累计抵扣成本', '收益率'];
  const outputRows = [outputHeaders];
  Object.entries(profitStats).forEach(([ticker, stats]) => {
    outputRows.push([
      ticker,
      stats.realized.toFixed(2),
      stats.totalCost.toFixed(2),
      stats.profitRate
    ]);
  });

  // 清空旧数据写入新结果
  outputSheet.clearContents();
  outputSheet.getRange(1, 1, outputRows.length, outputRows[0].length).setValues(outputRows);
}

逻辑验证

以你给出的示例为例:

1月买入5股单价10美元,2月买入8股单价15美元,后续以23美元单价卖出7股
代码运行后对应股票的统计结果为:

  • 累计抵扣成本:(510)+(215) = 80美元
  • 累计已实现收益:7*23 - 80 = 81美元
  • 收益率:81/80 = 101.25%
    完全符合FIFO规则要求。

调整说明

如果你的表格列顺序与代码假设不一致,只需调整rawData读取时的列索引,以及row[x]的取值序号即可适配。

内容的提问来源于stack exchange,提问作者Pashtun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 15:06:01