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.