计算没有辅助列/表的平均投资价格

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,这样会在适当的时候更方便。 如果可以做到,那么它也会对社区有所帮助。在此之前,我可能会有一些疑问,尤其是错误处理。我必须说,这似乎是我以前从未见过的收集队列方法的一个很好的逻辑。我仍在深入研究代码以更好地理解它。我可能很快就要回来找你了。

以上是关于计算没有辅助列/表的平均投资价格的主要内容,如果未能解决你的问题,请参考以下文章

python 计算时间加权平均价格:权衡近期价格

有没有办法在 WPF 中求和或平均属性?

投资之技术篇

用numpy计算成交量加权平均价格(VWAP),并实现读写文件

每天的平均销售价格

python 计算交易量加权平均价格