Broker

What is a broker

A broker receives orders and processes them. The outcome of this processing is an updated Account object. There are two types of broker implementations:

  1. SimBroker: simulates a broker and the trading that happens on an exchange.

  2. Third party brokers: implementations that integrate with a real broker like IBKR, Alpaca or Binance.

These two types implement the same Broker interface. Normally during back-testing and forward-testing you’ll use the SimBroker, and during paper-trading and live-trading you’ll use a real broker. The main method is broker.place. And although normally you wouldn’t invoke it directly since the Roboquant instance would do that as part of a run, you can if you want to:

val orders = listOf(
    MarketOrder(Asset("AAPL"), 100), // BUY 100 stocks Apple
    MarketOrder(Asset("TSLA"), -100), // SELL 100 stocks Tesla
)
val account = broker.place(orders)

Normally you would specify the broker you want to use when you create an instance of `Roboquant'

val roboquant = Roboquant(EMAStrategy(), AccountMetric(), broker = BinanceBroker())
println(roboquant.broker.account.fullSummary())
Broker implementations can differ in what order types and assets are supported. So when moving from the SimBroker to a real broker, always validate if the behavior is still as expected.

Account

broker.account property returns an Account object. This Account object contains a snapshot of the exposed state of a broker and is identical between the simulated broker and real brokers. The object is guaranteed not to change, which also implies that if you want an updated version, you need to invoke broker.account again.

The Account object contains the following key properties:

  • base currency of the account, used when converting a Wallet into a single-currency Amount

  • total cash balance (represents the credit cash - loan value)

  • the open positions

  • trades that contain all the trade executions that have happened so far

  • orders, both open orders and closed orders

  • total amount of buying power available for trading (denoted in the base currency of the account)

Always use the Account.buyingPower property if you want to know how much money is remaining for trading. Especially when using a MarginAccount, you shouldn’t rely on the Account.cash property, since that doesn’t take into account any margin or leverage you might have.

Since properties like trades and openOrders of the account object are plain Kotlin collections, you can use standard Kotlin access patterns. Kotlin’s collection APIs are a very powerful tool to access data structures once you master them.

If you are new to Kotlin’s collections, you can go to this page for more info: https://kotlinlang.org/docs/collections-overview.html

Additionally, roboquant defines several extensions that come in handy when dealing with the account object.

val account = broker.account

// Print the account
println(account.summary())
println(account.fullSummary())

// Standard Kotlin
val winningTrades = account.trades.filter { it.pnl > 0 }
val loosingTrades = account.trades.filter { it.pnl < 0 }
val biggestExposure = account.positions.maxBy { it.exposure.value }
val marketOrders = account.openOrders.filter { it.order is MarketOrder}
val appleTrades = account.trades.filter { it.asset.symbol == "AAPL" }

// Some useful extensions
val totalUnrealizedPNL = account.positions.unrealizedPNL
val totalRealizedPNL = account.trades.realizedPNL
val totalRealizedLosses = loosingTrades.realizedPNL
val biggestLoosingTrade = account.trades.map { it.pnl.convert(Currency.USD) }.minBy { it.value }
val totalFee = account.trades.fee
val realizedPNLApple = appleTrades.realizedPNL
val cancellationOrders = account.openOrders.cancel()

// More advanced examples
val zone = ZoneId.systemDefault()
val realizedPNLPerDayOfWeek = account.trades
    .groupBy { it.time.atZone(zone).get(ChronoField.DAY_OF_WEEK) }
    .mapValues { it.value.realizedPNL }
val maxFeeExchange = account.trades
    .groupBy { it.asset.exchange }
    .mapValues { it.value.fee.convert(Currency.USD) }
    .maxBy { it.value.value }

As shown in the code snippet above, you can print an account.summary or account.fullSummary overview. The full summary includes all positions, trades and orders, so it can become very large after an extensive back test:

account
├── last update: 2023-01-09T14:47:01Z
├── base currency: BUSD
├── cash: BUSD 199374.52
├── buying power: BUSD 199374.52
├── equity: BUSD 999175.51
├── position summary
│   ├── open: 4
│   ├── long: 4
│   ├── short: 0
│   ├── total value: BUSD 799800.99
│   ├── long value: BUSD 799800.99
│   ├── short value: BUSD 0.00
│   └── unrealized p&l: BUSD 1085.35
├── trade summary
│   ├── total: 20
│   ├── realized p&l: BUSD -1909.83
│   ├── fee:
│   ├── first: 2023-01-09T14:03:00.170064Z
│   ├── last: 2023-01-09T14:43:00.149166Z
│   ├── winners: 2
│   └── loosers: 6
├── order summary
│   ├── open: 0
│   ├── closed: 20
│   ├── completed: 20
│   ├── cancelled: 0
│   ├── expired: 0
│   └── rejected: 0
├── cash
│   ├──   ccy│    amount│
│   └──  BUSD│ 199374.52│
├── positions
│   ├──   symbol│  ccy│       size│ entry price│ mkt price│ mkt value│ unrlzd p&l│
│   ├──  XRPBUSD│ BUSD│  567419.08│        0.35│      0.35│ 200128.71│     433.97│
│   ├──  BNBBUSD│ BUSD│     714.96│      279.33│    279.80│ 200045.81│     337.51│
│   ├──  TRXBUSD│ BUSD│ 3630666.41│        0.05│      0.06│ 199831.88│     270.50│
│   └──  ETHBUSD│ BUSD│     150.81│     1324.52│   1324.81│ 199794.60│      43.37│
├── open orders
│   └── EMPTY
├── closed orders
│   ├──   type│  symbol│  ccy│    status│  id│            opened at│            closed at│                  details│
│   ├── MARKET│ TRXBUSD│ BUSD│ COMPLETED│ 336│ 2023-01-09T14:02:00Z│ 2023-01-09T14:03:00Z│  size=3640997.63 tif=GTC│
│   ├── MARKET│ XRPBUSD│ BUSD│ COMPLETED│ 337│ 2023-01-09T14:03:00Z│ 2023-01-09T14:04:00Z│   size=571265.35 tif=GTC│
│   ├── MARKET│ BNBBUSD│ BUSD│ COMPLETED│ 338│ 2023-01-09T14:04:00Z│ 2023-01-09T14:05:00Z│      size=719.66 tif=GTC│
│   ├── MARKET│ BNBBUSD│ BUSD│ COMPLETED│ 339│ 2023-01-09T14:06:00Z│ 2023-01-09T14:07:00Z│     size=-719.66 tif=GTC│
│   ├── MARKET│ XRPBUSD│ BUSD│ COMPLETED│ 340│ 2023-01-09T14:06:00Z│ 2023-01-09T14:07:00Z│  size=-571265.35 tif=GTC│
│   ├── MARKET│ TRXBUSD│ BUSD│ COMPLETED│ 341│ 2023-01-09T14:06:01Z│ 2023-01-09T14:07:00Z│ size=-3640997.63 tif=GTC│
│   ├── MARKET│ ETHBUSD│ BUSD│ COMPLETED│ 342│ 2023-01-09T14:07:00Z│ 2023-01-09T14:08:00Z│     size=-151.51 tif=GTC│
│   ├── MARKET│ BTCBUSD│ BUSD│ COMPLETED│ 343│ 2023-01-09T14:07:00Z│ 2023-01-09T14:08:00Z│      size=-11.59 tif=GTC│
│   ├── MARKET│ ETHBUSD│ BUSD│ COMPLETED│ 344│ 2023-01-09T14:11:00Z│ 2023-01-09T14:12:00Z│      size=151.51 tif=GTC│
│   ├── MARKET│ BTCBUSD│ BUSD│ COMPLETED│ 345│ 2023-01-09T14:11:00Z│ 2023-01-09T14:12:00Z│       size=11.59 tif=GTC│
│   ├── MARKET│ BNBBUSD│ BUSD│ COMPLETED│ 346│ 2023-01-09T14:12:00Z│ 2023-01-09T14:13:00Z│      size=717.63 tif=GTC│
│   ├── MARKET│ XRPBUSD│ BUSD│ COMPLETED│ 347│ 2023-01-09T14:12:00Z│ 2023-01-09T14:13:00Z│   size=569823.99 tif=GTC│
│   ├── MARKET│ TRXBUSD│ BUSD│ COMPLETED│ 348│ 2023-01-09T14:18:01Z│ 2023-01-09T14:19:00Z│  size=3637915.47 tif=GTC│
│   ├── MARKET│ TRXBUSD│ BUSD│ COMPLETED│ 349│ 2023-01-09T14:36:01Z│ 2023-01-09T14:37:00Z│ size=-3637915.47 tif=GTC│
│   ├── MARKET│ XRPBUSD│ BUSD│ COMPLETED│ 350│ 2023-01-09T14:37:00Z│ 2023-01-09T14:38:00Z│  size=-569823.99 tif=GTC│
│   ├── MARKET│ BNBBUSD│ BUSD│ COMPLETED│ 351│ 2023-01-09T14:38:00Z│ 2023-01-09T14:39:00Z│     size=-717.63 tif=GTC│
│   ├── MARKET│ XRPBUSD│ BUSD│ COMPLETED│ 352│ 2023-01-09T14:40:00Z│ 2023-01-09T14:41:00Z│   size=567419.08 tif=GTC│
│   ├── MARKET│ BNBBUSD│ BUSD│ COMPLETED│ 353│ 2023-01-09T14:41:00Z│ 2023-01-09T14:42:00Z│      size=714.96 tif=GTC│
│   ├── MARKET│ TRXBUSD│ BUSD│ COMPLETED│ 354│ 2023-01-09T14:41:01Z│ 2023-01-09T14:42:00Z│  size=3630666.41 tif=GTC│
│   └── MARKET│ ETHBUSD│ BUSD│ COMPLETED│ 355│ 2023-01-09T14:42:00Z│ 2023-01-09T14:43:00Z│      size=150.81 tif=GTC│
└── trades
    ├──                  time│  symbol│  ccy│        size│       cost│  fee│ rlzd p&l│    price│
    ├──  2023-01-09T14:03:00Z│ TRXBUSD│ BUSD│  3640997.63│  199983.59│ 0.00│     0.00│     0.05│
    ├──  2023-01-09T14:04:00Z│ XRPBUSD│ BUSD│   571265.35│  200191.40│ 0.00│     0.00│     0.35│
    ├──  2023-01-09T14:05:00Z│ BNBBUSD│ BUSD│      719.66│  199869.57│ 0.00│     0.00│   277.73│
    ├──  2023-01-09T14:07:00Z│ BNBBUSD│ BUSD│     -719.66│ -199613.72│ 0.00│  -255.85│   277.37│
    ├──  2023-01-09T14:07:00Z│ XRPBUSD│ BUSD│  -571265.35│ -199294.55│ 0.00│  -896.85│     0.35│
    ├──  2023-01-09T14:07:00Z│ TRXBUSD│ BUSD│ -3640997.63│ -199361.09│ 0.00│  -622.49│     0.05│
    ├──  2023-01-09T14:08:00Z│ ETHBUSD│ BUSD│     -151.51│ -199576.28│ 0.00│     0.00│  1317.25│
    ├──  2023-01-09T14:08:00Z│ BTCBUSD│ BUSD│      -11.59│ -199515.11│ 0.00│     0.00│ 17214.42│
    ├──  2023-01-09T14:12:00Z│ ETHBUSD│ BUSD│      151.51│  200113.21│ 0.00│  -536.92│  1320.79│
    ├──  2023-01-09T14:12:00Z│ BTCBUSD│ BUSD│       11.59│  199726.10│ 0.00│  -210.99│ 17232.62│
    ├──  2023-01-09T14:13:00Z│ BNBBUSD│ BUSD│      717.63│  199449.32│ 0.00│     0.00│   277.93│
    ├──  2023-01-09T14:13:00Z│ XRPBUSD│ BUSD│   569823.99│  199629.30│ 0.00│     0.00│     0.35│
    ├──  2023-01-09T14:19:00Z│ TRXBUSD│ BUSD│  3637915.47│  199814.30│ 0.00│     0.00│     0.05│
    ├──  2023-01-09T14:37:00Z│ TRXBUSD│ BUSD│ -3637915.47│ -199519.71│ 0.00│  -294.59│     0.05│
    ├──  2023-01-09T14:38:00Z│ XRPBUSD│ BUSD│  -569823.99│ -199931.24│ 0.00│   301.94│     0.35│
    ├──  2023-01-09T14:39:00Z│ BNBBUSD│ BUSD│     -717.63│ -200055.24│ 0.00│   605.92│   278.77│
    ├──  2023-01-09T14:41:00Z│ XRPBUSD│ BUSD│   567419.08│  199694.74│ 0.00│     0.00│     0.35│
    ├──  2023-01-09T14:42:00Z│ BNBBUSD│ BUSD│      714.96│  199708.30│ 0.00│     0.00│   279.33│
    ├──  2023-01-09T14:42:00Z│ TRXBUSD│ BUSD│  3630666.41│  199561.38│ 0.00│     0.00│     0.05│
    └──  2023-01-09T14:43:00Z│ ETHBUSD│ BUSD│      150.81│  199751.23│ 0.00│     0.00│  1324.52│ 
Equity

Equity is a key metric to monitor if you want to see how your strategy is performing during a run. The following two equations hold true when running a back test:

// Equity property
val equity0 = account.equity

// Sum of cash balances + open positions
val equity1 = account.cash + account.positions.marketValue

// Sum of initial deposit + all the Profit And Loss
val equity2 = initialDeposit + account.trades.realizedPNL + account.positions.unrealizedPNL

assert(equity0 == equity1)
assert(equity0 == equity2)

The metric AccountMetric makes the equity curve available for further analysis.

When you plot the equity curve in a Jupyter Notebook, you don’t only can see the PNL performance, you also can see how smooth the curve is, providing a visual indication on the volatility of the returns of your strategy.

Out of the box available brokers

The following Broker implementations are available:

brokers

See also SimBroker and Integration for more details on how to use these brokers.