Closed scoriiu closed 4 years ago
This definitely needs to be changed and your solution is probably good. So that we can reach a more stable API as fast as possible, let's look ahead to some of the fields on the execution events being returned from the exchanges we intend to integrate with, and CCXT.
I'll do some research myself but also feel free to comment.
Excepting the quotation part I think the API of the position is pretty good already. I would also add ROI and margin related information.
Bitmex position update:
{
"table": "position",
"action": "partial",
"keys": [
"account",
"symbol"
],
"types": {
"account": "long",
"symbol": "symbol",
"currency": "symbol",
"underlying": "symbol",
"quoteCurrency": "symbol",
"commission": "float",
"initMarginReq": "float",
"maintMarginReq": "float",
"riskLimit": "long",
"leverage": "float",
"crossMargin": "boolean",
"deleveragePercentile": "float",
"rebalancedPnl": "long",
"prevRealisedPnl": "long",
"prevUnrealisedPnl": "long",
"prevClosePrice": "float",
"openingTimestamp": "timestamp",
"openingQty": "long",
"openingCost": "long",
"openingComm": "long",
"openOrderBuyQty": "long",
"openOrderBuyCost": "long",
"openOrderBuyPremium": "long",
"openOrderSellQty": "long",
"openOrderSellCost": "long",
"openOrderSellPremium": "long",
"execBuyQty": "long",
"execBuyCost": "long",
"execSellQty": "long",
"execSellCost": "long",
"execQty": "long",
"execCost": "long",
"execComm": "long",
"currentTimestamp": "timestamp",
"currentQty": "long",
"currentCost": "long",
"currentComm": "long",
"realisedCost": "long",
"unrealisedCost": "long",
"grossOpenCost": "long",
"grossOpenPremium": "long",
"grossExecCost": "long",
"isOpen": "boolean",
"markPrice": "float",
"markValue": "long",
"riskValue": "long",
"homeNotional": "float",
"foreignNotional": "float",
"posState": "symbol",
"posCost": "long",
"posCost2": "long",
"posCross": "long",
"posInit": "long",
"posComm": "long",
"posLoss": "long",
"posMargin": "long",
"posMaint": "long",
"posAllowance": "long",
"taxableMargin": "long",
"initMargin": "long",
"maintMargin": "long",
"sessionMargin": "long",
"targetExcessMargin": "long",
"varMargin": "long",
"realisedGrossPnl": "long",
"realisedTax": "long",
"realisedPnl": "long",
"unrealisedGrossPnl": "long",
"longBankrupt": "long",
"shortBankrupt": "long",
"taxBase": "long",
"indicativeTaxRate": "float",
"indicativeTax": "long",
"unrealisedTax": "long",
"unrealisedPnl": "long",
"unrealisedPnlPcnt": "float",
"unrealisedRoePcnt": "float",
"simpleQty": "float",
"simpleCost": "float",
"simpleValue": "float",
"simplePnl": "float",
"simplePnlPcnt": "float",
"avgCostPrice": "float",
"avgEntryPrice": "float",
"breakEvenPrice": "float",
"marginCallPrice": "float",
"liquidationPrice": "float",
"bankruptPrice": "float",
"timestamp": "timestamp",
"lastPrice": "float",
"lastValue": "long"
},
"foreignKeys": {
"symbol": "instrument"
},
"attributes": {
"account": "sorted",
"symbol": "grouped",
"underlying": "grouped"
},
"filter": {
"account": 1513111
},
"data": [
]
}
Ok, so I know where this has come from now. Its taken from the Currency
field of the ExecutionReport
FIX4.4 message, which is the currency used for the prices. https://www.onixs.biz/fix-dictionary/4.4/tagNum_15.html
I think this link explains the transaction vs settlement currency well. https://ibkr.info/node/295
So actually the use of the quote_currency
is correct right now, as in the examples using USD/JPY the USD is the transaction currency, the position is settled in JPY which is the currency used to determine PNL (then converted into the account base currency). This is obviously FX centric though. There is already a SecurityType
enum available, which may be good to add to the OrderFill
events if it could help us sort out the logic...
I agree though we can be clearer about this and even provide all the fields so there is no confusion. For instance I see BitMEX returns both the currency
and settlCurrency
. For XBt\USD
the settlement currency is on the other side (XBt), I think you were alluding to this before?
How do you propose we should change things?
It seems BitMEX provides some PNL info calculated from their side.
Just brainstorming, we could always push this upstream away from the Position
object and place it into the OrderFilled
event instead. The Position
just captures the data. ExecutionEngine
could handle this sort of calculation for instance. Then the Position
object doesn't need knowledge about the specifics of the account currency - which simplifies things...
Interesting. I am more used with seeing the position in the base transaction currency by default but I think it would be better to provide all the fields for more clarity, like you propose.
The same goes with submitting an order, where currently the quantity is expressed in quote currency. All the APIs I worked so far requires for the quantity to be expressed in base currency. Some APIs allow to provide the quantity in quote currency, but this is optional.
As for tracking the PNLs I'm not sure what is best. My preference is also to drop the PNLs upstream and calculate them ourselves. This would mean that we have to track all the fills in the position in order to be able to calculate the realized PNL. So basically being able to construct the position based on the upstream trades.
I think it would be useful if not too complicated to switch the quantity to base currency when submitting orders. This would mean that also the Position.quantity and PNLs has to be in line with this change and be expressed in the based currency by default.
Did you have the time to trade live on a test account inverse and direct swaps on a testnet account to get the feeling of how the positions are represented? If you will, we can setup a video call and show you a couple of these examples.
I think that would be useful to have a live discussion and demo. Information transfer will be faster 👍🏻
I have had a chance to play around with a testnet, it seems they're using a really simple netting type OMS. BitMEX have huge capacity hurdles so I suppose it's as simple as possible.
The quantity is for the left hand side of any currency pair or number of contracts, that's definitely the intention for quantity
in the orders. I think we need to rewrite how PNL is being calculated basically, but I agree it doesn't need to happen upstream, I think the Position
objects will be able to continue to calculate the PNLs - it just needs to be perfectly correct.
I rewrote the ExecutionEngine
. The client position ids have been removed in favour of just a PositionId
. So there's no longer a need to specify a PositionId
when submitting an order. If one does however then the system will need to have a record of that position id already, because its clearly meant to be an order to modify an existing position. This keeps things consistent with any position ids the exchange may or may not be generating.
If a broker/exchange is using hedging type order management then their own position id will be captured and indexed for that order.
If not and the order management is netting (BitMEX), then the system will automatically generate and track a position id for new positions.
The ExecutionEngine
is much simpler and more robust now - it still needs to be refactored some more. Theres scope for more order validation logic too.
We still need to decide if a position is allowed to be 'flipped' with a single order. My preference is the trader should be required to close out the original position first before going in the order direction with an additional order... It avoids the need for complex logic splitting the original OrderFill
event into two new events (one to close the original position and one to create the new position).
Position
flipping logic has been implemented inside ExecutionEngine
. Lets open a new issue for further enhancements/changes to Position
.
Currently the position is represented in quote currency. I find this confusing, hence I propose to change it to base currency.