Backtest: FX bar data
Tutorial for PoseiTrader a high-performance algorithmic trading platform and event driven backtester.
We are currently working on this tutorial.
Overview
This tutorial runs through how to set up a
BacktestEngine
(low-level API) for a
single 'one-shot' backtest run using FX
bar data.
Prerequisites
- Python 3.11+ installed
-
JupyterLab
or similar installed (
pip install -U jupyterlab
) -
PoseiTrader
latest release installed (
pip install -U posei_trader
)
Imports
We'll start with all of our imports for the remainder of this tutorial.
from decimal import Decimal
from posei_trader.backtest.engine import BacktestEngine
from posei_trader.backtest.engine import BacktestEngineConfig
from posei_trader.backtest.models import FillModel
from posei_trader.backtest.modules import FXRolloverInterestConfig
from posei_trader.backtest.modules import FXRolloverInterestModule
from posei_trader.config import LoggingConfig
from posei_trader.config import RiskEngineConfig
from posei_trader.examples.strategies.ema_cross import EMACross
from posei_trader.examples.strategies.ema_cross import EMACrossConfig
from posei_trader.model import BarType
from posei_trader.model import Money
from posei_trader.model import Venue
from posei_trader.model.currencies import JPY
from posei_trader.model.currencies import USD
from posei_trader.model.enums import AccountType
from posei_trader.model.enums import OmsType
from posei_trader.persistence.wranglers import QuoteTickDataWrangler
from posei_trader.test_kit.providers import TestDataProvider
from posei_trader.test_kit.providers import TestInstrumentProvider
Set up backtest engine
# Initialize a backtest configuration
config = BacktestEngineConfig(
trader_id="BACKTESTER-001",
logging=LoggingConfig(log_level="ERROR"),
risk_engine=RiskEngineConfig(
bypass=True, # Example of bypassing pre-trade risk checks for backtests
),
)
# Build backtest engine
engine = BacktestEngine(config=config)
Add simulation module
We can optionally plug in a module to simulate rollover interest. The data is available from pre-packaged test data.
provider = TestDataProvider()
interest_rate_data = provider.read_csv("short-term-interest.csv")
config = FXRolloverInterestConfig(interest_rate_data)
fx_rollover_interest = FXRolloverInterestModule(config=config)
Add fill model
For this backtest we'll use a simple probabilistic fill model.
fill_model = FillModel(
prob_fill_on_limit=0.2,
prob_fill_on_stop=0.95,
prob_slippage=0.5,
random_seed=42,
)
Add venue
For this backtest we just need a single trading venue which will be a similated FX ECN.
SIM = Venue("SIM")
engine.add_venue(
venue=SIM,
oms_type=OmsType.HEDGING, # Venue will generate position IDs
account_type=AccountType.MARGIN,
base_currency=None, # Multi-currency account
starting_balances=[Money(1_000_000, USD), Money(10_000_000, JPY)],
fill_model=fill_model,
modules=[fx_rollover_interest],
)
Add instruments and data
Now we can add instruments and data. For this
backtest we'll pre-process bid and ask side
bar data into quotes using a
QuoteTickDataWrangler
.
# Add instruments
USDJPY_SIM = TestInstrumentProvider.default_fx_ccy("USD/JPY", SIM)
engine.add_instrument(USDJPY_SIM)
# Add data
wrangler = QuoteTickDataWrangler(instrument=USDJPY_SIM)
ticks = wrangler.process_bar_data(
bid_data=provider.read_csv_bars("fxcm/usdjpy-m1-bid-2013.csv"),
ask_data=provider.read_csv_bars("fxcm/usdjpy-m1-ask-2013.csv"),
)
engine.add_data(ticks)
Configure strategy
Next we'll configure and initialize a simple
EMACross
strategy we'll use for
the backtest.
# Configure your strategy
config = EMACrossConfig(
instrument_id=USDJPY_SIM.id,
bar_type=BarType.from_str("USD/JPY.SIM-5-MINUTE-BID-INTERNAL"),
fast_ema_period=10,
slow_ema_period=20,
trade_size=Decimal(1_000_000),
)
# Instantiate and add your strategy
strategy = EMACross(config=config)
engine.add_strategy(strategy=strategy)
Run backtest
We now have everything required to run the backtest. Once the engine has completed running through all the data, a post-analysis report will be logged.
engine.run()
Generating reports
Additionally, we can produce various reports to further analyze the backtest result.
engine.trader.generate_account_report(SIM)
engine.trader.generate_order_fills_report()
engine.trader.generate_positions_report()