计算没有辅助列/表的平均投资价格
Posted
技术标签:
【中文标题】计算没有辅助列/表的平均投资价格【英文标题】:Calculate average invested price without helper columns/tables 【发布时间】:2021-07-04 08:54:57 【问题描述】:这篇文章真的值得一读。这个主题的另一个更好的版本是here。
如果您查看下表,我会尝试在不添加辅助列的情况下查找每笔交易的平均价格。当边是 Buy 但在 Sell 边显示不正确的平均价格时,平均价格是正确的,我正在为其寻找平均价格的公式、数组公式或 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 | 10150 |
价值的公式是=E3*D3
,持有的公式是=SUM($D$3:D3)
,平均价格的公式是=SUMPRODUCT($D$3:D3,$E$3:E3)/SUM($D$3:$D3)
我往下拖。除了最后一个值 10150 之外,一切似乎都是正确的。根据下面给出的 FIFO 逻辑,理想情况下它应该有 10,100。
第一个订单:数量 = 225 |价格 = 卢比。 10,000.00
二阶:数量 = 75 |价格 = 卢比。 10,200.00
要计算平均价格,首先要计算价值(数量 x 价格)。因此:
第一笔交易:卢比。 22,50,000.00
第二笔交易:卢比。 7,65,000.00
总数量 = 300
前两个订单的总价值:卢比。 30,15,000.00
总值除以总量:
卢比。 30,15,000.00 ÷ 300 = Rs.10,050.00(使用=sumproduct
公式计算)
7 月 3 日,我们下了 150 个卖单(共 300 个)。价格:卢比。 9,950.00
现在这里将应用 FIFO(先进先出)方法。该方法将检查第一笔交易(在买方)。在这种情况下,它是 225。150 卖出的股票将从 225 中扣除(第一次持有)。第一次持有的余额是 225,现在是 225 - 150 = 75
先进先出后,表格扣除卖出数量后变成这样转换。看到第一个 Qty 从 225 更改为 75,因为 150 个库存已售出。
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,则将转移到下一笔交易以扣除剩余数量。
现在要获得解决方案,需要额外的辅助列或辅助表,我希望消除它们并找到公式或数组公式或 UDF 来计算平均价格。我请求 excel 专家帮助我解决这个问题。
下面给出了我正在尝试的另一个示例,其中投资价格显示不正确:
Date | Side | Qty | Price | Value | Holding | Avg Price |
---|---|---|---|---|---|---|
1-Jul | Buy | 5 | 10 | 50 | 5 | 10 |
2-Jul | Sell | -3 | 17 | -51 | 2 | -0.5 |
3-Jul | Buy | 17 | 3 | 51 | 19 | 2.63 |
4-Jul | Sell | -15 | 7.8 | -117 | 4 | -16.75 |
编辑
从@Tom Sharpe 获得解决方案后完成
为了获得平均价格,我将两个变量 avgRate 和 sumRate 声明为 double 并稍微修改了 For Each
代码。请告知是否有有效的方法来做到这一点。感谢这是否可以转换为 UDF,这样我就不必一次又一次地运行代码。非常感谢您提供的出色解决方案。
For Each bs In queue
Debug.Print ("qty=" & bs.qty)
Debug.Print ("rate=" & bs.rate)
avgRate = avgRate + (bs.qty * bs.rate)
sumRate = sumRate + bs.qty
Debug.Print avgRate / sumRate
Next
【问题讨论】:
正确答案是什么?如何回答? 我认为投资价格是 3,因为在第二次交易中,第一次购买时卖出了 3 股,后来在第四次交易中卖出了 15 股,其中 2 股以 10 的价格被卖出,其余的以 3 的价格购买。因此,剩下的任何数量都以 3 的价格购买。我可能没有很好地解释它,FIFO(先进先出)方法的解释在我发布的链接中给出。感谢您的回复 请参考我在原帖中写的小编辑。谢谢 "我尝试了几个公式,例如 sumif、sumproduct、sumif 和 sumproduct 的组合,但没有得到正确的答案。"分享您的尝试并解释您遇到困难的地方。 这是预期的结果吗?=SUMPRODUCT(A2:A5,C2:C5)/SUM(C2:C5)
或 =SUM(D2:D5)/SUM(C2:C5)
【参考方案1】:
好吧,这里是 VBA 实现的测试版本。
算法:
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.
程序使用了 BuySell 类,因此您需要创建一个类模块,将其重命名为 BuySell 并包含这些行
Public rate As Double
Public qty As Double
以下内容进入普通模块。
Option Explicit
Sub FifoTrading()
' 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
For i = 2 To 5
Debug.Print (Cells(i, 3).Value())
Debug.Print (Cells(i, 4).Value())
rate = Cells(i, 4).Value()
qty = Cells(i, 3).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
For Each bs In queue
Debug.Print ("qty=" & bs.qty)
Debug.Print ("rate=" & bs.rate)
Next
avRate = 0
totQty = 0
For Each bs In queue
avRate = avRate + bs.qty * bs.rate
totQty = totQty + bs.qty
Next
avRate = avRate / totQty
Debug.Print ("average=" & avRate)
End Sub
对于第一个表,输出是
所以平均比率是 10100。
对于第二个表,输出是
所以平均比率是 3。
编辑
这里是 UDF 版本,被称为
=avRate(qtyRange,rateRange)
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
【讨论】:
感谢 vba。希望你现在感觉好多了。我很高兴了解算法。当我尝试运行它时,它在Set bs = New BuySell
线上给了我错误我认为我需要设置一个引用,所以我尝试用谷歌搜索系统收集队列并尝试设置对mscorlib
的引用(C:\windows\ microsoft.net\framework\v4.0.30319\mscorlib.tlb), ms wmi scripting v1.2 lib
& ms scripting runtime libraries
但似乎没有任何效果。你能告诉我可能是什么问题吗?
是的,我错了,我忘了说您需要为 BuySell 类添加一个类模块 - 将其添加到我的答案中。
谢谢.. 我需要一些时间来了解它是如何工作的。我将研究算法,并用更多的样本对此进行测试,并很快回复您。感谢您节省宝贵的时间。
很高兴。我可以轻松地将其更改为 UDF,这样会在适当的时候更方便。
如果可以做到,那么它也会对社区有所帮助。在此之前,我可能会有一些疑问,尤其是错误处理。我必须说,这似乎是我以前从未见过的收集队列方法的一个很好的逻辑。我仍在深入研究代码以更好地理解它。我可能很快就要回来找你了。以上是关于计算没有辅助列/表的平均投资价格的主要内容,如果未能解决你的问题,请参考以下文章