Average Entry Price Accounting for Inverse Perpetual Contracts
Summary
The document examines how to calculate average entry price while backtesting positions in perpetual swap contracts, focusing on inverse contracts whose value is denominated in cryptocurrency. A simple harmonic-style average of execution prices can fail when trades reduce, close, or reverse a position. The questioner also reports an exchange-specific procedure that tracks execution cost in satoshis and resets entry totals when a position crosses through zero, but finds their implementation gives an implausible result.
The answer says average entry price must be derived from both position cost and realized gross profit or loss. It gives a contract-value relationship using execution cost, realized P&L, and contract count, then distinguishes converting that value to price for quanto contracts versus inverse contracts. The response suggests integer division may account for rounding differences on the exchange. These formulas are contract-specific; the document does not fully specify fee treatment, rounding rules, or a complete position-update algorithm for all trade sequences.
Key ideas
- Average entry price for inverse contracts cannot always be computed by averaging execution prices alone.
- Position reductions, closures, and reversals affect how entry cost should be tracked.
- The answer derives held contract value from execution cost and realized gross P&L per contract.
- Quanto and inverse contracts use different conversions from held contract value to price.
- Exchange integer arithmetic may create rounding differences, and contract specifications matter.
Tags
Full text
# How to calculate average entry price for perpetual swap contracts?
# How to calculate average entry price for perpetual swap contracts?
I'm trying to calculate the average entry price for perpetual swap contracts for use in back-testing a trading strategy, as per Bitmex's documentation:
> A Perpetual Contract is a derivative product that is similar to a traditional Futures Contract, but has a few differing specifications: There is no expiry or settlement. Perpetual Contracts mimic a margin-based spot market and hence trade close to the underlying reference Index Price. This is in contrast to a Futures Contract which may trade at significantly different prices due to basis. The primary mechanism to tether to spot price is Funding
This contract specification is found on numerous exchanges, Bitmex, Okex, Binance etc. The average entry price is used in calculating the realized PNL, the following describes the context in which it is used.
> John is long 1,000 XBTUSD contracts with an average entry price of $1,000. The mark price of XBTUSD is currently $1,250. John’s unrealised PNL is based on the difference between his average entry price and the mark price. Unrealised Profit = ($1/$1,000 - $1/$1,250) * 1,000 = 0.20 XBT The last price of XBTUSD is $1,500. However for the calculation of unrealised PNL, the mark price is used not the last price. To understand why, please read Fair Price Marking. John decides to sell 500 XBTUSD contracts at $1,500 and realise some profit. John’s realised PNL is based on the difference between his average entry price and the price at which he sells XBTUSD. Realised Profit = ($1/1,000 - $1/$1,500) * 500 = 0.17 XBT Realised PNL is based on where you can actually buy or sell your position, which in most cases is not the mark price. If John had sold his 500 contracts at the mark price of $1,250, he would have a realised profit of 0.10 XBT.
I have taken note of the answers located here.
> total_quantity / ((quantity_1 / price_1) + (quantity_2 / price_2)) = entry_price
and have appended each execution to an array then run a summation before dividing the current position by the summed executions i.e.
```
entries = []
position = 0
orders = [
{"size":-50, "price":10000, "side":"sell"},
{"size":5, "price":10000, "side":"sell"},
]
for order in orders:
entries.append(abs(order["size"])/order["price"])
position += order["size"]
average_entry_price = abs(position)/sum(entries)
print(average_entry_price) # 8181.818181818182
```
However in this instance it returns a presumably incorrect average entry price of 8181.818181818182.This happens in numerous cases.
I then after contacting support was given the following protocol for deriving the average entry price:
> The execCost of each entry order is added together. For our contracts, execCost is calculated as round(1e8/price) * number of contracts. The total execCost is divided by the total number of entry contracts. This is the 'average satoshi price' of the position. For long positions, floor() the average satoshi price. For short position, round() the average satoshi price. Divide 1e8 by the average satoshi price to get the average USD price. This is then rounded to 4 decimal places for the API, and rounded to the nearest tick for the front-end.
And thereafter implemented a simple "proof of concept" implementation:
```
orders = [
{"price":10000, "size": -100},
{"price":10000, "size": -100},
{"price":10000, "size": 100},
{"price":10000, "size": 100},
{"price":9000, "size": 100},
]
execCosts = 0
totalEntry = 0
position = 0
for order in orders:
next_position = position+order["size"]
if position * next_position <0:
#Current position is closed and opposite position is opened
execCosts = 0
totalEntry = 0
execCosts += (round(1e8/order["price"]) * order["size"])
totalEntry += abs(order["size"])
elif abs(position) > abs(next_position):
# The current position is reduced
pass
else:
#The current position is increased
execCosts += (round(1e8/order["price"]) * order["size"])
totalEntry += abs(order["size"])
position = next_position
averageSatoshiPrice = abs(execCosts/totalEntry)
print(averageSatoshiPrice) # 2963.0 ???
```
However this again seemed to be an insufficient implementation. Could someone please advise me on how to calculate the Average Entry Price. Your guidance is truly appreciated.
## Answer by Will (score 0)
https://quant.stackexchange.com/a/70149
To calculate the average entry price you need to know the position cost AND the current profit loss. Pseudocode to get the contract value in satoshi:
```
heldContractValue = (executionCost + realisedGrossPNL) / nContracts
```
You can then calculate the price from the contract value. This differs between contracts, but for a quanto contract the formula where satoshiPerDollar is defined in the contract specification:
```
price = heldContractValue / satoshiPerDollar
```
Whereas for the inverse contracts it's
```
price = 100_000_000 / heldContractValue
```
I think BitMEX just using integer division under the hood, causing rounding errors.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.