【发布时间】:2021-07-12 17:50:05
【问题描述】:
这篇文章是我earlier post 的延续,我已经从Tom Sharpe 那里获得了有关如何根据下面给出的交易表和 UDF 使用 FIFO 方法计算股票平均价格的帮助。为了给它添加更多功能,我试图通过调整 UDF 来计算我的损益,但我没有成功,因此我为此创建了一个新线程。
盈亏分为两部分。一个是我卖出少量股票所获得的利润/损失,称为已实现收益,第二个是在证券交易所可用于我未售出股票的收益,称为未实现收益。如果出现亏损而不是盈利,两者都可能变为负数。
计算未实现的收益相当简单,因为已经提供了解决方案,答案是剩余数量 x 平均价格。参考表格,150 x 10 100 = 1 515 000(我认为应该这样计算 - 如果我错了,请纠正我)。但是计算Realized Gain是我面临的挑战。根据表格,已实现收益为 -7 500,这是一个损失,计算为 (Sold Price - First Price) x Sold Quantity(希望这个逻辑背后的数学也是正确)。另外,当交易数量增加时,我面临着更大的困难。
简而言之,我期待拥有 3 样东西。投资平均价格(UDF 已经给出),未实现利润(可以根据 UDF 计算)。需要知道如何计算 Realized Profit 以及是否可以通过在公式中添加参数使用相同的 UDF 返回所有三项。
这是桌子
| Date | Side | Qty | Price | Value | Holding | Avg Price |
|---|---|---|---|---|---|---|
| 1-Jul | Buy | 225 | 10000 | 2250000 | 225 | 10000 |
| 2-Jul | Buy | 75 | 10200 | 765000 | 300 | 10050 |
| 3-Jul | Sell | -150 | 9950 | -1492500 | 150 | 10100 |
下面是解释
-
第一个订单:数量 = 225 |价格 = 卢比。 10 000.00
-
二阶:数量 = 75 |价格 = 卢比。 10 200.00
要计算平均价格,首先要计算价值(数量 x 价格)。因此:
-
第一笔交易:卢比。 2 250 000.00
-
第二笔交易:卢比。 765 000.00
-
总数量 = 300
-
前两个订单的总价值:卢比。 3 015 000.00
现在问题来了。 7 月 3 日,我们下了一个卖单 150(共 300 个)@价格:卢比。 9 950.00
现在这里将应用 FIFO(先进先出)方法。该方法将检查第一笔交易(在买方)。在这种情况下,它是 225。150 卖出的股票将从 225 中扣除(第一次持有)。第一次持有的余额是 225,现在是 225 - 150 = 75
先进先出后,表格扣除卖出数量后变成这样转换。看到第一个数量从 225 变为 75,因为卖出了 150 只股票,因此平均价格为 10100(我可以从下面的 UDF 中得到它。
| Date | Side | Qty | Price | Value | Holding | Avg Price |
|---|---|---|---|---|---|---|
| 1-Jul | Buy | 75 | 10000 | 750000 | 75 | 10000 |
| 2-Jul | Buy | 75 | 10200 | 765000 | 150 | 10100 |
如果卖出数量超过225,则转移到下一笔交易以扣除剩余数量
感谢Tom Sharpe 提供这个称为=avgRate(qtyRange,rateRange) 的UDF
程序使用了 BuySell 类,因此您需要创建一个类模块,将其重命名为 BuySell 并包含这些行
Public rate As Double
Public qty As Double
这里是 UDF
Function avgRate(qtyRange As Range, rateRange As Range)
' Create the queue
Dim queue As Object
Set queue = CreateObject("System.Collections.Queue") 'Create the Queue
' Declare some variables
Dim bs As Object
Dim qty As Double
Dim rate As Double
Dim qtySold As Double
Dim qtyBought As Double
Dim qtyRemaining As Double
Dim rateBought As Double
Dim i As Long
Dim sumRate As Double, totQty As Double
For i = 1 To qtyRange.Cells().Count
qty = qtyRange.Cells(i).Value()
rate = rateRange.Cells(i).Value()
If qty > 0 Then
'Buy
Set bs = New BuySell
bs.rate = rate
bs.qty = qty
queue.Enqueue bs
Else
'Sell
qtyRemaining = -qty
'Work through the 'buy' transactions in the queue starting at the oldest.
While qtyRemaining > 0
If qtyRemaining < queue.peek().qty Then
'More than enough stocks in this 'buy' to cover the sale so just work out what's left
queue.peek().qty = queue.peek().qty - qtyRemaining
qtyRemaining = 0
ElseIf qtyRemaining = queue.peek().qty Then
'Exactly enough stocks in this 'buy' to cover the sale so remove from queue
Set bs = queue.dequeue()
qtyRemaining = 0
Else
'Not enough stocks in this 'buy' to cover the sale so remove from queue and reduce amount of sale remaining
Set bs = queue.dequeue()
qtyRemaining = qtyRemaining - bs.qty
End If
Wend
End If
Next i
'Calculate average rate over remaining stocks
sumRate = 0
totQty = 0
For Each bs In queue
sumRate = sumRate + bs.qty * bs.rate
totQty = totQty + bs.qty
Next
avgRate = sumRate / totQty
End Function
算法:
If 'buy' transaction, just add to the queue.
If 'sell' transaction (negative quantity)
Repeat
Take as much as possible from earliest transaction
If more is required, look at next transaction
until sell amount reduced to zero.
【问题讨论】:
-
未实现收益看起来很简单,但实现收益对我来说看起来很棘手。如果您像示例中那样卖出 150 只股票,这很简单,因为它们都来自您第一次购买 225 @ 10000。如果您卖出 250 股,我想 225 来自您的第一批 10000,其余 25 来自您的第二手在10200。然后如果你多买多卖,那就更复杂了。换句话说,在我看来,您似乎必须为每笔销售进行平均价格计算。您可能必须在队列中存储销售以及购买。
-
也许还不错,我认为您只需要在每次销售时进行平均价格计算,因此希望不会有太多额外代码。
-
感谢您的回复。你想象的没错。您是否尝试使用基于 udf 的公式而不是额外的编码来建议计算?假设我每次都进行平均价格计算,那么 UDF 会给我前 3 笔交易的 10000、10050 和 10100 平均价格。现在我第三次卖出时,显示的平均价格是 10100,在这种情况下,使用什么公式可以得到我的已实现收益(在这个例子中是损失)?抱歉,我没有得到你想要的建议......
-
不,对于每次销售,我们必须查看与该销售相关的股票的购买价格,并将它们的总成本从销售价格 * 数量中扣除,但我认为这只是意味着添加一些原始 UDF 的代码行。
-
同时我正在尝试添加一个额外的列来计算已实现的利润。使用相同的示例,我卖掉了所有 300 只股票而不是 150 只,这会导致错误。我应该添加哪一行以及在哪里使其结果为 0?即,当队列中的购买数量变为 0 或变为负数时。我试过
on error、if bs.qty<=0,但后来所有单元格都出错了。
标签: excel vba user-defined-functions stock fifo