Synthetic Instruments
The platform supports creating customized synthetic instruments, which can generate synthetic quote and trades. These are useful for:
- Enabling
Actor
andStrategy
components to subscribe to quote or trade feeds - Triggering emulated orders
- Constructing bars from synthetic quotes or trades
Synthetic instruments cannot be traded directly, as they are constructs that only exist locally within the platform. They serve as analytical tools, providing useful metrics based on their component instruments.
In the future, we plan to support order management for synthetic instruments, enabling trading of their component instruments based on the synthetic instrument's behavior.
The venue for a synthetic instrument is always designated as 'SYNTH'
.
Formula
A synthetic instrument is composed of a combination of two or more component instruments (which can include instruments from multiple venues), as well as a "derivation formula". Utilizing the dynamic expression engine powered by the evalexpr Rust crate, the platform can evaluate the formula to calculate the latest synthetic price tick from the incoming component instrument prices.
See the evalexpr
documentation for a full description of available features, operators and precedence.
Before defining a new synthetic instrument, ensure that all component instruments are already defined and exist in the cache.
Subscribing
The following example demonstrates the creation of a new synthetic instrument with an actor/strategy.
This synthetic instrument will represent a simple spread between Bitcoin and
Ethereum spot prices on Binance. For this example, it is assumed that spot instruments for
BTCUSDT.BINANCE
and ETHUSDT.BINANCE
are already present in the cache.
from nautilus_trader.model.instruments import SyntheticInstrument
btcusdt_binance_id = InstrumentId.from_str("BTCUSDT.BINANCE")
ethusdt_binance_id = InstrumentId.from_str("ETHUSDT.BINANCE")
# Define the synthetic instrument
synthetic = SyntheticInstrument(
symbol=Symbol("BTC-ETH:BINANCE"),
price_precision=8,
components=[
btcusdt_binance_id,
ethusdt_binance_id,
],
formula=f"{btcusdt_binance_id} - {ethusdt_binance_id}",
ts_event=self.clock.timestamp_ns(),
ts_init=self.clock.timestamp_ns(),
)
# Recommended to store the synthetic instruments ID somewhere
self._synthetic_id = synthetic.id
# Add the synthetic instrument for use by other components
self.add_synthetic(synthetic)
# Subscribe to quotes for the synthetic instrument
self.subscribe_quote_ticks(self._synthetic_id)
The instrument_id
for the synthetic instrument in the above example will be structured as {symbol}.{SYNTH}
, resulting in 'BTC-ETH:BINANCE.SYNTH'
.
Updating formulas
It's also possible to update a synthetic instrument formulas at any time. The following example shows how to achieve this with an actor/strategy.
# Recover the synthetic instrument from the cache (assuming `synthetic_id` was assigned)
synthetic = self.cache.synthetic(self._synthetic_id)
# Update the formula, here is a simple example of just taking the average
new_formula = "(BTCUSDT.BINANCE + ETHUSDT.BINANCE) / 2"
synthetic.change_formula(new_formula)
# Now update the synthetic instrument
self.update_synthetic(synthetic)
Trigger instrument IDs
The platform allows for emulated orders to be triggered based on synthetic instrument prices. In the following example, we build upon the previous one to submit a new emulated order. This order will be retained in the emulator until a trigger from synthetic quotes releases it. It will then be submitted to Binance as a MARKET order:
order = self.strategy.order_factory.limit(
instrument_id=ETHUSDT_BINANCE.id,
order_side=OrderSide.BUY,
quantity=Quantity.from_str("1.5"),
price=Price.from_str("30000.00000000"), # <-- Synthetic instrument price
emulation_trigger=TriggerType.DEFAULT,
trigger_instrument_id=self._synthetic_id, # <-- Synthetic instrument identifier
)
self.strategy.submit_order(order)
Error handling
Considerable effort has been made to validate inputs, including the derivation formula for synthetic instruments. Despite this, caution is advised as invalid or erroneous inputs may lead to undefined behavior.
See the SyntheticInstrument
API reference
for a detailed understanding of input requirements and potential exceptions.