Tracking Realized and Unrealized P&L When a Position Reverses
Summary
The document presents a question about how realized profit and loss, average opening price, and unrealized profit and loss behave when a trade crosses through zero and reverses direction. Its included answer proposes a fill-by-fill accounting procedure that tracks signed position, average entry price, realized P&L, and unrealized P&L. Adding to a position updates the weighted average entry; reducing it realizes gains or losses against that basis; closing it resets the open-position basis; and a trade that exceeds the current position establishes a position in the opposite direction.
The evidence is a code example rather than a worked numerical case or formal derivation. The snippet illustrates long and short branches, but it contains potential implementation issues, including inconsistent accumulation of realized P&L and a questionable unrealized-P&L update after crossing from short to long. It should be treated as an informal example and checked against a clearly specified accounting convention, instrument multiplier, fees, and fill data before practical use.
Key ideas
- Position and opening price can be tracked from signed trade quantities and fill prices.
- Adding to an open position changes its weighted average entry price.
- A fill that closes or reverses a position realizes P&L on the quantity that offsets the old position.
- The included code has apparent inconsistencies and should be validated before being used for accounting.
Tags
Full text
# Realized and Unrealized Profit and Loss
# Realized and Unrealized Profit and Loss
https://www.tradingtechnologies.com/xtrader-help/fix-adapter-reference/pl-calculation-algorithm/understanding-pl-calculations/
What I want to know is, after Scenario 4 in this article how does the Realized PnL, Opening Price, Unrealized PnL behave now that direction has changed!
Edit: I have figured out how to deal with the scenario when the opening position is >=0, however, I wish to know what happens after Scenario 4 of this article where-in during the next fill the opening position will <0, how does the different factors behave then.
## Answer by Anirban Saha (score 1, accepted)
https://quant.stackexchange.com/a/50981
I think I finally got the required answer to my problem. I'm sharing the code in Python 3 in case it helps anyone struggling with a similar problem.
Running Profit and Loss in Python 3
```
import pandas as pd
import numpy as np
dataset=pd.read_csv('trades.txt',delimiter= ' ');
#Initialising Variables to be used throughout the program
position=0;
averageopenprice=0;
pnlunrealized=0;
pnlrealized=0;
pnltotal=0;
#Inserting Columns into the dataset to visualise the working in a better way
dataset.insert(2,'Position',np.zeros(len(dataset.index)));
dataset.insert(3,'Average Open Price',np.zeros(len(dataset.index)));
dataset.insert(4,'PnL Unrealized',np.zeros(len(dataset.index)));
dataset.insert(5,'PnL Realized',np.zeros(len(dataset.index)));
dataset.insert(6,'PnL Total',np.zeros(len(dataset.index)));
#Algorithm Begins
for i in range(0,len(dataset.index)):
qty=dataset.iat[i,0]; #This will help in writing more legible codes
price=dataset.iat[i,1];
if position==0:
position=qty;
averageopenprice=price;
pnlrealized=pnlrealized;
pnlunrealized=0;
pnltotal=pnlrealized+pnlunrealized;
elif position>0:
if qty>0:
averageopenprice=(position*averageopenprice+qty*price)/(position+qty);
position+=qty;
pnlrealized=pnlrealized;
pnlunrealized=(price-averageopenprice)*position;
pnltotal=pnlrealized+pnlunrealized;
elif qty<0:
if abs(qty)<position:
averageopenprice=averageopenprice;
pnlrealized+=(price-averageopenprice)*abs(qty);
position+=qty;
pnlunrealized=(price-averageopenprice)*position;
pnltotal=pnlrealized+pnlunrealized;
elif abs(qty)==position:
pnlrealized+=(price-averageopenprice)*abs(qty);
position=0;
averageopenprice=0;
pnlunrealized=0;
pnltotal=pnlrealized+pnlunrealized;
elif abs(qty)>position:
pnlrealized+=(price-averageopenprice)*position;
position+=qty;
averageopenprice=price;
pnlunrealized=(price-averageopenprice)*position;
pnltotal=pnlrealized+pnlunrealized;
elif position<0:
if qty<0:
averageopenprice=(abs(position)*averageopenprice+abs(qty)*price)/(abs(position+qty));
position+=qty;
pnlrealized=pnlrealized;
pnlunrealized=(averageopenprice-price)*abs(position);
pnltotal=pnlrealized+pnlunrealized;
elif qty>0:
if qty<abs(position):
averageopenprice=averageopenprice;
pnlrealized=(averageopenprice-price)*qty;
position+=qty;
pnlunrealized=(averageopenprice-price)*abs(position);
pnltotal=pnlrealized+pnlunrealized;
elif qty==abs(position):
pnlrealized+=(averageopenprice-price)*qty;
position=0;
averageopenprice=0;
pnlunrealized=0;
pnltotal=pnlrealized+pnlunrealized;
elif qty>abs(position):
pnlrealized+=(averageopenprice-price)*abs(position);
position+=qty;
averageopenprice=price;
pnlunrealized=0;
pnltotal=pnlrealized+pnlunrealized;
dataset.iat[i,2]=position;
dataset.iat[i,3]=averageopenprice;
dataset.iat[i,4]=pnlunrealized;
dataset.iat[i,5]=pnlrealized;
dataset.iat[i,6]=pnltotal;
output=dataset[['qty','price','PnL Realized','PnL Unrealized','PnL Total']];
```Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.