Simulated Broker

You use the SimBroker class (short for Simulated Broker) both for the back testing and forward testing stages. See also the four stages for more background about the different stages when developing trading strategies.

SimBroker can simulate a wide range of brokers and exchanges. Besides several configuration parameters that are available, the behavior can be further extended by providing custom models for trading, fee and account modelling.

Usage

The instantiation of a SimBroker is surprisingly simple if the default settings suit your needs:

val broker = SimBroker()
This is also the configuration you get when you create an instance of Roboquant and don’t specify a broker at all.

However, you can overwrite many of the default values. Common use cases are:

  • you don’t trade in USD but in some other currency

  • you want to simulate a MarginAccount and not the default CashAccount

  • you want to apply custom fees and pricing logic

So a more custom configured version of the SimBroker can be instantiated using:

val broker = SimBroker(
    initialDeposit = Wallet(10_000.EUR), // How much to initially deposit into the account
    baseCurrency = Currency.EUR, // Currency to use for reporting
    feeModel = PercentageFeeModel(), // Logic to use to calculate fees, commissions, etc
    accountModel = MarginAccount(), // Cash or Margin account
    pricingEngine = SpreadPricingEngine() // Engine to use to calculate the price of a trade
)
if you are trading specific markets, like FOREX, you most likely want to use customize the configuration since the default values like those selected for spreads are not representative for those type of markets.

The initial deposit is the cash that is deposited at the opening of an account.Any time the SimBroker is reset, the account will be reset to this initial deposit.Typically, the initial deposit is denoted in a single currency, like a $50,000.00 USD account.But this is not a hard requirement and with SimBroker you can use a multi-currency initial deposit if you prefer.However, there is always only one baseCurrency.

Order Handling

When an order is placed at the SimBroker, the following rules apply:

  • create-orders only gets executed once there is a known price for the underlying asset.Till an event appears that has that price action available, the order will remain in the INITIAL state and not be executed.

  • modify-orders will always get executed, even if there is no known price for the asset.

  • any TIF (Time In Force) enforcement of order execution will only start after an order has been accepted.So an order in INITIAL state will not expire, and time based TIF will only start counting after it has been accepted.

  • pending modify orders will always be processed BEFORE pending create orders.So if you send a CancelOrder and MarketOrder for the same asset, the CancelOrder is guaranteed to be processed first.

  • Open create orders will be processed based on FIFO (first-in-first-out) basis, so older open orders are processed first.

Custom Configuration

Custom models are a way to further configure SimBroker and ensure it closely mimics the behavior of your real broker.

if you don’t take any type of trading cost into account, your profitable strategy in back-test might turn out not to be that profitable in live trading. When in doubt, better to be on the safe side and configure the cost to be slightly higher.

The following three types of models can be implemented if the default ones that comes with roboquant are not sufficient:

  1. PricingEngine: calculate the price to use for a trade. It can be used to simulate the following type of trading cost:

    • Spread (difference between buy and sell price)

    • Slippage (large orders will require multiple trades against different prices)

    • Exchange rates

  2. FeeModel: the transaction related fees and commissions. Trading cost to take into consideration:

  3. Broker commission fees

  4. Borrowing cost when shorting

  5. Storage & custodial fees

  6. Exchange fees and/or rebates

  7. AccountModel: model the buying power calculations based on the account type.

Below are the default included models with examples of their configuration parameters:

// Account Models
val accountModel1 = CashAccount(minimum = 1_000.0)
val accountModel2 = MarginAccount(
    initialMargin = 0.5,
    maintenanceMarginLong = 0.3,
    maintenanceMarginShort = 0.2,
    minimumEquity = 10_000.0
)

// Fee models
val feeModel1 = PercentageFeeModel(0.01) // 1% fee
val feeModel2 = NoFeeModel()

// Pricing engines
val pricingEngine1 = SpreadPricingEngine(spreadInBips = 5, priceType = "OPEN")
val pricingEngine2 = NoCostPricingEngine(priceType = "CLOSE")

Limitations

Like any simulation, SimBroker also has limitations compared to live trading. It is important to realize this and take appropriate actions where possible:

  1. The default cost calculations become less realistic if you trade large sizes compared to overall daily volume. As a rule of thumb: don’t underestimate the additional costs like spread, slippage and commissions and better be on the safe side.

  2. Order types might behave slightly different between SimBroker and different real brokers. If possible, perform paper trading to see if your solution still performs as expected.

  3. The buying power and margin calculations might differ. Again, be on the safe side and use a higher margin requirement in the SimBroker than required by your actual broker.

Account Models

Account models allow to model the amount of buying-power for different types of accounts, like a Cash Account or Margin Account when using the SimBroker. Buying-power represents the amount of money available for trading.

Out of the box roboquant comes with two models that cater for the most common account types:

  1. CashAccount; account that doesn’t have any leverage or margin available

  2. MarginAccount; account that has (configurable) initial and maintenance margin requirements

Cash Accounts

Cash accounts can be modelled using the CashAccount. This model is also the default if you don’t provide a BuyingPowerModel when instantiating a SimBroker.

CashAccount bases the buying power only on the available cash, and no margin is calculated and no leverage is used. So cash and buying power are equal, however cash can contain multiple currencies while the buying power is always converted to the base currency of the account.

The below table shows an example using the default CashAccount when trading in different currencies, EUR and USD in this case.

Time Action Cash Positions Margin Equity Buying Power Unrealized PnL Realized PnL

1

open account with €10,000

€10,000

0

0

€10,000

€10,000

0

0

2

buy ABC 40 @ €100

€6,000

€4,000

0

€10,000

€6,000

0

0

3

price ABC drops to €75

€6,000

€3,000

0

€9,000

€6,000

-€1,000

0

4

sell ABC -40 @ €75

€ 9,000

0

0

€ 9,000

€9,000

0

-€1,000

5

buy XYZ 25 @ $200

€9,000 -$5,000

$5,000

0

€9,000

€4,500

0

-€1,000

6

price XYZ raises to $240

€9,000 -$5,000

$6,000

0

€9,000 $1,000

€4,500

$1,000

-€1,000

7

sell XYZ -25 @ $240

€9,000 $1,000

0

0

€9,000 $1,000

€9,900

0

-€1,000 $1,000

Margin Accounts

Margin based accounts can be modelled using the MarginAccount. This model supports:

  1. Initial margin (aka leverage)

  2. Maintenance margin, with support for different values for long and short positions

  3. Minimum amount of required equity that cannot be used as margin

All margin calculations are based on the equity of the account, and not just the cash. And although in the below tables the (maintenance) margin is shown, it is actually not exposed. Only the buying-power value is available through the account object.

The logic for calculating the buying power looks roughly like this in pseudocode:

long_value = long_positions * maintenance_margin_long
short_value = short_positions * maintenance_margin_short
excess_margin = equity - long_value - short_value - minimum_equity
buying_power = excess_margin * ( 1 / initial_margin)

The following table shows an example for long trading with a margin account in a Japanese Yen, using the following (default) values:

  • initial margin: 50%

  • maintenance margin: 30% (both for long and short positions)

  • no minimum equity is required

Also, there are no commissions or other cost associated with the transactions. This is not a recommended approach, but just used here to make it a bit simpler to comprehend.

Time Action Cash Portfolio Margin Equity Buying Power Unrealized PnL Realized PnL

1

open account with ¥1,000,000

¥1,000,000

0

0

¥1,000,000

¥2,000,000

0

0

2

buy ABC 500 @ ¥1,000

¥500,000

¥500,000

¥150,000

¥1,000,000

¥1,700,000

0

0

3

ABC drops to ¥500

¥500,000

¥250,000

¥75,000

¥750,000

¥1,350,000

-¥250,000

0

4

buy ABC 2000 @ ¥500

-¥500,000

¥1,250,000

¥375,000

¥750,000

¥750,000

-¥250,000

0

5

ABC drops to ¥400

-¥500,000

¥1,000,000

¥300,000

¥500,000

¥400,000

-¥500,000

0

6

sell ABC -2500 @ ¥400

¥500,000

0

0

¥500,000

¥1,000,000

0

-¥500,000

The following table shows another example, but this time shorting on a USD margin account. It uses the same default values for the margin calculations as the above table.

Time Action Cash Positions Margin Equity Buying Power Unrealized PnL Realized PnL

1

open account with $20,000

$20,000

0

0

$20,000

$40,000

0

0

2

sell XYZ -50 @ $200

$30,000

-$10,000

$3,000

$20,000

$34,000

0

0

3

price XYZ raises to $300

$30,000

-$15,000

$4,500

$15,000

$21,000

-$5,000

0

5

sell XYZ -50 @ $300

$45,000

-$30,000

$9,000

$15,000

$12,000

-$5,000

0

6

buy XYZ 100 @ $300

$15,000

0

0

$15,000

$30,000

0

-$5,000

Order impact on Buying Power

The following rules apply when taking into account orders when calculating the buying power:

  • until an order is accepted, it doesn’t impact margin or buying power.

  • open orders that reduce a position, don’t require buying power. So you can always close a position, both short and long positions.

  • once an order has been accepted, it will not be cancelled due to lack of buying power.

  • an order in a closed state will not impact margin or buying power (so similar to INITIAL state)

Not all rules are currently implemented yet.