mirror of
https://github.com/manifoldbt/manifoldbt.git
synced 2026-08-24 14:38:04 +00:00
- Cross-exchange backtesting (Pro) - Dict universe format (provider-based symbol resolution) - Exogenous data support (register_exo + exo() expressions) - Provider-based data layout (binance/1h/TICKER.arrow) - Preload fix for provider layout - Exo column resampling for multi-resolution - Pro gate for cross-exchange (clean exit) - ATR/ADX rolling SMA fix - Precise mode hybrid fills
93 lines
3.2 KiB
Python
93 lines
3.2 KiB
Python
"""Example 15: Cross-Exchange — Signal Binance, Execution dYdX.
|
|
|
|
Simple RSI mean-reversion:
|
|
- RSI computed on Binance BTC perp data
|
|
- Trades executed at dYdX BTC-USD prices
|
|
- Both loaded via universe dict — no special config needed
|
|
|
|
Prerequisite:
|
|
Binance perp data (bars_1m/201.arrow) + dYdX data (dydx/1h/BTC-USD.arrow)
|
|
"""
|
|
|
|
import time
|
|
import manifoldbt as mbt
|
|
from manifoldbt.indicators import rsi, ema
|
|
from manifoldbt.expr import col, symbol_ref, lit, when
|
|
from manifoldbt.helpers import time_range, Interval, Slippage
|
|
|
|
# =============================================================================
|
|
# Signal — RSI + EMA from Binance BTC, applied to dYdX BTC
|
|
# All SymbolRef expressions must be named signals (for pass 2b resolution)
|
|
# =============================================================================
|
|
bn_btc_close = symbol_ref("binance:BTC-USDT:perp", "close")
|
|
bn_btc_rsi = rsi(bn_btc_close, 14)
|
|
bn_ema_fast = ema(bn_btc_close, 15)
|
|
bn_ema_slow = ema(bn_btc_close, 30)
|
|
trend_up = bn_ema_fast > bn_ema_slow
|
|
|
|
# Size references named signals only (no inline SymbolRef)
|
|
signal = when(
|
|
(col("trend") > lit(0.5)) & (col("bn_rsi") > lit(70.0)), 1.0,
|
|
when((col("trend") < lit(0.5)) & (col("bn_rsi") < lit(30.0)), -1.0,
|
|
0.0),
|
|
)
|
|
|
|
# =============================================================================
|
|
# Strategy
|
|
# =============================================================================
|
|
strategy = (
|
|
mbt.Strategy.create("cross_exchange_rsi")
|
|
.signal("bn_rsi", bn_btc_rsi)
|
|
.signal("trend", when(trend_up, 1.0, 0.0))
|
|
.size(signal)
|
|
.describe("Signal: Binance RSI | Execution: dYdX")
|
|
)
|
|
|
|
# =============================================================================
|
|
# Config — everything in universe
|
|
# =============================================================================
|
|
START, END = time_range("2024-02-01", "2026-03-01")
|
|
|
|
config = mbt.BacktestConfig(
|
|
universe={
|
|
"dydx": ["BTC-USD:perp"], # execution (fills here)
|
|
"binance": ["BTC-USDT:perp"], # signal source (via symbol_ref)
|
|
},
|
|
time_range_start=START,
|
|
time_range_end=END,
|
|
bar_interval=Interval.hours(6),
|
|
initial_capital=10_000,
|
|
warmup_bars=30,
|
|
execution=mbt.ExecutionConfig(signal_delay=1),
|
|
fees=mbt.FeeConfig(maker_fee_bps=1.0, taker_fee_bps=2.5),
|
|
slippage=Slippage.fixed_bps(2),
|
|
)
|
|
|
|
# =============================================================================
|
|
# Run
|
|
# =============================================================================
|
|
if __name__ == "__main__":
|
|
import os
|
|
root = os.path.dirname(os.path.abspath(__file__))
|
|
data_root = os.path.abspath(os.path.join(root, "..", "data"))
|
|
meta_db = os.path.join(root, "..", "metadata", "metadata.sqlite")
|
|
|
|
store = mbt.DataStore(
|
|
data_root=data_root,
|
|
metadata_db=meta_db,
|
|
arrow_dir=os.path.join(data_root, "mega"),
|
|
)
|
|
|
|
print("Running: cross_exchange_rsi")
|
|
print(" Signal: binance:BTC-USDT:perp (RSI + EMA)")
|
|
print(" Execution: dydx:BTC-USD:perp")
|
|
print()
|
|
|
|
t0 = time.perf_counter()
|
|
result = mbt.run(strategy, config, store)
|
|
elapsed = time.perf_counter() - t0
|
|
|
|
print(result.summary())
|
|
print(f"\nElapsed: {elapsed:.3f}s")
|
|
result.plot_equity(show=True)
|