val broker = SimBroker()
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:
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 defaultCashAccount
-
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(), // model to use to calculate fees, commissions, etc
accountModel = MarginAccount(), // type of account to model, like a Cash or Margin account
pricingEngine = SpreadPricingEngine() // engine to use to calculate the final 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 Execution
When an order is placed at the SimBroker, the following rules apply:
-
create-orders only get executed if there is a known price for the underlying asset. Till an event occurs that has that price action available, the order will remain in its
INITIAL
state and will 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. -
modify orders will always be processed BEFORE create-orders. So if you send a
CancelOrder
andMarketOrder
for the same asset, theCancelOrder
is guaranteed to be processed first in theSimBroker
. -
Create and modify-orders will be processed based on FIFO (first-in-first-out) basis, so older orders are processed first.
-
Once an order is closed, it cannot be opened again.
The following diagram shows the possible OrderStatus
transitions:

Customize SimBroker
Custom models are a way to further customize the 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 than expected. |
The following three types of models can be implemented if the default ones that comes with roboquant are not sufficient:
-
PricingEngine
: calculate the price to use for a trade. It can be used to simulate the following type of trading cost:-
Spread (difference between the buy- and sell-price)
-
Slippage (large orders will require multiple trades against different prices)
-
-
FeeModel
: the transaction related fees and commissions. Trading cost to take into consideration: -
Broker commission fees
-
Borrowing cost when shorting
-
Storage & custodial fees
-
Exchange fees and/or rebates
-
AccountModel
: model the buying power calculations based on the account type.
Before implementing your own model, it is advised to check if the default included models with their configuration options aren’t sufficient. Below the available models with some 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() // no additional fees apply
// 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 measures when possible:
-
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.
-
Order types might behave slightly different between SimBroker and different real brokers. If possible, perform extensive paper trading testing to see if your solution still performs as expected.
-
The buying power and margin calculations might differ. Again, be on the safe side and use higher margin requirements in the SimBroker than required by your actual broker.
Account Models
Account models allow you 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 account models that cater for the most common account types:
-
CashAccount
; account that doesn’t have any leverage or margin available. -
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.
Although roboquant doesn’t stop you from using this model even when shorting assets, it is not advised to do so.
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:
-
Initial margin (aka leverage)
-
Maintenance margin, with support for different values for long and short positions
-
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 size, 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 doesn’t impact margin or buying power (so similar to INITIAL state)
Not all rules are currently implemented yet. |
Margin Calls
A negative value for BuyingPower
indicates a possible margin call situation. However, roboquant doesn’t interrupt the back-test for that.
If you are interested in detecting these conditions, best to configure a metric that logs the BuyingPower at each step and evaluate that metric after the run.