You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ProtoOAPositionUnrealizedPnL.netUnrealizedPnL is compiled as int32 (the proto says int64), so values get truncated #50
The compiled ctrader_open_api/messages/OpenApiModelMessages_pb2.py shipped in ctrader-open-api==0.9.2 (and in the current main) declares netUnrealizedPnL as int32, while grossUnrealizedPnL next to it is int64. Protobuf truncates a 64-bit varint that is read into an int32 field. So every value outside ±2^31 wraps modulo 2^32.
For accounts with moneyDigits = 8 (for example, USDT deposit assets) that limit is only ±21.47 in the deposit currency, so the field is wrong for most real positions. We see this in production. On the wire, the server sends the correct int64 value, which equals gross + commission + swap. Only the SDK's decoding corrupts it.
Reproduction (offline, no credentials needed):
# pip install ctrader-open-api==0.9.2fromctrader_open_api.messages.OpenApiModelMessages_pb2importProtoOAPositionUnrealizedPnLdefvarint(n: int) ->bytes:
n&= (1<<64) -1# int64 two's complement, as protobuf encodes it on the wireout=bytearray()
whileTrue:
b=n&0x7Fn>>=7out.append(b| (0x80ifnelse0))
ifnotn:
returnbytes(out)
# What the server sends: position 1, gross 58.3651, net 61.0371 (moneyDigits 8)wire=b"\x08"+varint(1) +b"\x10"+varint(5_836_510_000) +b"\x18"+varint(6_103_710_000)
msg=ProtoOAPositionUnrealizedPnL()
msg.ParseFromString(wire)
print("gross", msg.grossUnrealizedPnL) # 5836510000 -> correctprint("net ", msg.netUnrealizedPnL) # 1808742704 -> truncated to 32 bits; expected 6103710000
We confirmed this against live ProtoOAGetPositionUnrealizedPnLRes messages. We decoded the raw payload by hand and with the SDK: the raw int64 value is always correct, and the SDK value is always the raw value truncated to 32 bits.
Other differences between the compiled messages and the proto (harmless as far as we can tell):
ProtoOADealListByPositionIdRes.hasMore: proto bool, SDK int64
ProtoOADynamicLeverageTier.leverage: proto int32, SDK int64
Suggested fix: regenerate the _pb2 modules from the current proto files and release a new version.
Workaround for users: do not read netUnrealizedPnL through the SDK. Either decode the payload bytes yourself, or compute the net from grossUnrealizedPnL + the position's commission + swap (all int64).
Activity