英文:
I need to calculate profit/loss for given stock data set, ensuring that the first bought items are sold first
问题
考虑以下示例数据集。
日期 | 符号 | 数量 | 每单位价格 | 类型 |
---|---|---|---|---|
2022年7月7日 | REL2300PE | 200 | 50 | 买入 |
2022年7月7日 | IDBI2300PE | 200 | 50 | 卖出 |
2022年7月15日 | REL2300PE | 100 | 50 | 买入 |
2022年7月15日 | IDBI2300PE | 20 | 50 | 买入 |
2022年7月16日 | REL2300PE | 200 | 35 | 买入 |
2022年7月30日 | IDBI2300PE | 60 | 50 | 卖出 |
2022年7月30日 | REL2300PE | 450 | 45 | 卖出 |
2022年7月30日 | IDBI2300PE | 200 | 25 | 卖出 |
如果我们专注于股票符号'REL2300PE',它以450的数量出售,导致总销售价值为$20,250。现在,让我们计算这支股票的前450单位的成本价格。成本价格可以通过将与这支股票相关的每次买入交易的数量和每单位价格的乘积相加来计算。在这种情况下,前450单位的成本价格计算如下:200单位 * $50 + 100单位 * $50 + 150单位 * $35 = $20,250。由于销售价值和前450单位的成本价格相同($20,250),因此这支股票的盈亏应为0。
英文:
Consider the following sample dataset.
Date | symbol | qty | price per qty | type |
---|---|---|---|---|
07 July 2022 | REL2300PE | 200 | 50 | buy |
07 July 2022 | IDBI2300PE | 200 | 50 | sell |
15 July 2022 | REL2300PE | 100 | 50 | buy |
15 July 2022 | IDBI2300PE | 20 | 50 | buy |
16 July 2022 | REL2300PE | 200 | 35 | buy |
30 July 2022 | IDBI2300PE | 60 | 50 | sell |
30 July 2022 | REL2300PE | 450 | 45 | sell |
30 July 2022 | IDBI2300PE | 200 | 25 | sell |
If we focus on the stock symbol 'REL2300PE', it is being sold in quantities of 450, resulting in a total sale value of $20,250. Now, let's calculate the cost price for the first 450 units of this stock. The cost price can be calculated by summing up the product of the quantity and price per quantity for each buying trade associated with this stock. In this case, the cost price for the first 450 units is calculated as follows: 200 units * $50 + 100 units * $50 + 150 units * $35 = $20,250. Since the sale value and the cost price for the first 450 units are the same ($20,250), the profit/loss for this stock should be 0.
答案1
得分: 1
You could follow this algorithm:
- 计算当前行和所有前面行的销售和购买的累积总和,考虑负数的销售数量
- 计算当前行和所有后续行的销售累积总和,以获取最早购买数量的总销售物品
- 获取前两者之间的差异,并将其视为实际消耗数量
- 按符号/产品分组并计算利润/亏损
Input:
from pyspark.sql import Window
from pyspark.sql.functions import col, when, sum
data = [\
('05 July 2022', 'IDBI2300PE', 500, 45, 'buy'),\
('07 July 2022', 'REL2300PE', 200, 50, 'buy'),\
('07 July 2022', 'IDBI2300PE', 200, 50, 'sell'),\
('15 July 2022', 'REL2300PE', 100, 50, 'buy'),\
('15 July 2022', 'IDBI2300PE', 20, 50, 'buy'),\
('16 July 2022', 'REL2300PE', 200, 35, 'buy'),\
('20 July 2022', 'REL2300PE', 200, 45, 'sell'),\
('30 July 2022', 'IDBI2300PE', 60, 50, 'sell'),\
('30 July 2022', 'REL2300PE', 250, 45, 'sell'),\
('31 July 2022', 'IDBI2300PE', 200, 25, 'sell')]
df = spark.createDataFrame(data, ["Date", "symbol", "qty", "price", "type"])
Calculate consumed qty:
# 计算当前和所有前面行的销售和购买的累积总和
cum_sum_wind = Window.partitionBy('symbol').orderBy('Date').rangeBetween(Window.unboundedPreceding, 0)
df = df.withColumn('cum_sum', sum(when(col('type') == 'sell', -1 * col('qty'))\
.otherwise(col('qty'))).over(cum_sum_wind))
# 计算当前和所有后续行的销售累积总和
sell_cum_sum_wind = Window.partitionBy('symbol').orderBy('Date').rangeBetween(0, Window.unboundedFollowing)
df = df.withColumn('sell_cum_sum', sum(when(col('type') == 'sell', col('qty'))\
.otherwise(0)).over(sell_cum_sum_wind))
# 计算实际消耗数量
df = df.withColumn('cons_qty', when(col('type') == 'sell', col('qty'))\
.when(col('sell_cum_sum') > col('cum_sum'), col('qty'))\
# 如果为负数,则从此行中未消耗任何物品
.when((col('qty') - (col('cum_sum') - col('sell_cum_sum'))) < 0, 0)\
.otherwise(col('qty') - (col('cum_sum') - col('sell_cum_sum'))))
df.show()
+------------+----------+---+-----+----+-------+------------+--------+
| Date| symbol|qty|price|type|cum_sum|sell_cum_sum|cons_qty|
+------------+----------+---+-----+----+-------+------------+--------+
|05 July 2022|IDBI2300PE|500| 45| buy| 500| 460| 460|
|07 July 2022|IDBI2300PE|200| 50|sell| 300| 460| 200|
|15 July 2022|IDBI2300PE| 20| 50| buy| 320| 260| 0|
|30 July 2022|IDBI2300PE| 60| 50|sell| 260| 260| 60|
|31 July 2022|IDBI2300PE|200| 25|sell| 60| 200| 200|
|07 July 2022| REL2300PE|200| 50| buy| 200| 450| 200|
|15 July 2022| REL2300PE|100| 50| buy| 300| 450| 100|
|16 July 2022| REL2300PE|200| 35| buy| 500| 450| 150|
|20 July 2022| REL2300PE|200| 45|sell| 300| 450| 200|
|30 July 2022| REL2300PE|250| 45|sell| 50| 250| 250|
+------------+----------+---+-----+----+-------+------------+--------+
Calculate global profit/loss per symbol:
# 按符号分组并计算利润/亏损
result = df.groupby('symbol')\
.agg(
sum(when(col('type') == 'buy', -1 * col('price') * col('cons_qty'))\
.otherwise(col('price') * col('cons_qty'))\
).alias("profit_loss"))
result.show()
+----------+-----------+
| symbol|profit_loss|
+----------+-----------+
|IDBI2300PE| -2700|
| REL2300PE| 0|
+----------+-----------+
英文:
You could follow this algorithm:
- Calculate the cumulative sum for the sells and buys for the current row and all prev rows, and consider the sell qty with negative
- Calculate the cumulative sum for the sales only for the current row and all following rows, so you can get the total sold items at the oldest buy qty
- Get the difference between the first two and consider it as the actual consumed-qty
- Group by symbol/product and calculate the profit/loss
Input:
from pyspark.sql import Window
from pyspark.sql.functions import col, when, sum
data = [\
('05 July 2022', 'IDBI2300PE', 500, 45, 'buy'),\
('07 July 2022', 'REL2300PE', 200, 50, 'buy'),\
('07 July 2022', 'IDBI2300PE', 200, 50, 'sell'),\
('15 July 2022', 'REL2300PE', 100, 50, 'buy'),\
('15 July 2022', 'IDBI2300PE', 20, 50, 'buy'),\
('16 July 2022', 'REL2300PE', 200, 35, 'buy'),\
('20 July 2022', 'REL2300PE', 200, 45, 'sell'),\
('30 July 2022', 'IDBI2300PE', 60, 50, 'sell'),\
('30 July 2022', 'REL2300PE', 250, 45, 'sell'),\
('31 July 2022', 'IDBI2300PE', 200, 25, 'sell')]
df = spark.createDataFrame(data, ["Date", "symbol", "qty", "price", "type"])
Calculate consumed qty:
# Calculate cumulative sum for sells and buys for the current and all prev rows
cum_sum_wind = Window.partitionBy('symbol').orderBy('Date').rangeBetween(Window.unboundedPreceding, 0)
df = df.withColumn('cum_sum', sum(when(col('type') == 'sell', -1 * col('qty'))\
.otherwise(col('qty'))).over(cum_sum_wind))
# Calculate cumulative sum for sells only for the current and all following rows
sell_cum_sum_wind = Window.partitionBy('symbol').orderBy('Date').rangeBetween(0, Window.unboundedFollowing)
df = df.withColumn('sell_cum_sum', sum(when(col('type') == 'sell', col('qty'))\
.otherwise(0)).over(sell_cum_sum_wind))
# Calculate the actual consumed qty
df = df.withColumn('cons_qty', when(col('type') == 'sell', col('qty'))\
.when(col('sell_cum_sum') > col('cum_sum'), col('qty'))\
# If negative then nothing is consumed from this row
.when((col('qty') - (col('cum_sum') - col('sell_cum_sum'))) < 0, 0)\
.otherwise(col('qty') - (col('cum_sum') - col('sell_cum_sum'))))
df.show()
+------------+----------+---+-----+----+-------+------------+--------+
| Date| symbol|qty|price|type|cum_sum|sell_cum_sum|cons_qty|
+------------+----------+---+-----+----+-------+------------+--------+
|05 July 2022|IDBI2300PE|500| 45| buy| 500| 460| 460|
|07 July 2022|IDBI2300PE|200| 50|sell| 300| 460| 200|
|15 July 2022|IDBI2300PE| 20| 50| buy| 320| 260| 0|
|30 July 2022|IDBI2300PE| 60| 50|sell| 260| 260| 60|
|31 July 2022|IDBI2300PE|200| 25|sell| 60| 200| 200|
|07 July 2022| REL2300PE|200| 50| buy| 200| 450| 200|
|15 July 2022| REL2300PE|100| 50| buy| 300| 450| 100|
|16 July 2022| REL2300PE|200| 35| buy| 500| 450| 150|
|20 July 2022| REL2300PE|200| 45|sell| 300| 450| 200|
|30 July 2022| REL2300PE|250| 45|sell| 50| 250| 250|
+------------+----------+---+-----+----+-------+------------+--------+
Calculate global profit/loss per symbol:
# Groupby symbol and calculate the profit/loss
result = df.groupby('symbol')\
.agg(
sum(when(col('type') == 'buy', -1 * col('price') * col('cons_qty'))\
.otherwise(col('price') * col('cons_qty'))\
).alias("profit_loss"))
result.show()
+----------+-----------+
| symbol|profit_loss|
+----------+-----------+
|IDBI2300PE| -2700|
| REL2300PE| 0|
+----------+-----------+
答案2
得分: 0
使用withColumn
添加一个名为total_price
的新列,根据类型为'buy'时乘以-1,类型为'sell'时乘以1进行额外计算。
df = df.withColumn('total_price', when(df.type == 'buy', (df.qty * df.price_per_qty * (-1))).otherwise(df.qty * df.price_per_qty))
df = df.groupBy(col('symbol')).sum(col('total_price').alias('profit_loss'))
请尽量理解我的概念。可能会有代码语法错误,因为我现在没有笔记本电脑。
谢谢。
英文:
Add a new column total_price using withColumn
using additional calculation as multiply by -1 to total_price if type is 'buy' and multiply by 1 if type is 'sell'
df = df.withColumn('total_price', when(df.type == 'buy', (df.qty * df.price_per_qty * (-1))).otherwise(df.qty*df.price_per_qty)
df = df.groupBy(col('symbol')).sum(col('total_price').alias('profit_loss'))
Please try to understand my concept here. There may be code syntax error as I don't have laptop now.
Thanks
答案3
得分: 0
如果您想使用Hive来解决这个问题,可以使用以下查询来获得所需的结果:
select date, symbol, total_profit from (
select date,
symbol,
sum(qty * (CASE
WHEN type='buy' THEN price_per_qty
ELSE (price_per_qty*(-1))
END
)
) over (partition by symbol order by date rows between unbounded preceding and current row) as total_profit,
row_number() over( partition by symbol order by date desc ) as rn
from stock1
) as running_sum where rn=1;
英文:
If you want to use Hive to solve this problem you can use below query to get the desired result:
select date
,symbol
,sum(qty * (CASE
WHEN type='buy' THEN price_per_qty
ELSE (price_per_qty*(-1))
END
)
) over (partition by symbol order by date rows between unbounded preceding and current row) as total_profit
,row_number() over( partition by symbol order by date desc ) as rn
from stock1) as running_sum where rn=1;
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论