← Back to list

btc_basis_pair

BTC Spot/Perp Basis Pair Trading A delta-neutral pair trading strategy that trades the basis (premium/discount) between BTC spot and BTC perpetual futures.

Symbol: BTC | Exchange: Bitfinex

6/6
Profitable Years
+116.8%
Total Return
51.9%
Avg Win Rate
0.26
Avg Sharpe

Year-by-Year Results

Year Return Win Rate Trades Max DD Sharpe
2020 +6.9% 50.3% 314 51.7% 0.08
2021 +92.4% 52.8% 288 28.9% 1.01
2022 +3.1% 53.3% 60 25.1% 0.09
2023 +1.5% 54.5% 22 19.2% 0.05
2024 +6.9% 50.0% 142 26.8% 0.14
2025 +5.9% 50.8% 124 19.1% 0.16

Entry Logic

See strategy file

Exit Logic

See strategy file

Source Code

"""
Strategy: btc_basis_pair
========================
BTC Spot/Perp Basis Pair Trading

A delta-neutral pair trading strategy that trades the basis (premium/discount)
between BTC spot and BTC perpetual futures.

Entry Conditions:
- When perp trades at premium (>0.15%): long spot, short perp
- When perp trades at discount (<-0.15%): short spot, long perp

Exit Conditions:
- Basis normalizes (<0.05%)
- Time exit after 18 bars (3 days on 4h)

Performance: 6/6 years profitable | Total: +116.8%
2020: +6.9% | 50% WR | 314 trades
2021: +92.4% | 53% WR | 288 trades
2022: +3.1% | 53% WR | 60 trades
2023: +1.5% | 54% WR | 22 trades
2024: +6.9% | 50% WR | 142 trades
2025: +5.9% | 51% WR | 124 trades
"""
import sys
sys.path.insert(0, "/root/trade_rules")


def init_strategy():
    return {
        'name': 'btc_basis_pair',
        'subscriptions': [
            {'symbol': 'tBTCUSD', 'exchange': 'bitfinex', 'timeframe': '4h'},
            {'symbol': 'tBTCF0:USTF0', 'exchange': 'bitfinex', 'timeframe': '4h'},
        ],
        'parameters': {
            'basis_threshold': 0.15,
            'exit_threshold': 0.05,
            'hold_bars': 18,
        }
    }


def process_time_step(ctx):
    spot_key = ('tBTCUSD', 'bitfinex')
    perp_key = ('tBTCF0:USTF0', 'bitfinex')

    spot_bars = ctx['bars'].get(spot_key, [])
    perp_bars = ctx['bars'].get(perp_key, [])
    i = ctx['i']
    positions = ctx['positions']
    params = ctx['parameters']

    if not spot_bars or not perp_bars or i < 50:
        return []

    # Calculate basis (perp premium/discount vs spot)
    spot_price = spot_bars[i].close
    perp_price = perp_bars[i].close
    basis_pct = (perp_price - spot_price) / spot_price * 100

    actions = []
    has_spot = spot_key in positions
    has_perp = perp_key in positions

    if not has_spot and not has_perp:
        # Entry: significant basis
        if basis_pct > params['basis_threshold']:
            # Perp at premium - long spot, short perp (collect premium)
            actions.append({
                'action': 'open_long',
                'symbol': 'tBTCUSD',
                'exchange': 'bitfinex',
                'size': 1.0,
            })
            actions.append({
                'action': 'open_short',
                'symbol': 'tBTCF0:USTF0',
                'exchange': 'bitfinex',
                'size': 1.0,
            })
        elif basis_pct < -params['basis_threshold']:
            # Perp at discount - short spot, long perp
            actions.append({
                'action': 'open_short',
                'symbol': 'tBTCUSD',
                'exchange': 'bitfinex',
                'size': 1.0,
            })
            actions.append({
                'action': 'open_long',
                'symbol': 'tBTCF0:USTF0',
                'exchange': 'bitfinex',
                'size': 1.0,
            })

    elif has_spot and has_perp:
        # Exit: basis normalized or time exit
        spot_pos = positions[spot_key]
        bars_held = i - spot_pos.entry_bar

        should_exit = bars_held >= params['hold_bars'] or abs(basis_pct) < params['exit_threshold']

        if should_exit:
            perp_pos = positions[perp_key]

            if spot_pos.side == 'long':
                actions.append({
                    'action': 'close_long',
                    'symbol': 'tBTCUSD',
                    'exchange': 'bitfinex',
                })
            else:
                actions.append({
                    'action': 'close_short',
                    'symbol': 'tBTCUSD',
                    'exchange': 'bitfinex',
                })

            if perp_pos.side == 'long':
                actions.append({
                    'action': 'close_long',
                    'symbol': 'tBTCF0:USTF0',
                    'exchange': 'bitfinex',
                })
            else:
                actions.append({
                    'action': 'close_short',
                    'symbol': 'tBTCF0:USTF0',
                    'exchange': 'bitfinex',
                })

    return actions


if __name__ == '__main__':
    from strategy import backtest_strategy
    results, profitable = backtest_strategy(init_strategy, process_time_step, verbose=True)
    total = sum(r['return'] for r in results.values())
    print(f"\nBTC Basis Pair: {profitable}/6 years profitable, {total:+.1f}%")