Skip to content

Calculate profit and loss

Calculating P&L is an essential use case that needs to be implemented by nearly every cTrader Open API application and service.

Manually calculating P&L is a difficult process as the calculation needs to account for the position size and the possible conversion between the quote asset of the bought or sold symbol and the account deposit currency.

Fortunately, you can simply ask the cTrader backend to calculate P&L for you by sending the ProtoOAGetPositionUnrealizedPnLReq message. You should receive the ProtoOAGetPositionUnrealizedPnLRes message containing the ProtoOAPositionUnrealizedPnL model message in one of its fields.

Note

ProtoOAGetPositionUnrealizedPnLRes contains information about the P&Ls of all currently open positions of the account with the specified ctidTraderAccountId in its repeated positionUnrealizedPnL field.

Rate limits

Requesting P&L is a non-historical request, meaning that you can only perform a maximum of 50 such requests per second. We recommend refreshing P&L once every two to three seconds so that there is no risk of exceeding rate limits.

Learn more about sending and receiving messages in tutorials on Protobuf and JSON.

When operating with Protobuf, you could use the classes generated by the compiler for serialising and deserialising the message for attaining P&L rates. When operating with JSON, you would need to create such classes yourself. Here is an example of how they could look.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public abstract class OpenAPIModelMessageBase { }

public class PositionUnrealizedPnL : OpenAPIModelMessageBase
{
    public PositionUnrealizedPnL() { }
    public PositionUnrealizedPnL(int positionId, int grossUnrealizedPnL, int netUnrealizedPnL) 
    {
        this.PositionId = positionId;
        this.GrossUnrealizedPnL = grossUnrealizedPnL;
        this.NetUnrealizedPnL = netUnrealizedPnL;
    }

    public int PositionId { get; set; }
    public int GrossUnrealizedPnL { get; set; }
    public int NetUnrealizedPnL { get; set; }
}

public class GetPositionUnrealizedPnLReq : OpenAPIMessageBase
{
    public GetPositionUnrealizedPnLReq() { }
    public GetPositionUnrealizedPnLReq(int ctidTraderAccountId)
    {
        this.Payload = new GetPositionUnrealizedPnLReqPayload(ctidTraderAccountId);
        this.ClientMsgId = Guid.NewGuid().ToString();
    }

    public override int PayloadType => 2187;
    public GetPositionUnrealizedPnLReqPayload? Payload { get; set; }
}

public class GetPositionUnrealizedPnLReqPayload : OpenAPIMessagePayloadBase
{
    public GetPositionUnrealizedPnLReqPayload() { }
    public GetPositionUnrealizedPnLReqPayload(int ctidTraderAccountId)
    {
        this.CtidTraderAccountId = ctidTraderAccountId;
    }

    public int CtidTraderAccountId { get; set; } = 0;

}

public class GetPositionUnrealizedPnLRes : OpenAPIMessageBase 
{
    public GetPositionUnrealizedPnLRes() { }
    public GetPositionUnrealizedPnLRes(int ctidTraderAccountId, int moneyDigits, List<PositionUnrealizedPnL> positionUnrealizedPnL)
    {
        this.Payload = new GetPositionUnrealizedPnLResPayload(ctidTraderAccountId, moneyDigits, positionUnrealizedPnL);
        this.ClientMsgId = Guid.NewGuid().ToString();
    }

    public override int PayloadType => 2188;
    public GetPositionUnrealizedPnLResPayload? Payload { get; set; }

}

public class GetPositionUnrealizedPnLResPayload : OpenAPIMessagePayloadBase
{
    public GetPositionUnrealizedPnLResPayload() { }
    public GetPositionUnrealizedPnLResPayload(int ctidTraderAccountId, int moneyDigits, List<PositionUnrealizedPnL> positionUnrealizedPnL)
    {
        this.CtidTraderAccountId = ctidTraderAccountId;
        this.MoneyDigits = moneyDigits;
        this.PositionUnrealizedPnL = positionUnrealizedPnL;
    }

    public int CtidTraderAccountId { get; set; } = 0;
    public int MoneyDigits { get; set; } = 0;
    public List<PositionUnrealizedPnL> PositionUnrealizedPnL { get; set; } = new List<PositionUnrealizedPnL>();
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class OpenAPIModelMessage:
    def __init__(self):
        pass

class PositionUnrealizedPnL(OpenAPIModelMessage):
    def __init__(self, position_id, gross_unrealized_pnl, net_unrealized_pnl):
        self.position_id = position_id
        self.gross_unrealized_pnl = gross_unrealized_pnl
        self.net_unrealized_pnl = net_unrealized_pnl

class GetPositionUnrealizedPnLReq(OpenAPIMessage):
    def __init__(self, ctid_trader_account_id, client_msg_id = str(uuid.uuid4())):
        self.ctid_trader_account_id = ctid_trader_account_id
        self.payload_type = 2187
        self.client_msg_id = client_msg_id
        self.payload = {"ctidTraderAccountId": self.ctid_trader_account_id}

    def as_json_string(self):
        return json.dumps({"clientMsgId": self.client_msg_id, "payloadType": self.payload_type, "payload": self.payload})

    @staticmethod
    def from_json(json_dct):
        return GetPositionUnrealizedPnLReq(client_id=json_dct['payload']['clientId'], client_secret=json_dct['payload']['clientSecret'], client_msg_id=json_dct['clientMsgId'])

class GetPositionUnrealizedPnLRes(OpenAPIMessage):
    def __init__(self, ctid_trader_account_id, position_unrealized_pnl, money_digits, client_msg_id):
        self.ctid_trader_account_id = ctid_trader_account_id
        self.position_unrealized_pnl = position_unrealized_pnl
        self.money_digits = money_digits
        self.client_msg_id = client_msg_id

    def as_json_string(self):
        return json.dumps({"clientMsgId": self.client_msg_id, "payloadType": self.payload_type, "payload": self.payload})

    @staticmethod
    def from_json(json_dct):
        return GetPositionUnrealizedPnLRes(ctid_trader_account_id=json_dct['payload']['clientSecret'], position_unrealized_pnl=json_dct['payload']['positionUnrealizedPnL'], money_digits=json_dct['payload']['moneyDigits'], client_msg_id=['clientMsgId'])