Как в Pandas реализовать подсчет методом FIFO?
Есть датафрейм, в котором отображаются покупки и продажи. Как при продаже части позиции, получить разницу между продажей и покупкой по методу FIFO?
date quantity price cum_qty
0 2020-03-13 1 16.53 1
1 2020-03-18 1 10.21 2
2 2020-03-26 1 17.61 3
3 2020-05-01 1 14.70 4
4 2020-05-22 1 14.51 5
5 2020-05-27 1 18.15 6
6 2020-06-08 -2 26.00 4
7 2020-06-19 -4 17.94 0
Т.е. когда в строке 6 произошла продажа 2 элементов, то в строках 0 и 1, например, в колонке "Profit" станет: 26 - 16.53 = 9.47 и 26 - 10.21 = 15.79 и т.д.
Дополнение: После продажи в строке 7, получается следующее: мы находим не проданные позиции, а это получается строки с 2-5, и "price" из строки 7 вычитаем "price" из найденной строки:
- 2: 17.94 - 17.61 = 0.33
- 3: 17.94 - 14.70 = 3.24
- ...
- 5: 17.94 - 18.15 = -0.21
date quantity price cum_qty profit
0 2020-03-13 1 16.53 1 9.47
1 2020-03-18 1 10.21 2 15.79
2 2020-03-26 1 17.61 3 0.33
3 2020-05-01 1 14.70 4 3.24
4 2020-05-22 1 14.51 5 3.43
5 2020-05-27 1 18.15 6 -0.21
6 2020-06-08 -2 26.00 4 0.00
7 2020-06-19 -4 17.94 0 0.00
Никак не найду с помощью каких функций и методов могу искать строки вверх от имеющейся и менять в них значений по условию. Либо сопоставлять текущую строку с другими по условию. Буду рад предложенным вариантам.
Дополнение 2: Хотелось бы заменить вот этот цикл или оптимизировать его методами pandas.
df['profit'] = Decimal(0)
df['qnt_available'] = df['quantity']
# ------------------
df_sales = df[df['quantity'] < 0] # получаем датафрейм с продажами
for i, row_sell in df_sales.iterrows(): # перебираем все продажи
qnt_sell = abs(row_sell.quantity) # количество проданных позиций
price_sell = row_sell.price # цена продажи
while qnt_sell != 0: # Цикл до тех пор пока не будут обработаны все продажи из текущей строки
# формируем новый датафрейм, где остались покупки
df_buy = df[df['qnt_available'] > 0]
for j, row_buy in df_buy.iterrows(): # цикл по покупкам
qnt_buy = row_buy.qnt_available # количество оставшихся покупок
price_buy = row_buy.price # цена покупки
if qnt_buy == qnt_sell: # если количество покупок равно количеству продаж, то обнуляем значения
new_value = 0
qnt_sell = 0
profit = (price_sell - price_buy) * qnt_buy # сумма дохода
elif qnt_buy > qnt_sell: # если сумма покупок больше
new_value = qnt_buy - qnt_sell
qnt_sell = 0
profit = (price_sell - price_buy) * qnt_sell
elif qnt_buy < qnt_sell: # если сумма покупки меньше
new_value = 0
qnt_sell -= qnt_buy
profit = (price_sell - price_buy) * qnt_buy
# Записываем новые данные
df.at[j, 'qnt_available'] = new_value
df.at[j, 'profit'] += profit
Начальный датафрейм:
date quantity price profit qnt_available
0 2020-03-13 1 16.53 0 1
1 2020-03-18 1 10.21 0 1
2 2020-03-26 1 17.61 0 1
3 2020-05-01 1 14.70 0 1
4 2020-05-22 1 14.51 0 1
5 2020-05-27 1 18.15 0 1
6 2020-06-08 -2 26.00 0 -2
7 2020-06-19 -4 17.94 0 -4
После обработки первой продажи:
date quantity price profit qnt_available
0 2020-03-13 1 16.53 9.47 0
1 2020-03-18 1 10.21 15.79 0
2 2020-03-26 1 17.61 0 1
3 2020-05-01 1 14.70 0 1
4 2020-05-22 1 14.51 0 1
5 2020-05-27 1 18.15 0 1
6 2020-06-08 -2 26.00 0 -2
7 2020-06-19 -4 17.94 0 -4
После второй продажи:
date quantity price profit qnt_available
0 2020-03-13 1 16.53 9.47 0
1 2020-03-18 1 10.21 15.79 0
2 2020-03-26 1 17.61 0.33 0
3 2020-05-01 1 14.70 3.24 0
4 2020-05-22 1 14.51 3.43 0
5 2020-05-27 1 18.15 -0.21 0
6 2020-06-08 -2 26.00 0 -2
7 2020-06-19 -4 17.94 0 -4