backtestingfx
A Rust-powered FX backtesting library for Python. Write your strategy in Python, let Rust handle the heavy lifting.
Inspired by backtesting.py but built specifically for forex — lot sizes, pip-based PnL, stop loss, take profit, and realistic account currency conversion.
pip install backtestingfx
Quick Start
import pandas as pd
from backtestingfx import Backtest, Strategy
class MyCrossStrategy(Strategy):
def next(self):
self.close_all()
self.buy(lot_size=0.1)
df = pd.read_csv("EURUSD_1H.csv")
bt = Backtest(df, MyCrossStrategy, cash=10000.0, spread=0.0001)
stats = bt.run()
print(stats)
--- Backtest Results ---
Initial Cash: 10000.00
Final Cash: 9823.50
Total Return: -1.77%
Trades: 248
Win Rate: 52.4%
Avg PnL: -0.71380
Best Trade: 84.20000
Worst Trade: -61.30000
Profit Factor: 0.94
Max Drawdown: 3.21%
Installation
pip install backtestingfx
Requires Python 3.9+.
Writing a Strategy
Inherit from Strategy and implement next(). It is called once per bar.
from backtestingfx import Backtest, Strategy
class MyStrategy(Strategy):
def init(self):
# called once before the loop starts
# self._bars contains all Bar objects if you need to pre-compute
pass
def next(self):
# self._bar — current bar (open, high, low, close, volume, timestamp)
# self._broker — the broker instance (advanced use)
if self._bar.close > 1.1000:
self.buy(lot_size=0.1, stop_loss=1.0950, take_profit=1.1100)
else:
self.close_all()
Strategy methods
| Method | Description |
|---|---|
self.buy(lot_size, stop_loss=None, take_profit=None) |
Open a long position |
self.sell(lot_size, stop_loss=None, take_profit=None) |
Open a short position |
self.close_all() |
Close all open positions |
self.close_position(id) |
Close a specific position by ID |
Bar fields
self._bar.open
self._bar.high
self._bar.low
self._bar.close
self._bar.volume
self._bar.timestamp # unix timestamp (int)
Backtest Parameters
Backtest(
df, # pandas DataFrame with OHLCV columns
StrategyClass,
cash=10000.0, # starting account balance in USD
commission=0.0, # commission per lot (e.g. 7.0 = $7/lot)
spread=0.0, # spread in price units (e.g. 0.0001 = 1 pip)
contract_size=100000.0, # standard FX lot size, don't change this
quote_to_account=1.0, # conversion rate from quote currency to USD
)
Trading non-USD pairs
By default quote_to_account=1.0 which is correct for USD-quoted pairs (EURUSD, GBPUSD).
For other pairs, pass the rate that converts the quote currency to USD:
| Pair | quote_to_account |
|---|---|
| EURUSD, GBPUSD | 1.0 (default) |
| EURGBP | GBPUSD rate (e.g. 1.27) |
| USDCAD, GBPCAD | CADUSD rate (e.g. 0.74) |
| USDJPY | JPYUSD rate (e.g. 0.0067) |
bt = Backtest(df, MyStrategy, cash=10000.0, spread=0.00015, quote_to_account=1.27)
Stats
| Field | Description |
|---|---|
initial_cash |
Starting balance |
final_cash |
Ending balance |
total_return_pct |
Total return as a percentage |
num_trades |
Number of completed trades |
num_wins |
Number of winning trades |
win_rate_pct |
Win rate as a percentage |
avg_pnl |
Average PnL per trade in USD |
best_trade |
Best single trade PnL in USD |
worst_trade |
Worst single trade PnL in USD |
profit_factor |
Gross profit / gross loss |
max_drawdown_pct |
Maximum drawdown as a percentage |
Data Format
Pass a pandas DataFrame with these columns:
open, high, low, close, volume
The index should be a DatetimeIndex, or include a timestamp column. Volume is optional (defaults to 0).
Why Rust?
The backtesting engine is written in Rust and compiled as a native Python extension via PyO3. This means the event loop, broker simulation, and stats computation run at native speed while your strategy stays in plain Python.
License
MIT — see LICENSE
Metadata
Release files for backtestingfx 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| backtestingfx-0.1.1.tar.gz | 13.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| backtestingfx-0.1.1-cp314-cp314-manylinux_2_34_x86_64.whl | CPython 3.14 | CPython 3.14 | Linux glibc 2.34+ x86-64 | Details |
Total release size: 276.5 kB
Release files / backtestingfx-0.1.1.tar.gz
| Download URL | backtestingfx-0.1.1.tar.gz |
|---|---|
| Size | 13.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dd4bfc1cae67bfaa55abbc047bbf94a13d1813ef1cf4c8941c5d69564ac7735c
|
|
BLAKE2b-256 checksum How to use checksums |
019c739784dc7f23828147cc4e0c4b9a193e0a42c7fa68da8e9819672bc7b3d1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.5
|
Release files / backtestingfx-0.1.1-cp314-cp314-manylinux_2_34_x86_64.whl
| Download URL | backtestingfx-0.1.1-cp314-cp314-manylinux_2_34_x86_64.whl |
|---|---|
| Size | 262.6 kB |
| Tags | CPython 3.14 Linux glibc 2.34+ x86-64 |
|
SHA-256 checksum How to use checksums |
9639bf821c3c9faa92a211601130786569d2aff1214d626d99399936dbfce47c
|
|
BLAKE2b-256 checksum How to use checksums |
4ae63f09193a3b63a6eb5ad1ef8b74c0ce93dd11ca006725cbecc03610b1f625
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.5
|
Security for backtestingfx 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.