undefined

Account & Portfolio Data

LYNX offers a comprehensive overview of your account and portfolio through its Account and Portfolio windows.

Account Updates

Requesting

The IBApi.EClient.reqAccountUpdates function creates a subscription to LYNX through which account and portfolio information is delivered. This information is the exact same as the one displayed within the LYNX Account Window. Note this function receives a specific account along with a flag indicating whether to start or stop the subscription. In a single account structure, the account number is not necessary. Just as with the LYNX Account Window, unless there is a position change this information is updated at a fixed interval of three minutes.

** Python **

self.reqAccountUpdates(True, self.account)

** Java **

client.reqAccountUpdates(true, "U150462");

Receiving

Resulting account and portfolio information will be delivered via the IBApi.EWrapper.updateAccountValue, IBApi.EWrapper.updatePortfolio, IBApi.EWrapper.updateAccountTime and IBApi.EWrapper.accountDownloadEnd

** Python **

class TestWrapper(wrapper.EWrapper):

def updateAccountValue(self, key: str, val: str, currency:
                       str,accountName: str):
    super().updateAccountValue(key, val, currency, accountName)
    print("UpdateAccountValue. Key:", key, "Value:", val,
          "Currency:", currency, "AccountName:", accountName)


def updatePortfolio(self, contract: Contract, position: float,
                    marketPrice: float, marketValue:
                    float,averageCost: float, unrealizedPNL:
                    float,realizedPNL: float, accountName: str):
    super().updatePortfolio(contract, position, marketPrice,
                            marketValue,averageCost, unrealizedPNL,
                            realizedPNL, accountName)

    print("UpdatePortfolio.", "Symbol:", contract.symbol, "SecType:",
          contract.secType, "Exchange:",contract.exchange,
          "Position:", position, "MarketPrice:",
          marketPrice,"MarketValue:", marketValue, "AverageCost:",
          averageCost,"UnrealizedPNL:", unrealizedPNL, "RealizedPNL:",
          realizedPNL,"AccountName:", accountName)

def updateAccountTime(self, timeStamp: str):
    super().updateAccountTime(timeStamp)
    print("UpdateAccountTime. Time:", timeStamp)

def accountDownloadEnd(self, accountName: str):
    super().accountDownloadEnd(accountName)
    print("AccountDownloadEnd. Account:", accountName)

** Java **

public class EWrapperImpl implements EWrapper {

@Override
public void updateAccountValue(String key, String value, String currency,
        String accountName) {
    System.out.println("UpdateAccountValue. Key: " + key + ", Value: " + value + ", Currency: " + currency + ", AccountName: " + accountName);
}

@Override
public void updatePortfolio(Contract contract, double position,
        double marketPrice, double marketValue, double averageCost,
        double unrealizedPNL, double realizedPNL, String accountName) {
    System.out.println("UpdatePortfolio. "+contract.symbol()+", "+contract.secType()+" @ "+contract.exchange()
            +": Position: "+position+", MarketPrice: "+marketPrice+", MarketValue: "+marketValue+", AverageCost: "+averageCost
            +", UnrealizedPNL: "+unrealizedPNL+", RealizedPNL: "+realizedPNL+", AccountName: "+accountName);
}

@Override
public void updateAccountTime(String timeStamp) {
    System.out.println("UpdateAccountTime. Time: " + timeStamp+"\n");
}

@Override
public void accountDownloadEnd(String accountName) {
    System.out.println("Account download finished: "+accountName+"\n");
}

Cancelling

Once the subscription to account updates is no longer needed, it can be cancelled by invoking the IBApi.EClient.reqAccountUpdates method while specifying the susbcription flag to be False:

** Python **

self.reqAccountUpdates(False, self.account)

** Java **

client.reqAccountUpdates(false, "U123456");

[!NOTE] An important key passed back in IBApi.EWrapper.updateAccountValue after a call to IBApi.EClient.reqAccountUpdates is a boolean value 'accountReady'. If an accountReady value of false is returned that means that the IB server is in the process of resetting at that moment, i.e. the account is 'not ready'. When this occurs subsequent key values returned to IBApi.EWrapper.updateAccountValue in the current update can be out of date or incorrect.

[!WARNING] Only one account at a time can be subscribed at a time. Attempting a second subscription without previously cancelling an active one will not yield any error message although it will override the already subscribed account with the new one. With Financial Advisory (FA) account structures there is an alternative way of specifying the account code such that information is returned for 'All' sub accounts- this is done by appending the letter 'A' to the end of the account number, i.e. reqAccountUpdates(true, "F123456A")

Identifying the Account Keys

Account values delivered via IBApi.EWrapper.updateAccountValue can be classified in the following way:

  • Commodities: suffixed by a "-C"
  • Securities: suffixed by a "-S"
  • Totals: no suffix

Account Value Update Subscriptions by Model

The IBApi.EClient.reqAccountUpdatesMulti can be used in any account structure to create simultaneous account value subscriptions from one or multiple accounts and/or models. As with IBApi.EClient.reqAccountUpdates the data returned will match that displayed within the LYNX Account Window.

self.reqAccountUpdatesMulti(9005, self.account, "", True)

** Python **

self.reqAccountUpdatesMulti(9005, self.account, "", True)

** Java **

client.reqAccountUpdatesMulti(9002, "U123456", "EUstocks", true);

The resulting account and portfolio information will be delivered via the IBApi.EWrapper.accountUpdateMulti and IBApi.EWrapper.accountUpdateMultiEnd

** Python **

class TestWrapper(wrapper.EWrapper):

def accountUpdateMulti(self, reqId: int, account: str, modelCode: str,
                       key: str, value: str, currency: str):
super().accountUpdateMulti(reqId, account, modelCode, key, value,currency)
print("AccountUpdateMulti. RequestId:", reqId, "Account:", account,"ModelCode:", modelCode, "Key:", key, "Value:", value,
      "Currency:", currency)

def accountUpdateMultiEnd(self, reqId: int):
    super().accountUpdateMultiEnd(reqId)
    print("AccountUpdateMultiEnd. RequestId:", reqId)

** Java **

public class EWrapperImpl implements EWrapper {

    @Override
    public void accountUpdateMulti(int reqId, String account, String modelCode,
            String key, String value, String currency) {
        System.out.println("Account Update Multi. Request: " + reqId + ", Account: " + account + ", ModelCode: " + modelCode + ", Key: " + key + ", Value: " + value + ", Currency: " + currency + "\n");
    }

    @Override
    public void accountUpdateMultiEnd(int reqId) {
        System.out.println("Account Update Multi End. Request: " + reqId + "\n");
    }

Account Summary

Requesting

The IBApi.EClient.reqAccountSummary method creates a subscription for the account data displayed in the LYNX Account Summary window. It is commonly used with multiple-account structures. Introducing broker (IBroker) accounts with more than 50 subaccounts or configured for on-demand account lookup cannot use reqAccountSummary with group="All".

Unlike IBApi.EClient.reqAccountUpdates , IBApi.EClient.reqAccountSummary can not only retrieve summarized information for either one or all the managed accounts but also extract only the specified values to be monitored by the client application. The initial invocation of reqAccountSummary will result in a list of all requested values being returned, and then every three minutes those values which have changed will be returned. The update frequency of 3 minutes is the same as the LYNX Account Window and cannot be changed.

** Python **

self.reqAccountSummary(9001, "All", AccountSummaryTags.AllTags)

** Java **

client.reqAccountSummary(9001, "All", "AccountType,NetLiquidation,TotalCashValue,SettledCash,AccruedCash,BuyingPower,EquityWithLoanValue,PreviousEquityWithLoanValue,GrossPositionValue,ReqTEquity,ReqTMargin,SMA,InitMarginReq,MaintMarginReq,AvailableFunds,ExcessLiquidity,Cushion,FullInitMarginReq,FullMaintMarginReq,FullAvailableFunds,FullExcessLiquidity,LookAheadNextChange,LookAheadInitMarginReq ,LookAheadMaintMarginReq,LookAheadAvailableFunds,LookAheadExcessLiquidity,HighestSeverity,DayTradesRemaining,Leverage");  

Starting from LYNX Build 956 and IB Gateway 956, we have added the function to request account summary data (including CashBalance and TotalCashBalance) for every currency separately using LEDGER tags.

When the "$LEDGER" tag is specified, the account summary data will be returned in BASE CURRENCY only.

** Python **

self.reqAccountSummary(9002, "All", "$LEDGER")

** Java **

client.reqAccountSummary(9002, "All", "$LEDGER"); 

When the "$LEDGER:CURRENCY" tag is specified, the account summary data will be returned only in the CURRENCY specified. The CashBalance and TotalCashBalance returned are the balance in that specific currency only as you see within the LYNX Account Window.

Example: "$LEDGER:USD", "$LEDGER:EUR", "$LEDGER:HKD" etc.

** Python **

self.reqAccountSummary(9003, "All", "$LEDGER:EUR")

** Java **

client.reqAccountSummary(9003, "All", "$LEDGER:EUR"); 

When the "$LEDGER:ALL" tag is specified, the account summary data returned will be summed up values for ALL accounts and currencies.

Example:

Account = All, Currency = EUR, CashBalance = 12345.67

Account = All, Currency = JPY, CashBalance = 987.54

** Python **

self.reqAccountSummary(9004, "All", "$LEDGER:ALL")

** Java **

client.reqAccountSummary(9004, "All", "$LEDGER:ALL");

[!WARNING] Only two active summary subscriptions are allowed at a time!

Receiving

Summarised information is delivered via IBApi.EWrapper.accountSummary and IBApi.EWrapper.accountSummaryEnd

** Python **

class TestWrapper(wrapper.EWrapper):

def accountSummary(self, reqId: int, account: str, tag: str, value: str,currency: str):
    super().accountSummary(reqId, account, tag, value, currency)
    print("AccountSummary. ReqId:", reqId, "Account:", account,"Tag:
          ", tag, "Value:", value, "Currency:", currency)

def accountSummaryEnd(self, reqId: int):
    super().accountSummaryEnd(reqId)
    print("AccountSummaryEnd. ReqId:", reqId)

** Java **

public class EWrapperImpl implements EWrapper {

@Override
public void accountSummary(int reqId, String account, String tag,
        String value, String currency) {
    System.out.println("Acct Summary. ReqId: " + reqId + ", Acct: " + account + ", Tag: " + tag + ", Value: " + value + ", Currency: " + currency);
}

@Override
public void accountSummaryEnd(int reqId) {
    System.out.println("AccountSummaryEnd. Req Id: "+reqId+"\n");
}

Cancelling

Once the subscription to account summary is no longer needed, it can be cancelled via the IBApi.EClient.cancelAccountSummary method:

** Python **

self.cancelAccountSummary(9001)
self.cancelAccountSummary(9002)
self.cancelAccountSummary(9003)
self.cancelAccountSummary(9004)

** Java **

client.cancelAccountSummary(9001);
client.cancelAccountSummary(9002);
client.cancelAccountSummary(9003);
client.cancelAccountSummary(9004);

Positions

Requesting

A limitation of the function IBApi.EClient.reqAccountUpdates is that it can only be used with a single account at a time. To create a subscription for position updates from multiple accounts, the function IBApi.EClient.reqPositions is available.

[!NOTE] The reqPositions function is not available in Introducing Broker or Financial Advisor master accounts that have very large numbers of subaccounts (> 50) to optimize the performance of LYNX Gateway v973+. Instead the function reqPositionsMulti can be used to subscribe to updates from individual subaccounts. Also not available with IBroker accounts configured for on-demand account lookup.

After initially invoking reqPositions, information about all positions in all associated accounts will be returned, followed by the IBApi.EWrapper.positionEnd callback. Thereafter, when a position has changed an update will be returned to the IBApi.EWrapper.position function. To cancel a reqPositions subscription, invoke IBApi.EClient.cancelPositions.

** Python **

self.reqPositions()

** Java **

client.reqPositions();

Receiving

After invoking the above, the positions will then be received through the IBApi.EWrapper.position callback. After the initial callback (only) of all positions, the IBApi.EWrapper.positionEnd function will be triggered.

  • For futures, the exchange field will not be populated in the position callback as some futures trade on multiple exchanges

** Python **

class TestWrapper(wrapper.EWrapper):

def position(self, account: str, contract: Contract, position: float, avgCost: float):
    super().position(account, contract, position, avgCost)
    print("Position.", "Account:", account, "Symbol:",
          contract.symbol, "SecType:",
          contract.secType, "Currency:", contract.currency,
          "Position:", position, "Avg cost:", avgCost)

def positionEnd(self):
    super().positionEnd()
    print("PositionEnd")

** Java **

public class EWrapperImpl implements EWrapper {

@Override
public void position(String account, Contract contract, double pos,
        double avgCost) {
    System.out.println("Position. "+account+" - Symbol: "+contract.symbol()+", SecType: "+contract.secType()+", Currency: "+contract.currency()+", Position: "+pos+", Avg cost: "+avgCost);
}

@Override
public void positionEnd() {
    System.out.println("PositionEnd \n");
}

Cancelling

To cancel the reqPosition subscription, invoke IBApi.EClient.cancelPositions :

** Python **

self.cancelPositions()

** Java **

client.cancelPositions();

Position Update Subscription by Model

The function IBApi.EClient.reqPositionsMulti can be used with any account structure to subscribe to positions updates for multiple accounts and/or models. The account and model parameters are optional if there are not multiple accounts or models available.

** Python **

self.reqPositionsMulti(9006, self.account, "")

** Java **

client.reqPositionsMulti(9003, "DU74649", "EUstocks");  

After invoking IBApi.EClient.reqPositionsMulti data will be returned to the IBApi.EWrapper.positionMulti function. After the initial callback of all positions matching the supplied criteria to reqPositionsMulti, the IBApi.EWrapper.positionMultiEnd function will be triggered. Thereafter, there will only be messages sent to positionsMulti when there is a change. To cancel a positionsMulti subscription the function IBApi.EClient.cancelPositionsMulti is invoked with the same request ID used to create the subscription.

** Python **

class TestWrapper(wrapper.EWrapper):

def positionMulti(self, reqId: int, account: str, modelCode: str,contract: Contract, pos: float, avgCost: float):
    super().positionMulti(reqId, account, modelCode, contract, pos,
                          avgCost)
    print("PositionMulti. RequestId:", reqId, "Account:",
          account,"ModelCode:", modelCode, "Symbol:", contract.symbol,
          "SecType:",contract.secType, "Currency:", contract.currency,
          ",Position:", pos, "AvgCost:", avgCost)

def positionMultiEnd(self, reqId: int):
super().positionMultiEnd(reqId)
print("PositionMultiEnd. RequestId:", reqId)

** Java **

public class EWrapperImpl implements EWrapper {

@Override
public void positionMulti(int reqId, String account, String modelCode,
        Contract contract, double pos, double avgCost) {
    System.out.println("Position Multi. Request: " + reqId + ", Account: " + account + ", ModelCode: " + modelCode + ", Symbol: " + contract.symbol() + ", SecType: " + contract.secType() + ", Currency: " + contract.currency() + ", Position: " + pos + ", Avg cost: " + avgCost + "\n");
}

@Override
public void positionMultiEnd(int reqId) {
    System.out.println("Position Multi End. Request: " + reqId + "\n");
}

Profit And Loss (P&L)

P&L data in the Account Window

UnRealized and Realized P&L is sent to the API function IBApi.EWrapper.updateAccountValue function after a subscription request is made with IBApi.EClient.reqAccountUpdates. This information corresponds to the data in the LYNX Account Window, and has a different source of information, a different update frequency, and different reset schedule than PnL data in the LYNX Portfolio Window and associated API functions (below). In particular, the unrealized P&L information shown in the LYNXAccount Window which is sent to updatePortfolioValue will update either (1) when a trade for that particular instrument occurs or (2) every 3 minutes. The realized P&L data in the LYNX Account Window is reset to 0 once per day.

  • It is important to keep in mind that the P&L data shown in the Account Window and Portfolio Window will sometimes differ because there is a different source of information and a different reset schedule.

P&L data in the Portfolio Window

Beginning with API v973.03, requests can be made to receive real time updates about the daily P&L and unrealized P&L for an account, or for individual positions. Financial Advisors can also request P&L figures for 'All' subaccounts, or for a portfolio model. In API v973.05+/LYNX v968+ this is further extended to include realized P&L information at the account or individual position level.

These newer P&L API functions demonstrated below return the data which is displayed in the LYNX Portfolio Window in current versions of LYNX (v963+). As such, the P&L values are calculated based on the reset schedule specified in LYNX Global Configuration (by default an instrument-specific reset schedule) and this setting affects values sent to the associated API functions as well. Also on the LYNX Platform, P&L data from virtual forex positions will be included in the account P&L if and only if the Virtual Fx section of the Account Window is expanded.

[!NOTE] The P&L functions in Python API are available starting in API v973.06+.

P&L subscription requests for individual positions

Subscribe using the IBApi.EClient.reqPnLSingle function Cannot be used with IBroker accounts configured for on-demand lookup with account = 'All'

** Python **

self.reqPnLSingle(17002, "DU111519", "", 8314)

** Java **

client.reqPnLSingle(17001, "DUD00029", "", 268084);

Currently updates are returned to IBApi.EWrapper.pnlSingle approximately once per second. *subject to change in the future

** Python **

def pnlSingle(self, reqId: int, pos: int, dailyPnL: float, unrealizedPnL: float, realizedPnL: float, value: float):

super().pnlSingle(reqId, pos, dailyPnL, unrealizedPnL, realizedPnL, value)
print("Daily PnL Single. ReqId:", reqId, "Position:", pos,"DailyPnL:", dailyPnL, "UnrealizedPnL:", unrealizedPnL,"RealizedPnL:", realizedPnL, "Value:", value)

** Java **

@Override
public void pnlSingle(int reqId, int pos, double dailyPnL, double unrealizedPnL, double realizedPnL, double value) {
    System.out.println(EWrapperMsgGenerator.pnlSingle(reqId, pos, dailyPnL, unrealizedPnL, realizedPnL, value));                
}
  • If a P&L subscription request is made for an invalid conId or contract not in the account, there will not be a response.
  • As elsewhere in the API, a max double value will indicate an 'unset' value. This corresponds to an empty cell.
  • Introducing broker accounts without a large number of subaccounts (<50) can receive aggregate data by specifying the account as "All".
  • *Cannot be used with IBroker accounts configured for on-demand lookup with account = 'All'

Subscriptions are cancelled using the IBApi.EClient.cancelPnLSingle function:

** Python **

self.cancelPnLSingle(17002)

** Java **

client.cancelPnLSingle(17001); 

P&L subscription requests for accounts

Subscribe using the IBApi.EClient.reqPnL function:

** Python **

self.reqPnL(17001, "DU111519", "")

** Java **

client.reqPnL(17001, "DUD00029", "");  
  • Introducing broker accounts with less than 50 subaccounts can receive aggregate PnL for all subaccounts by specifying 'All' as the account code.
  • With requests for advisor accounts with many subaccounts and/or positions can take several seconds for aggregated P&L to be computed and returned.
  • For account P&L data the setting "Prepare portfolio PnL data when downloading positions" must be checked.

Updates are sent to IBApi.EWrapper.pnl

** Python **

def pnl(self, reqId: int, dailyPnL: float,unrealizedPnL: float, realizedPnL: float):
super().pnl(reqId, dailyPnL, unrealizedPnL, realizedPnL)
print("Daily PnL. ReqId:", reqId, "DailyPnL:", dailyPnL,"UnrealizedPnL:", unrealizedPnL, "RealizedPnL:", realizedPnL)

** Java **

@Override
public void pnl(int reqId, double dailyPnL, double unrealizedPnL, double realizedPnL) {
    System.out.println(EWrapperMsgGenerator.pnl(reqId, dailyPnL, unrealizedPnL, realizedPnL));
}

Cancel unnecessary subscriptions with the IBApi.EClient.cancelPnL function:

** Python **

self.cancelPnL(17001)

** Java **

client.cancelPnL(17001);