使用基于交易历史的 FIFO 方法创建一个函数来计算股票的已实现收益
Creating a function to calculate realized gains of stock using FIFO method based on transactional history
我是 google 应用程序脚本的新手,正在尝试尽可能多地学习,但我遇到了一些我无法理解的事情......
我的目标是创建一个函数,根据交易历史的输入数据,以先进先出的会计风格“FIFO”计算股票的已实现收益。
计算已实现收益的公式相对简单
(Total Sell price - Original Cost ) = Realized gains/loss
Realized Gains/Loss / Original Cost = Profit/loss (%)
如果有人在三个不同的时间以三个不同的价格 (x1,x2,x3) 购买了一只股票,而您希望计算 'realized gains' - 您是采用 x1、x2 还是 x3 的原始成本? FIFO 样式将采用 X1,因为它是 'first in, first out'。这很重要,因为在美国有一种基于所有已实现收益的资本利得税。
这是我的数据:https://docs.google.com/spreadsheets/d/1V7CpCxBH0lg6wi1TAhfZJP5gXE8hj7ivQ8_ULxLSLgs/edit?usp=sharing
const ss = SpreadsheetApp.getActiveSpreadsheet();
const historySheet = ss.getSheetByName('Blad1');
function fifoProject () {
let input = historySheet.getDataRange().getValues();
let onlyBuyRows = input.filter(row => row[2] == 'Buy');
let roundedTotal, priceQuantityMultiplication;
let onlyColABDF = onlyBuyRows.map(row => {
roundedTotal = Math.round(row[5] * 100) / 100;
priceQuantityMultiplication = row[3] * roundedTotal;
return [
row[0], row[1], row[3], roundedTotal, priceQuantityMultiplication
]});
let sorted = onlyColABDF.sort((a, b) => {
if (a[1] > b[1]) return 1;
if (a[1] < b[1]) return -1;
if (a[0] > b[0]) return 1;
if (a[0] < b[0]) return -1;
return 0;
});
let arrayBuyData = [];
arrayBuyData.push(sorted);
console.log(arrayBuyData);
//ss.getSheetByName('output').getRange(1, 1, sorted.length, sorted[0].length).setValues(sorted)
let input2 = historySheet.getRange(2, 1, historySheet.getLastRow() - 1, 4).getValues();
let onlySellRows = input2.filter(row => row[2] == 'Sell');
let sellTotalArray = []
let addAllSellObject = onlySellRows.reduce((acc, curr) => {
if (curr[1] in acc) {
acc[curr[1]] += curr[3]
} else {
acc[curr[1]] = curr[3]
}
return acc;
}, {});
let addAllSellArray = Object.entries(addAllSellObject).sort((a, b) => {
if (a[0] > b[0]) return 1;
if (a[0] < b[0]) return -1;
return 0;
})
sellTotalArray.push(addAllSellArray);
console.log(sellTotalArray[0][1]);
}
这是我认为函数应该做的:
我们非常欢迎任何方向正确的想法。
编辑:试图澄清这个问题。希望这样做:
<style>
.demo {
border:1px solid #C0C0C0;
border-collapse:collapse;
padding:5px;
}
.demo th {
border:1px solid #C0C0C0;
padding:5px;
background:#F0F0F0;
}
.demo td {
border:1px solid #C0C0C0;
padding:5px;
}
</style>
<table class="demo">
<caption></caption> <thead> <tr> <th>Date</th>
<th>Type</th>
<th>Amount p.p</th>
<th>Quantity</th>
</tr>
</thead>
<tbody>
<tr>
<td> Jan</td>
<td> Buy</td>
<td> </td>
<td> 5</td>
</tr>
<tr>
<td> Feb</td>
<td> Buy</td>
<td> </td>
<td> 8</td>
</tr>
</tbody>
</table>
例如,如果 7 件以 23 美元的价格售出,则结果为:
<style>
.demo {
border:1px solid #C0C0C0;
border-collapse:collapse;
padding:5px;
}
.demo th {
border:1px solid #C0C0C0;
padding:5px;
background:#F0F0F0;
}
.demo td {
border:1px solid #C0C0C0;
padding:5px;
}
</style>
<table class="demo">
<caption></caption> <thead> <tr> <th>Date</th>
<th>Amount</th>
<th>Buy price p.p</th>
<th>Profit p.p</th>
</tr> </thead> <tbody> <tr>
<td> Jan</td>
<td> 5</td>
<td> </td>
<td> </td>
</tr>
<tr>
<td> Feb</td>
<td> 2</td>
<td> </td>
<td> </td>
</tr>
</tbody>
</table>
总计:
<style>
.demo {
border:1px solid #C0C0C0;
border-collapse:collapse;
padding:5px;
}
.demo th {
border:1px solid #C0C0C0;
padding:5px;
background:#F0F0F0;
}
.demo td {
border:1px solid #C0C0C0;
padding:5px;
}
</style>
<table class="demo">
<caption></caption> <thead> <tr> <th><br></th>
<th><br></th>
</tr>
</thead>
<tbody>
<tr>
<td> Total buy price</td>
<td> </td>
</tr>
<tr>
<td> Total sell price</td>
<td> 1</td>
</tr>
<tr>
<td> Total profit (or loss)</td>
<td> </td>
</tr>
<tbody>
</table>
编辑 2:根据 Jacques 的回答,我创建了以下代码:
const ss = SpreadsheetApp.getActiveSpreadsheet();
const historySheet = ss.getSheetByName('Blad1');
// Function which pushes item x amount of times based on a number
function pushNumber (number, array, item) {
for(var i = 0; i<number; i++){
array.push(item);
}
}
function shiftNumber (number, array, item) {
for(var i = 0; i<number; i++){
array.shift(item);
}
}
function projectFIFO () {
let input = historySheet.getRange(3,1,historySheet.getLastRow()-1, 5).getValues();
for (var i = 0; i<security.length; i++) {
let action = row[2].toString();
let quantity = Number(row[3]);
let price = Number(row[4]);
let emptyArray = [];
// console.log(quantity);
for (security of array)
if (action === 'Buy') {
let numberPush = quantity;
pushNumber(quantity, emptyArray, price);
};
console.log(emptyArray);
};
}
基本上我创建了一个辅助函数 pushNumber,它根据 Number 的数量进行推送。然后在 fifo 函数中,我创建了一个 for 循环来遍历每一行,看看是否有买入并根据数量推送买入,推送在一个空数组中。但是,我想学习如何分别为每个证券而不是整个范围执行此操作。我想也许一个对象是一个解决方案,但还没有想出来。
在研究了您的代码和示例后 Sheet 我发现您非常接近实现您的目标。您只需编写一个与您的 Transactions table 交互的函数,以获得所需结果的数据 table.
在第一次交互中,过滤 Transaction table 以仅显示您想要的票证(就像您在示例脚本中所做的那样)。继续从上到下交互table(可以使用getRange()
/getValues()
as shown in your example). Now, for every Buy row, push()
the price as many times as shares bought. So for example if you bought 3 shares at 3, you should push 313, 313, 313
. For every Sell order you only have to shift()
数组,计算返回值(买入价)和卖出价的差值。
通过这种方法,您将有效地开发管理买卖订单的 FIFO 逻辑,因此它们之间的差额将是已实现的利润。如果您对此方法有疑问,请随时发表评论。
更新
您可以使用此方法从数组中删除重复项。如果您事先不知道证券的名称,这将很有用。
var allTickers = ['AAPL', 'AAPL', 'GOOG', 'MSFT', '2222.SR', 'MSFT', 'AMZN',
'AAPL', '2222.SR', 'MSFT', 'AMZN', '2222.SR'
]
var uniqueTickers = [...new Set(allTickers)];
我是 google 应用程序脚本的新手,正在尝试尽可能多地学习,但我遇到了一些我无法理解的事情......
我的目标是创建一个函数,根据交易历史的输入数据,以先进先出的会计风格“FIFO”计算股票的已实现收益。
计算已实现收益的公式相对简单
(Total Sell price - Original Cost ) = Realized gains/loss
Realized Gains/Loss / Original Cost = Profit/loss (%)
如果有人在三个不同的时间以三个不同的价格 (x1,x2,x3) 购买了一只股票,而您希望计算 'realized gains' - 您是采用 x1、x2 还是 x3 的原始成本? FIFO 样式将采用 X1,因为它是 'first in, first out'。这很重要,因为在美国有一种基于所有已实现收益的资本利得税。
这是我的数据:https://docs.google.com/spreadsheets/d/1V7CpCxBH0lg6wi1TAhfZJP5gXE8hj7ivQ8_ULxLSLgs/edit?usp=sharing
const ss = SpreadsheetApp.getActiveSpreadsheet();
const historySheet = ss.getSheetByName('Blad1');
function fifoProject () {
let input = historySheet.getDataRange().getValues();
let onlyBuyRows = input.filter(row => row[2] == 'Buy');
let roundedTotal, priceQuantityMultiplication;
let onlyColABDF = onlyBuyRows.map(row => {
roundedTotal = Math.round(row[5] * 100) / 100;
priceQuantityMultiplication = row[3] * roundedTotal;
return [
row[0], row[1], row[3], roundedTotal, priceQuantityMultiplication
]});
let sorted = onlyColABDF.sort((a, b) => {
if (a[1] > b[1]) return 1;
if (a[1] < b[1]) return -1;
if (a[0] > b[0]) return 1;
if (a[0] < b[0]) return -1;
return 0;
});
let arrayBuyData = [];
arrayBuyData.push(sorted);
console.log(arrayBuyData);
//ss.getSheetByName('output').getRange(1, 1, sorted.length, sorted[0].length).setValues(sorted)
let input2 = historySheet.getRange(2, 1, historySheet.getLastRow() - 1, 4).getValues();
let onlySellRows = input2.filter(row => row[2] == 'Sell');
let sellTotalArray = []
let addAllSellObject = onlySellRows.reduce((acc, curr) => {
if (curr[1] in acc) {
acc[curr[1]] += curr[3]
} else {
acc[curr[1]] = curr[3]
}
return acc;
}, {});
let addAllSellArray = Object.entries(addAllSellObject).sort((a, b) => {
if (a[0] > b[0]) return 1;
if (a[0] < b[0]) return -1;
return 0;
})
sellTotalArray.push(addAllSellArray);
console.log(sellTotalArray[0][1]);
}
这是我认为函数应该做的:
我们非常欢迎任何方向正确的想法。
编辑:试图澄清这个问题。希望这样做:
<style>
.demo {
border:1px solid #C0C0C0;
border-collapse:collapse;
padding:5px;
}
.demo th {
border:1px solid #C0C0C0;
padding:5px;
background:#F0F0F0;
}
.demo td {
border:1px solid #C0C0C0;
padding:5px;
}
</style>
<table class="demo">
<caption></caption> <thead> <tr> <th>Date</th>
<th>Type</th>
<th>Amount p.p</th>
<th>Quantity</th>
</tr>
</thead>
<tbody>
<tr>
<td> Jan</td>
<td> Buy</td>
<td> </td>
<td> 5</td>
</tr>
<tr>
<td> Feb</td>
<td> Buy</td>
<td> </td>
<td> 8</td>
</tr>
</tbody>
</table>
例如,如果 7 件以 23 美元的价格售出,则结果为:
<style>
.demo {
border:1px solid #C0C0C0;
border-collapse:collapse;
padding:5px;
}
.demo th {
border:1px solid #C0C0C0;
padding:5px;
background:#F0F0F0;
}
.demo td {
border:1px solid #C0C0C0;
padding:5px;
}
</style>
<table class="demo">
<caption></caption> <thead> <tr> <th>Date</th>
<th>Amount</th>
<th>Buy price p.p</th>
<th>Profit p.p</th>
</tr> </thead> <tbody> <tr>
<td> Jan</td>
<td> 5</td>
<td> </td>
<td> </td>
</tr>
<tr>
<td> Feb</td>
<td> 2</td>
<td> </td>
<td> </td>
</tr>
</tbody>
</table>
总计:
<style>
.demo {
border:1px solid #C0C0C0;
border-collapse:collapse;
padding:5px;
}
.demo th {
border:1px solid #C0C0C0;
padding:5px;
background:#F0F0F0;
}
.demo td {
border:1px solid #C0C0C0;
padding:5px;
}
</style>
<table class="demo">
<caption></caption> <thead> <tr> <th><br></th>
<th><br></th>
</tr>
</thead>
<tbody>
<tr>
<td> Total buy price</td>
<td> </td>
</tr>
<tr>
<td> Total sell price</td>
<td> 1</td>
</tr>
<tr>
<td> Total profit (or loss)</td>
<td> </td>
</tr>
<tbody>
</table>
编辑 2:根据 Jacques 的回答,我创建了以下代码:
const ss = SpreadsheetApp.getActiveSpreadsheet();
const historySheet = ss.getSheetByName('Blad1');
// Function which pushes item x amount of times based on a number
function pushNumber (number, array, item) {
for(var i = 0; i<number; i++){
array.push(item);
}
}
function shiftNumber (number, array, item) {
for(var i = 0; i<number; i++){
array.shift(item);
}
}
function projectFIFO () {
let input = historySheet.getRange(3,1,historySheet.getLastRow()-1, 5).getValues();
for (var i = 0; i<security.length; i++) {
let action = row[2].toString();
let quantity = Number(row[3]);
let price = Number(row[4]);
let emptyArray = [];
// console.log(quantity);
for (security of array)
if (action === 'Buy') {
let numberPush = quantity;
pushNumber(quantity, emptyArray, price);
};
console.log(emptyArray);
};
}
基本上我创建了一个辅助函数 pushNumber,它根据 Number 的数量进行推送。然后在 fifo 函数中,我创建了一个 for 循环来遍历每一行,看看是否有买入并根据数量推送买入,推送在一个空数组中。但是,我想学习如何分别为每个证券而不是整个范围执行此操作。我想也许一个对象是一个解决方案,但还没有想出来。
在研究了您的代码和示例后 Sheet 我发现您非常接近实现您的目标。您只需编写一个与您的 Transactions table 交互的函数,以获得所需结果的数据 table.
在第一次交互中,过滤 Transaction table 以仅显示您想要的票证(就像您在示例脚本中所做的那样)。继续从上到下交互table(可以使用getRange()
/getValues()
as shown in your example). Now, for every Buy row, push()
the price as many times as shares bought. So for example if you bought 3 shares at 3, you should push 313, 313, 313
. For every Sell order you only have to shift()
数组,计算返回值(买入价)和卖出价的差值。
通过这种方法,您将有效地开发管理买卖订单的 FIFO 逻辑,因此它们之间的差额将是已实现的利润。如果您对此方法有疑问,请随时发表评论。
更新
您可以使用此方法从数组中删除重复项。如果您事先不知道证券的名称,这将很有用。
var allTickers = ['AAPL', 'AAPL', 'GOOG', 'MSFT', '2222.SR', 'MSFT', 'AMZN',
'AAPL', '2222.SR', 'MSFT', 'AMZN', '2222.SR'
]
var uniqueTickers = [...new Set(allTickers)];