R使用先进先出法计算总收益或损失

R使用先进先出法计算总收益或损失,r,R,我有两个数据集,一个是买入股票的详细信息,称为buy,另一个是卖出股票的详细信息,称为sell buy = data.frame(TransactionID = c(1:10), Ticker=c(rep('MSFT',4),rep('AMZN',3),rep('DOCU',3)), Date=c(rep('01-01-2020',2),rep('01-14-2020',2),rep('01-01-2020',2),rep('0

我有两个数据集,一个是买入股票的详细信息,称为
buy
,另一个是卖出股票的详细信息,称为
sell

buy = data.frame(TransactionID = c(1:10),
                 Ticker=c(rep('MSFT',4),rep('AMZN',3),rep('DOCU',3)),
                 Date=c(rep('01-01-2020',2),rep('01-14-2020',2),rep('01-01-2020',2),rep('01-14-2020',1),'01-01-2020','03-15-2020','04-06-2020'),
                 Price=c(100,102,102,107,2000,2010,2011,197,182,167),
                 Quantity=c(10,10,5,5,1,1,2,12,15,15))

sell = data.frame(TransactionID=c(1:7),
                  Ticker=c('MSFT','MSFT','AMZN','AMZN','DOCU','DOCU','DOCU'),
                  Date=c('01-07-2020','01-20-2020','01-01-2020','01-30-2020','01-15-2020','04-10-2020','04-20-2020'),
                  Price=c(97,110,2100,2050,210,205,225),
                  Quantity=c(7,12,1,3,10,5,3))
日期
mm dd YYYY

我的目标是使用先进先出(FIFO)(先进先出)方法计算数据中包含的时间段内所有交易的总损益

我试图使用R以编程方式完成这项工作,但尚未成功

方法和所需输出

我尝试手动进行计算,以演示FIFO计算方法和最终结果。我一直在寻找一种使用R实现这一点的方法,但还没有找到一种成功的方法-

1) 01-07-2020 - Ticker= MSFT - Sold 7 @ $97 - Total = $679
                               FIFO Cost of 7 @ $100 - Total = $700
                               Gain/Loss = -$21

2) 01-01-2020 - Ticker=AMZN - Sold 1 @ $2100 - Total = $2100
                              FIFO Cost of 1 @ $2000 - Total = $2000
                              Gain/Loss = +$100

3) 01-15-2020 - Ticker=DOCU - Sold 10 @ $210 - Total = $2100
                              FIFO Cost of 10 @ $197 - Total = $1970
                              Gain/Loss = +$130

4) 01-20-2020 - Ticker=MSFT - Sold 12 @ $110 - Total = $1320
                              FIFO Cost of 12 @ $[3x$100 + 9x$102] - Total = $1218
                              Gain/Loss = +$102

5) 01-30-2020 - Ticker=AMZN - Sold 3 @ $2050 - Total = $6150
                              FIFO Cost of 3 @ $[1x$2010 + 2x$2011] - Total = $6032
                              Gain/Loss = +$118

6) 04-10-2020 - Ticker=DOCU - Sold 5 @ $205 - Total = $1025
                              FIFO Cost of 5 @ $[2x$197 + 3x$182] - Total = $940
                              Gain/Loss = +$85

7) 04-20-2020 - Ticker=DOCU - Sold 3 @ $225 - Total = $675
                              FIFO Cost of 3 @ $182 - Total = $546
                              Gain/Loss = +$129
输出

损失总额:21美元 ,总收益:$664

净收益为643美元


对于如何以编程方式获得此答案的任何帮助,我们都将不胜感激。

有点混乱,但很有效

# First of all, let's convert date to Date class:
buy$Date <- as.Date(buy$Date, '%m-%d-%Y')
sell$Date <- as.Date(sell$Date, '%m-%d-%Y')

# Empty df that will have results of each transaction:
result <- data.frame()

# For each line in Sell df we are going to run this loop

for (i in 1:nrow(sell)) {
    
    print(sell[i, ])
    # Create a temporary table by filtering 'buy' df to the right Ticker and dates before or equal to Sell operation
    temp <- buy %>%
      filter(Date <= sell$Date[i],
             Ticker == sell$Ticker[i])
    
    j = 1
    sellQ = sell$Quantity[i]
    FIFO = 0
    
    # Running a while loop on temp table, iteratively updating FIFO
    while (sellQ > 0) {
      if (sellQ <= temp$Quantity[j]) {
        temp$Quantity[j] = temp$Quantity[j] - sellQ 
        FIFO = FIFO + (sellQ * temp$Price[j])
        sellQ = 0
      } else {
        FIFO = FIFO + (temp$Quantity[j] * temp$Price[j])
        sellQ = sellQ - temp$Quantity[j]
        temp$Quantity[j] = 0 
        j = j + 1
      }
    }
    
    SoldTotal <- sell$Price[i] * sell$Quantity[i]
    gain_loss <- SoldTotal - FIFO
    
    # Creating output line:
    output <- data.frame(round = i,
                         Ticker = sell$Ticker[i],
                         FIFO = FIFO,
                         SoldTotal = SoldTotal,
                         gain_loss = gain_loss)
    
    # Adding output line to result df:
    result <- rbind(result, output)
    
    # Updating buy table
    buy <- buy %>%
      filter(!TransactionID %in% temp$TransactionID) %>%
      bind_rows(temp) %>%
      arrange(TransactionID) %>%
      filter(Quantity != 0)
    
    print('Completed successfully')
}

根据注释,两个表中可能都有非整数
quantity
字段。在这个场景中,在继续之前,建议进行一个小的调整。假设数量字段可以四舍五入到2位小数。那么在这种情况下,请这样做

buy$Quantity <- as.integer(round(buy$Quantity*100))
buy$Price <- buy$Price/100

sell$Price <- sell$Price/100
sell$Quantity <- as.integer(round(sell$Quantity * 100))

方法2(使用purrr::map*)

buy=data.frame(交易ID=c(1:10),
股票代码=c(代表('MSFT',4),代表('AMZN',3),代表('DOCU',3)),
日期=c(代表('01-01-2020',2),代表('01-14-2020',2),代表('01-01-2020',2),代表('01-14-2020',1),'01-01-2020','03-15-2020','04-06-2020'),
价格=c(10010210210720002010201197182167),
数量=c(10,10,5,5,1,1,2,12,15,15))
sell=data.frame(TransactionID=c(1:7),
股票代码=c('MSFT'、'MSFT'、'AMZN'、'AMZN'、'DOCU'、'DOCU'、'DOCU'),
日期=c('01-07-2020'、'01-20-2020'、'01-01-2020'、'01-30-2020'、'01-15-2020'、'04-10-2020'、'04-20-2020'),
价格=c(971102100200205225),
数量=c(7,12,1,3,10,5,3))
图书馆(tidyverse)
购买%>%
变异(日期=as.Date(日期,%m-%d-%Y'),
买入或卖出=1)%>%
绑定行(销售%>%
变异(日期=as.Date(日期,%m-%d-%Y'),
买入或卖出=-1))%>%
拆分(.$Ticker)%>%
map_dfr(~.x%>%未计数(数量)%>%
分组依据(买入或卖出)%>%
变异(d=行数())%>%
解组%>%
安排(d,-买入或卖出)%>%
变动(利润=累计金额(买入或卖出*-1*价格),
d2=rev(累积金额)(rev(累积金额)(rev(买入或卖出==-1))==1&买入或卖出==-1)))%>%
(d)组%>%
突变(TransactionID=last(TransactionID))%>%
分组人(交易ID)%>%
总结(股票代码=第一(股票代码),
先进先出=总额(价格*(买入或卖出==1)*d2),
售出=总额(价格*(买入或卖出==-1)*d2),.groups='drop')
) %>%
过滤器(FIFO!=0 |已售出!=0)%>%
分组人(交易ID)%>%
变异(增益=售出-先进先出)%>%
总结(跨越(先进先出:收益,~sum())
#>#tibble:7 x 4
#>TransactionID FIFO售出收益
#>              
#> 1             1   700   679   -21
#> 2             2  1218  1320   102
#> 3             3  2000  2100   100
#> 4             4  6032  6150   118
#> 5             5  1970  2100   130
#> 6             6   940  1025    85
#> 7             7   546   675   129

由(v2.0.0)于2021年5月28日创建

您如何计算先进先出成本?公式是什么?每个股票的先进先出成本可以在
buy
数据集中找到。由于是先进先出的,在计算净损益时,首先考虑最早购买股票的价格,然后考虑以后的交易,例如票证MSFT。对于上面列出的第1)点,7股股票的成本为每股100美元,因为这是购买前7股MSFT股票的价格。对于上市的第4)点,MSFT的12股股票的成本被拆分,因为12股股票中的3股是从以每股100美元收购的第一批股票中提取的,其余9股的价格为每股102美元,因为它们是后来以更高的价格购买的。希望这澄清了计算方法OK我得到了正确的结果。我现在将发布我的解决方案,但我对操作顺序有点困惑。为什么AMZN sale排在第二位,而它是sell df中的第三个事务?谢谢,但是当我尝试使用一个稍微大一点的数据集时,它似乎抛出了一个错误,该数据集是以同样的方式构造的。在if中,错误为
错误(salesQ我在循环中添加了try:except:in the loop,因此如果出现错误,它应该向您显示发生交易的销售部门的交易,并继续到下一笔交易。也许您可以进行调查。我假设对于某些交易来说,可能存在一个问题,即在销售日期之前没有足够的股票。谢谢,现在的错误是
try中出错:临时%filter(Date啊,对不起,我的糟糕,使用了python语法。我现在在循环的开头添加了一个Sell事务的打印输出。如果循环完成,它将返回“Completed successfully”。如果没有,您将看到错误发生后的行。谢谢!我想我找到了导致问题的缺少的行。请在<代码中提出最后一个请求>结果
数据集,你将如何包含
股票代码
?做得好!太棒了。我希望我能再次投票,lolI似乎在使用结构相同的更大数据集尝试这两种方法时出错了
错误:由于精度损失,无法从权重转换为。*位置:1、2、3
所需输出ut格式将与您以前的解决方案中的格式相同,添加了股票
股票代码
,这很好@AnilGoyalYour结果非常完美。谢谢,如果您可以使用您共享的解决方案更新您的答案,我也会为您提供最佳答案状态
> print(paste('Total Gain:', TotalGain))
[1] "Total Gain: 664"
> print(paste('Total Loss:', TotalLoss))
[1] "Total Loss: -21"
> print(paste('Net Gains:', NetGains))
[1] "Net Gains: 643"
> 
> print('Details:')
[1] "Details:"
> result
  round FIFO SoldTotal gain_loss
1     1  700       679       -21
2     2 1218      1320       102
3     3 2000      2100       100
4     4 6032      6150       118
5     5 1970      2100       130
6     6  940      1025        85
7     7  546       675       129
buy$Quantity <- as.integer(round(buy$Quantity*100))
buy$Price <- buy$Price/100

sell$Price <- sell$Price/100
sell$Quantity <- as.integer(round(sell$Quantity * 100))
buy %>% uncount(Quantity) %>%
  group_by(Ticker) %>%
  mutate(Price = cumsum(Price), 
         Quantity2 = row_number()) %>%
  right_join(sell %>%
               group_by(Ticker) %>%
               mutate(Quantity2 = cumsum(Quantity)), by = c('Ticker' = 'Ticker', 'Quantity2' = 'Quantity2')) %>% 
  mutate(CP = diff(c(0, Price.x))) %>%
  group_by(TransactionID.y) %>%
  summarise(CP = sum(CP),
            SP = sum(Price.y * Quantity),
            Profit = SP - CP)
# A tibble: 7 x 4
  TransactionID.y    CP    SP Profit
            <int> <dbl> <dbl>  <dbl>
1               1   700   679    -21
2               2  1218  1320    102
3               3  2000  2100    100
4               4  6032  6150    118
5               5  1970  2100    130
6               6   940  1025     85
7               7   546   675    129