Skip to content

Commit 41d2d2f

Browse files
committed
Convert low level API backtest examples to additional high level API examples
1 parent d577b6d commit 41d2d2f

File tree

29 files changed

+1664
-52
lines changed

29 files changed

+1664
-52
lines changed

examples/backtest/example_01_load_bars_from_custom_csv/run_example.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import pandas as pd
2020
from strategy import DemoStrategy
21+
from strategy import DemoStrategyConfig
2122

2223
from nautilus_trader.backtest.engine import BacktestEngine
2324
from nautilus_trader.config import BacktestEngineConfig
@@ -101,7 +102,8 @@
101102
# ==========================================================================================
102103

103104
# Step 5: Create strategy and add it to the engine
104-
strategy = DemoStrategy(primary_bar_type=EURUSD_FUTURES_1MIN_BARTYPE)
105+
strategy_config = DemoStrategyConfig(primary_bar_type=EURUSD_FUTURES_1MIN_BARTYPE)
106+
strategy = DemoStrategy(config=strategy_config)
105107
engine.add_strategy(strategy)
106108

107109
# Step 6: Run engine = Run backtest
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#!/usr/bin/env python3
2+
# -------------------------------------------------------------------------------------------------
3+
# Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
4+
# https://nautechsystems.io
5+
#
6+
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
7+
# You may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
# -------------------------------------------------------------------------------------------------
16+
17+
import pandas as pd
18+
from strategy import DemoStrategy
19+
from strategy import DemoStrategyConfig
20+
21+
from nautilus_trader.backtest.config import BacktestDataConfig
22+
from nautilus_trader.backtest.config import BacktestEngineConfig
23+
from nautilus_trader.backtest.config import BacktestRunConfig
24+
from nautilus_trader.backtest.config import BacktestVenueConfig
25+
from nautilus_trader.backtest.node import BacktestNode
26+
from nautilus_trader.common.config import LoggingConfig
27+
from nautilus_trader.config import ImportableStrategyConfig
28+
from nautilus_trader.model.data import Bar
29+
from nautilus_trader.model.data import BarType
30+
from nautilus_trader.model.identifiers import TraderId
31+
from nautilus_trader.persistence.catalog.parquet import ParquetDataCatalog
32+
from nautilus_trader.persistence.wranglers import BarDataWrangler
33+
from nautilus_trader.test_kit.providers import TestInstrumentProvider
34+
35+
36+
if __name__ == "__main__":
37+
# ==========================================================================================
38+
# POINT OF FOCUS: Loading bars from CSV into data catalog for BacktestNode
39+
# ------------------------------------------------------------------------------------------
40+
41+
# Step 1: Create instrument definition
42+
EURUSD_FUTURES_INSTRUMENT = TestInstrumentProvider.eurusd_future(
43+
expiry_year=2024,
44+
expiry_month=3,
45+
venue_name="XCME",
46+
)
47+
48+
# Step 2: Load bar data from CSV file -> into pandas DataFrame
49+
csv_file_path = r"6EH4.XCME_1min_bars.csv"
50+
df = pd.read_csv(csv_file_path, sep=";", decimal=".", header=0, index_col=False)
51+
52+
# Step 3: Restructure DataFrame into required structure for BarDataWrangler
53+
# - 5 columns: 'open', 'high', 'low', 'close', 'volume' (volume is optional)
54+
# - 'timestamp' as index
55+
56+
# Change order of columns
57+
df = df.reindex(columns=["timestamp_utc", "open", "high", "low", "close", "volume"])
58+
# Convert string timestamps into datetime
59+
df["timestamp_utc"] = pd.to_datetime(df["timestamp_utc"], format="%Y-%m-%d %H:%M:%S")
60+
# Rename column to required name
61+
df = df.rename(columns={"timestamp_utc": "timestamp"})
62+
# Set column `timestamp` as index
63+
df = df.set_index("timestamp")
64+
65+
# Step 4: Define type of loaded bars
66+
EURUSD_FUTURES_1MIN_BARTYPE = BarType.from_str(
67+
f"{EURUSD_FUTURES_INSTRUMENT.id}-1-MINUTE-LAST-EXTERNAL",
68+
)
69+
70+
# Step 5: Convert DataFrame rows into Bar objects using BarDataWrangler
71+
wrangler = BarDataWrangler(EURUSD_FUTURES_1MIN_BARTYPE, EURUSD_FUTURES_INSTRUMENT)
72+
eurusd_1min_bars_list: list[Bar] = wrangler.process(df)
73+
74+
# Step 6: Create data catalog and write data to it
75+
catalog_path = "./data_catalog_example_01"
76+
data_catalog = ParquetDataCatalog(catalog_path)
77+
78+
# Write instrument and bar data to catalog
79+
data_catalog.write_data([EURUSD_FUTURES_INSTRUMENT]) # Store instrument definition
80+
data_catalog.write_data(eurusd_1min_bars_list) # Store bar data
81+
82+
# ------------------------------------------------------------------------------------------
83+
# END OF POINT OF FOCUS
84+
# ==========================================================================================
85+
86+
# Step 7: Configure BacktestNode with high-level API
87+
88+
# Configure strategy using ImportableStrategyConfig
89+
strategies = [
90+
ImportableStrategyConfig(
91+
strategy_path=DemoStrategy.fully_qualified_name(),
92+
config_path=DemoStrategyConfig.fully_qualified_name(),
93+
config={
94+
"primary_bar_type": str(EURUSD_FUTURES_1MIN_BARTYPE),
95+
},
96+
),
97+
]
98+
99+
# Configure logging
100+
logging = LoggingConfig(
101+
log_level="DEBUG", # Set DEBUG log level for console to see loaded bars in logs
102+
)
103+
104+
# Configure backtest engine
105+
engine_config = BacktestEngineConfig(
106+
trader_id=TraderId("BACKTEST_TRADER-001"),
107+
logging=logging,
108+
strategies=strategies,
109+
)
110+
111+
# Configure venue
112+
venues = [
113+
BacktestVenueConfig(
114+
name="XCME",
115+
oms_type="NETTING", # Order Management System type
116+
account_type="MARGIN", # Type of trading account
117+
starting_balances=["1_000_000 USD"], # Initial account balance
118+
base_currency="USD", # Base currency for account
119+
default_leverage=1.0, # No leverage used for account
120+
),
121+
]
122+
123+
# Configure data source from catalog
124+
data = [
125+
BacktestDataConfig(
126+
catalog_path=catalog_path,
127+
data_cls=Bar,
128+
instrument_id=EURUSD_FUTURES_INSTRUMENT.id,
129+
),
130+
]
131+
132+
# Create BacktestRunConfig
133+
run_config = BacktestRunConfig(
134+
engine=engine_config,
135+
venues=venues,
136+
data=data,
137+
)
138+
139+
# Step 8: Create and run BacktestNode
140+
node = BacktestNode(configs=[run_config])
141+
142+
# Run the backtest
143+
results = node.run()
144+
145+
# Step 9: Clean up resources
146+
node.dispose()

examples/backtest/example_01_load_bars_from_custom_csv/strategy.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,21 @@
1616
import datetime as dt
1717

1818
from nautilus_trader.common.enums import LogColor
19+
from nautilus_trader.config import StrategyConfig
1920
from nautilus_trader.model.data import Bar
2021
from nautilus_trader.model.data import BarType
2122
from nautilus_trader.trading.strategy import Strategy
2223

2324

25+
class DemoStrategyConfig(StrategyConfig, frozen=True):
26+
primary_bar_type: BarType
27+
28+
2429
# This is a trivial demo strategy that simply counts all processed 1-minute bars.
2530
class DemoStrategy(Strategy):
26-
def __init__(self, primary_bar_type: BarType):
27-
super().__init__()
28-
self.primary_bar_type = primary_bar_type
31+
def __init__(self, config: DemoStrategyConfig):
32+
super().__init__(config)
33+
self.primary_bar_type = self.config.primary_bar_type
2934
self.bars_processed = 0
3035
self.start_time = None
3136
self.end_time = None

examples/backtest/example_02_use_clock_timer/run_example.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from decimal import Decimal
1818

1919
from strategy import SimpleTimerStrategy
20+
from strategy import SimpleTimerStrategyConfig
2021

2122
from examples.utils.data_provider import prepare_demo_data_eurusd_futures_1min
2223
from nautilus_trader.backtest.engine import BacktestEngine
@@ -89,7 +90,8 @@
8990
# ----------------------------------------------------------------------------------
9091

9192
# Create and register the timer strategy
92-
strategy = SimpleTimerStrategy(primary_bar_type=eurusd_1min_bartype)
93+
strategy_config = SimpleTimerStrategyConfig(primary_bar_type=eurusd_1min_bartype)
94+
strategy = SimpleTimerStrategy(config=strategy_config)
9395
engine.add_strategy(strategy)
9496

9597
# Execute the backtest
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#!/usr/bin/env python3
2+
# -------------------------------------------------------------------------------------------------
3+
# Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
4+
# https://nautechsystems.io
5+
#
6+
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
7+
# You may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
# -------------------------------------------------------------------------------------------------
16+
17+
from strategy import SimpleTimerStrategy
18+
from strategy import SimpleTimerStrategyConfig
19+
20+
from examples.utils.data_provider import prepare_demo_data_eurusd_futures_1min
21+
from nautilus_trader.backtest.config import BacktestDataConfig
22+
from nautilus_trader.backtest.config import BacktestEngineConfig
23+
from nautilus_trader.backtest.config import BacktestRunConfig
24+
from nautilus_trader.backtest.config import BacktestVenueConfig
25+
from nautilus_trader.backtest.node import BacktestNode
26+
from nautilus_trader.common.config import LoggingConfig
27+
from nautilus_trader.config import ImportableStrategyConfig
28+
from nautilus_trader.model.data import Bar
29+
from nautilus_trader.model.identifiers import TraderId
30+
from nautilus_trader.persistence.catalog.parquet import ParquetDataCatalog
31+
32+
33+
if __name__ == "__main__":
34+
"""
35+
This example demonstrates how to use timer functionality in a trading strategy with
36+
the high-level BacktestNode API.
37+
38+
The strategy sets up a timer that triggers every 3 minutes and processes bar data.
39+
This is useful for implementing periodic actions in your trading strategies.
40+
41+
"""
42+
43+
# ----------------------------------------------------------------------------------
44+
# 1. Prepare market data and create data catalog
45+
# ----------------------------------------------------------------------------------
46+
47+
prepared_data: dict = prepare_demo_data_eurusd_futures_1min()
48+
venue_name: str = prepared_data["venue_name"]
49+
eurusd_instrument = prepared_data["instrument"]
50+
eurusd_1min_bartype = prepared_data["bar_type"]
51+
eurusd_1min_bars = prepared_data["bars_list"]
52+
53+
# Create data catalog and write data to it
54+
catalog_path = "./data_catalog_example_02"
55+
data_catalog = ParquetDataCatalog(catalog_path)
56+
57+
# Write instrument and bar data to catalog
58+
data_catalog.write_data([eurusd_instrument]) # Store instrument definition
59+
data_catalog.write_data(eurusd_1min_bars) # Store bar data
60+
61+
# ----------------------------------------------------------------------------------
62+
# 2. Configure BacktestNode with high-level API
63+
# ----------------------------------------------------------------------------------
64+
65+
# Configure strategy using ImportableStrategyConfig
66+
strategies = [
67+
ImportableStrategyConfig(
68+
strategy_path=SimpleTimerStrategy.fully_qualified_name(),
69+
config_path=SimpleTimerStrategyConfig.fully_qualified_name(),
70+
config={
71+
"primary_bar_type": str(eurusd_1min_bartype),
72+
},
73+
),
74+
]
75+
76+
# Configure logging
77+
logging = LoggingConfig(
78+
log_level="DEBUG", # Set to DEBUG to see detailed timer and bar processing logs
79+
)
80+
81+
# Configure backtest engine
82+
engine_config = BacktestEngineConfig(
83+
trader_id=TraderId("BACKTEST-TIMER-001"), # Unique identifier for this backtest
84+
logging=logging,
85+
strategies=strategies,
86+
)
87+
88+
# Configure venue
89+
venues = [
90+
BacktestVenueConfig(
91+
name=venue_name,
92+
oms_type="NETTING", # Use a netting order management system
93+
account_type="MARGIN", # Use a margin trading account
94+
starting_balances=["1_000_000 USD"], # Set initial capital
95+
base_currency="USD", # Account currency
96+
default_leverage=1.0, # No leverage (1:1)
97+
),
98+
]
99+
100+
# Configure data source from catalog
101+
data = [
102+
BacktestDataConfig(
103+
catalog_path=catalog_path,
104+
data_cls=Bar,
105+
instrument_id=eurusd_instrument.id,
106+
),
107+
]
108+
109+
# Create BacktestRunConfig
110+
run_config = BacktestRunConfig(
111+
engine=engine_config,
112+
venues=venues,
113+
data=data,
114+
)
115+
116+
# ----------------------------------------------------------------------------------
117+
# 3. Create and run BacktestNode
118+
# ----------------------------------------------------------------------------------
119+
120+
# Create the backtest node
121+
node = BacktestNode(configs=[run_config])
122+
123+
# Execute the backtest
124+
results = node.run()
125+
126+
# Clean up resources
127+
node.dispose()

examples/backtest/example_02_use_clock_timer/strategy.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,24 @@
1919

2020
from nautilus_trader.common.enums import LogColor
2121
from nautilus_trader.common.events import TimeEvent
22+
from nautilus_trader.config import StrategyConfig
2223
from nautilus_trader.core.datetime import unix_nanos_to_dt
2324
from nautilus_trader.model.data import Bar
2425
from nautilus_trader.model.data import BarType
2526
from nautilus_trader.trading.strategy import Strategy
2627

2728

29+
class SimpleTimerStrategyConfig(StrategyConfig, frozen=True):
30+
primary_bar_type: BarType
31+
32+
2833
class SimpleTimerStrategy(Strategy):
2934
TIMER_NAME = "every_3_minutes"
3035
TIMER_INTERVAL = pd.Timedelta(minutes=3)
3136

32-
def __init__(self, primary_bar_type: BarType):
33-
super().__init__()
34-
self.primary_bar_type = primary_bar_type
37+
def __init__(self, config: SimpleTimerStrategyConfig):
38+
super().__init__(config)
39+
self.primary_bar_type = self.config.primary_bar_type
3540
self.bars_processed = 0
3641
self.start_time = None
3742
self.end_time = None

examples/backtest/example_03_bar_aggregation/run_example.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from decimal import Decimal
1818

1919
from strategy import DemoStrategy
20+
from strategy import DemoStrategyConfig
2021

2122
from examples.utils.data_provider import prepare_demo_data_eurusd_futures_1min
2223
from nautilus_trader.backtest.engine import BacktestEngine
@@ -80,7 +81,8 @@
8081
# ----------------------------------------------------------------------------------
8182

8283
# Create and register the bar aggregation strategy
83-
strategy = DemoStrategy(bar_type_1min=eurusd_1min_bartype)
84+
strategy_config = DemoStrategyConfig(bar_type_1min=eurusd_1min_bartype)
85+
strategy = DemoStrategy(config=strategy_config)
8486
engine.add_strategy(strategy)
8587

8688
# Execute the backtest

0 commit comments

Comments
 (0)