Рассчитайте прибыль и убыток (PnL) за сделку, используя Pandas Dataframe - PullRequest
0 голосов
/ 19 мая 2019

У меня есть этот кадр данных панд, где 1 в long_entry или short_entry представляет вход в сделку в то время с длинной / короткой позицией, соответствующей. В то время как 1 в long_exit или short_exit означает выход из сделки. Могу ли я узнать, как я могу рассчитать PnL каждой отдельной сделки, которая будет отображаться в новом столбце df ['pnl_per_trade']?

Только одно сделка / позиция в любой момент времени для этого тестирования.

Ниже мой фрейм данных. Как мы видим, длинная сделка открыта 26.02.2017 и закрыта в 1/3/2019, а Pnl будет составлять $ 64,45, в то время как короткая сделка будет открыта 3/3/2019 и закрыта 03.05.2009 с pnl - $ 119,11 (убыток).

        date    price       long_entry  long_exit   short_entry short_exit
0   24/2/2019   4124.25           0          0           0              0
1   25/2/2019   4130.67           0          0           0              0
2   26/2/2019   4145.67           1          0           0              0
3   27/2/2019   4180.10           0          0           0              0
4   28/2/2019   4200.05           0          0           0              0
5   1/3/2019    4210.12           0          1           0              0
6   2/3/2019    4198.10           0          0           0              0
7   3/3/2019    4210.34           0          0           0              0
8   4/3/2019    4100.12           0          0           1              0
9   5/3/2019    4219.23           0          0           0              1

Я надеюсь получить такой вывод:

        date    price       long_entry  long_exit   short_entry short_exit  pnl
0   24/2/2019   4124.25           0          0           0             0    NaN
1   25/2/2019   4130.67           0          0           0             0    NaN
2   26/2/2019   4145.67           1          0           0             0  64.45
3   27/2/2019   4180.10           0          0           0             0    NaN
4   28/2/2019   4200.05           0          0           0             0    NaN
5   1/3/2019    4210.12           0          1           0             0    NaN
6   2/3/2019    4198.10           0          0           0             0    NaN
7   3/3/2019    4210.34           0          0           0             0    NaN
8   4/3/2019    4100.12           0          0           1             0 -119.11
9   5/3/2019    4219.23           0          0           0             1    NaN

Поскольку у меня много данных, я бы предпочел, чтобы в коде не было циклов, если это возможно. Спасибо!

Ответы [ 2 ]

0 голосов
/ 19 мая 2019

Я не уверен, поможет ли это: но я думаю, что ваше представление о PnL может быть неверным. Ниже показано, как получить дневное значение pnl вместо pnl позиции.

def get_position(long_entry,long_exit, short_entry,short_exit):
    if long_entry == 1 or short_exit == 1:
        position = 1
    elif long_exit == 1 or short_entry == 1:
        position = -1
    else:
        position = 0

    return position

df['position'] = list(map(get_position, df.long_entry.values, df.long_exit.values, df.short_entry.values, df.short_exit.values))

df = df[['date', 'price','position']]

df['amount'] = -df['price']*df['position']
df['pnl'] = df['amount'].cumsum()

Это результат:

        date    price  position   amount      pnl
0  24/2/2019  4124.25         0    -0.00    -0.00
1  25/2/2019  4130.67         0    -0.00    -0.00
2  26/2/2019  4145.67         1 -4145.67 -4145.67
3  27/2/2019  4180.10         0    -0.00 -4145.67
4  28/2/2019  4200.05         0    -0.00 -4145.67
5   1/3/2019  4210.12        -1  4210.12    64.45
6   2/3/2019  4198.10         0    -0.00    64.45
7   3/3/2019  4210.34         0    -0.00    64.45
8   4/3/2019  4100.12        -1  4100.12  4164.57
9   5/3/2019  4219.23         1 -4219.23   -54.66

, который является кумулятивным pnl независимо от длинной или короткой позиции. Надеюсь, это поможет.

0 голосов
/ 19 мая 2019

Я расширил ваши образцы данных, чтобы иметь 2 длинных значения PnL, и изменил столбец date на DateTime :

df = pd.DataFrame(data=[
    [ '24/2/2019', 4124.25, 0, 0, 0, 0 ],
    [ '25/2/2019', 4130.67, 0, 0, 0, 0 ],
    [ '26/2/2019', 4145.67, 1, 0, 0, 0 ],
    [ '27/2/2019', 4180.10, 0, 0, 0, 0 ],
    [ '28/2/2019', 4200.05, 0, 0, 0, 0 ],
    [ '1/3/2019',  4210.12, 0, 1, 0, 0 ],
    [ '2/3/2019',  4198.10, 0, 0, 0, 0 ],
    [ '3/3/2019',  4210.34, 0, 0, 0, 0 ],
    [ '4/3/2019',  4100.12, 0, 0, 1, 0 ],
    [ '5/3/2019',  4219.23, 0, 0, 0, 1 ],
    [ '6/3/2019',  4210.00, 1, 0, 0, 0 ],
    [ '7/3/2019',  4212.00, 0, 0, 0, 0 ],
    [ '8/3/2019',  4214.00, 0, 1, 0, 0 ]],
    columns=['date','price', 'long_entry', 'long_exit',
        'short_entry', 'short_exit'])
df.date = pd.to_datetime(df.date)

Следующим шагом является генерация df2 содержащие только строки для начала и конца длинных записей (на самом деле понадобятся только столбцы дата и цена , но для иллюстрации я включил также long_entry и long_exit :

df2 = df.query('long_entry > 0 or long_exit > 0').iloc[:,0:4]; df2

Результат (для моих данных):

         date    price  long_entry  long_exit
2  2019-02-26  4145.67           1          0
5  2019-01-03  4210.12           0          1
10 2019-06-03  4210.00           1          0
12 2019-08-03  4214.00           0          1

Затем мы должны определить функцию, которая будет применена в ближайшее время:

def fn(src):
    return pd.Series([src.iloc[0, 0], src.iloc[1, 1] - src.iloc[0, 1]])

Следующий шаг - применить вышеуказанную функцию к последовательным парам строк (вход и выход), задать имена столбцов и изменить столбец date на индекс:

lProf = df2.groupby(np.arange( len(df2.index)) // 2).apply(fn)
lProf.columns = ['date', 'pnl']
lProf.set_index('date', inplace=True)

Результат:

             pnl
date             
2019-02-26  64.45
2019-06-03   4.00

Пока у нас есть данные для вставки из длинных записей. Теперь пришло время создать похожий DataFrame для коротких записей,применяя ту же функцию, что и раньше:

df2 = df.query('short_entry > 0 or short_exit > 0').iloc[:,[0, 1, 4, 5]]
sProf = df2.groupby(np.arange( len(df2.index)) // 2).apply(fn)
sProf.columns = ['date', 'pnl']
sProf.set_index('date', inplace=True)

Но на этот раз мы должны изменить знакисходные значения:

sProf = -sProf

Результат:

               pnl
date              
2019-04-03 -119.11

Прежде чем мы добавим результаты в основной DataFrame, мы должны установить столбец date в качестве индекса:

df.set_index('date', inplace=True)

И теперь мы добавляем результаты long записей:

df['pnl'] = lProf

Это создало новый столбец, так что теперь, чтобы добавить результаты коротких записей, мы должны сделать update :

df.update(sProf)

Если вы хотите, чтобы date вернулось как обычностолбец, запустить:

df.reset_index(inplace=True)
...