Skip to content
All library documents

Tracking Realized and Unrealized P&L When a Position Reverses

Article Quant Q&A · Author: Anirban Saha

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.