A flexible backtesting framework for Python designed for quantitative trading strategy development and testing.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Advanced security types for specialized use cases including fixed income securities, hedge securities, and coupon-paying instruments. These securities provide custom valuation methods, cash flow handling, and specialized strategy behavior.
Securities designed for fixed income instruments with par value-based notional value calculation instead of market value.
class FixedIncomeSecurity(SecurityBase):
"""
Fixed income security with par value-based notional value.
Inherits from SecurityBase but overrides notional value calculation
to use par value (PAR * position) instead of market value.
Args:
name (str): Security name/ticker
multiplier (float): Security multiplier (default 1)
lazy_add (bool): Whether to add to parent lazily
"""
def update(self, date, data=None, inow=None):
"""Update security state with fixed income specific logic."""
...
class FixedIncomeStrategy(Strategy):
"""
Strategy optimized for fixed income with notional weighting.
Uses notional values instead of market values for weight calculations,
appropriate for fixed income portfolio management.
Args:
name (str): Strategy name
algos (list, optional): Algorithm list
children (list, optional): Child securities
"""
def __init__(self, name: str, algos=None, children=None): ...Securities that handle irregular coupons and cash disbursements, tracking both price movements and cash flows.
class CouponPayingSecurity(FixedIncomeSecurity):
"""
Security handling irregular coupons and cash disbursements.
Tracks coupon payments and holding costs in addition to price movements.
Inherits fixed income notional value calculation.
Args:
name (str): Security name/ticker
multiplier (float): Security multiplier (default 1)
fixed_income (bool): Whether to use fixed income logic (default True)
lazy_add (bool): Whether to add to parent lazily
"""
def __init__(self, name: str, multiplier=1, fixed_income=True, lazy_add=False): ...
def setup(self, universe, **kwargs):
"""Setup security with coupon data from universe."""
...
def update(self, date, data=None, inow=None):
"""Update security with coupon processing logic."""
...
# Properties
@property
def coupon(self): ...
@property
def coupons(self): ...
@property
def holding_cost(self): ...
@property
def holding_costs(self): ...Securities with zero notional value that don't contribute to strategy value calculations, useful for hedging instruments.
class HedgeSecurity(SecurityBase):
"""
Security with zero notional value.
Hedge securities don't count toward strategy value calculations,
making them useful for overlay strategies and hedging instruments.
Args:
name (str): Security name/ticker
multiplier (float): Security multiplier (default 1)
lazy_add (bool): Whether to add to parent lazily
"""
def update(self, date, data=None, inow=None):
"""Update security with hedge-specific logic (zero notional value)."""
...
class CouponPayingHedgeSecurity(CouponPayingSecurity):
"""
Coupon-paying security with zero notional value.
Combines coupon payment handling with hedge security behavior.
Useful for fixed income overlay strategies.
Args:
name (str): Security name/ticker
multiplier (float): Security multiplier (default 1)
fixed_income (bool): Whether to use fixed income logic (default True)
lazy_add (bool): Whether to add to parent lazily
"""
def update(self, date, data=None, inow=None):
"""Update security with coupon processing and zero notional value."""
...import bt
import pandas as pd
# Create fixed income securities
tlt = bt.FixedIncomeSecurity('TLT') # 20+ Year Treasury Bond ETF
iei = bt.FixedIncomeSecurity('IEI') # 3-7 Year Treasury Bond ETF
tip = bt.FixedIncomeSecurity('TIP') # Treasury Inflation-Protected Securities
# Create fixed income strategy
fi_strategy = bt.FixedIncomeStrategy('FixedIncome',
algos=[
bt.algos.RunMonthly(),
bt.algos.SelectAll(),
bt.algos.WeighEqually(),
bt.algos.Rebalance()
],
children=[tlt, iei, tip]
)
# Set notional value for portfolio
fi_strategy.algos.insert(0, bt.algos.SetNotional(10000000)) # $10M notional
# Run backtest
data = bt.get('TLT,IEI,TIP', start='2010-01-01', end='2020-01-01')
backtest = bt.Backtest(fi_strategy, data)
result = bt.run(backtest)# Create coupon-paying security with coupon data
coupon_data = pd.DataFrame({
'BOND_COUPON': [0.025, 0.025, 0.025, 0.025], # 2.5% quarterly coupons
'BOND_HOLDING_COST': [0.001, 0.001, 0.001, 0.001] # Holding costs
}, index=pd.date_range('2020-01-01', periods=4, freq='Q'))
bond = bt.CouponPayingSecurity('BOND')
# Setup with coupon data
universe_data = {'BOND': price_data, 'BOND_COUPON': coupon_data['BOND_COUPON']}
bond.setup(universe_data)
# Create strategy
strategy = bt.Strategy('CouponBond',
algos=[
bt.algos.RunDaily(),
bt.algos.SelectAll(),
bt.algos.WeighEqually(),
bt.algos.Rebalance()
],
children=[bond]
)# Create main portfolio securities
spy = bt.Security('SPY')
qqq = bt.Security('QQQ')
# Create hedge securities (don't count toward portfolio value)
vix_hedge = bt.HedgeSecurity('VXX') # VIX hedge
put_hedge = bt.HedgeSecurity('PUTS') # Put option hedge
# Main strategy with hedge overlay
main_strategy = bt.Strategy('MainStrategy',
algos=[
bt.algos.RunMonthly(),
bt.algos.SelectTypes(include_types=(bt.Security,)), # Only main securities
bt.algos.WeighEqually(),
bt.algos.Rebalance()
],
children=[spy, qqq, vix_hedge, put_hedge]
)
# Hedge strategy (runs separately)
hedge_strategy = bt.Strategy('HedgeStrategy',
algos=[
bt.algos.RunDaily(),
bt.algos.SelectTypes(include_types=(bt.HedgeSecurity,)), # Only hedge securities
bt.algos.WeighSpecified(VXX=0.05, PUTS=0.02), # Small hedge positions
bt.algos.Rebalance()
],
parent=main_strategy
)# Portfolio with different security types
securities = [
bt.Security('SPY'), # Regular equity
bt.FixedIncomeSecurity('TLT'), # Fixed income
bt.CouponPayingSecurity('CORP_BOND'), # Corporate bond with coupons
bt.HedgeSecurity('VIX_HEDGE'), # Hedge instrument
bt.CouponPayingHedgeSecurity('FI_HEDGE') # Fixed income hedge with coupons
]
# Strategy that handles all types
mixed_strategy = bt.Strategy('MixedPortfolio',
algos=[
bt.algos.RunMonthly(),
# Handle regular securities
bt.algos.SelectTypes(include_types=(bt.Security,)),
bt.algos.WeighSpecified(SPY=0.6),
bt.algos.Rebalance(),
# Handle fixed income
bt.algos.SelectTypes(include_types=(bt.FixedIncomeSecurity,)),
bt.algos.WeighSpecified(TLT=0.3, CORP_BOND=0.1),
bt.algos.Rebalance(),
# Handle hedges (separate allocation)
bt.algos.SelectTypes(include_types=(bt.HedgeSecurity,)),
bt.algos.WeighSpecified(VIX_HEDGE=0.02, FI_HEDGE=0.01),
bt.algos.Rebalance()
],
children=securities
)# Create duration-matched fixed income portfolio
short_duration = bt.FixedIncomeSecurity('SHY') # 1-3 Year
med_duration = bt.FixedIncomeSecurity('IEI') # 3-7 Year
long_duration = bt.FixedIncomeSecurity('TLT') # 20+ Year
# Duration targeting strategy
duration_strategy = bt.FixedIncomeStrategy('DurationTarget',
algos=[
# Set target notional
bt.algos.SetNotional(1000000),
# Monthly rebalancing
bt.algos.RunMonthly(),
bt.algos.SelectAll(),
# Dynamic duration weighting based on yield curve
bt.algos.WeighTarget(duration_weights), # External weight calculation
bt.algos.Rebalance()
],
children=[short_duration, med_duration, long_duration]
)
# Run with yield curve data
yield_data = bt.get('SHY,IEI,TLT', start='2010-01-01', end='2020-01-01')
backtest = bt.Backtest(duration_strategy, yield_data)
result = bt.run(backtest)
# Analyze results with fixed income focus
print("Portfolio Duration:", result.get_weights(0).mean())
result.plot_weights(0, title='Duration Allocation Over Time')notional_value = price * position (market value)notional_value = PAR * position (par value)notional_value = 0 (excluded from portfolio value)Install with Tessl CLI
npx tessl i tessl/pypi-bt