Generate code in 3 minutes
Enter your idea and AI generates runnable code.
Here is a complete Python script for a Spot-Future Crypto Arbitrage Bot using the QuantConnect Lean API.
This algorithm implements a Basis Trading (Convergence) strategy.
entry_threshold (e.g., 1%), the bot buys Spot and shorts the Future.entry_threshold, the bot shorts Spot and buys the Future.exit_threshold), the bot liquidates all positions to capture the profit.set_holdings to maintain a delta-neutral portfolio (50% capital in Spot, 50% in Futures).# region imports
from AlgorithmImports import *
# endregion
class CryptoSpotFutureArbitrage(QCAlgorithm):
def initialize(self):
# 1. Set Setup and Strategy Parameters
self.set_start_date(2023, 1, 1)
self.set_end_date(2024, 1, 1)
self.set_cash(100000) # Set Strategy Cash (USDT)
# 2. Brokerage Setup
# We use Binance as it supports both Spot and Futures well in LEAN
self.set_brokerage_model(BrokerageName.BINANCE, AccountType.MARGIN)
# 3. Add Assets
# Add Spot BTCUSDT
self.spot_symbol = self.add_crypto("BTCUSDT", Resolution.MINUTE).symbol
# Add Future BTCUSDT (Perpetual or Front Month)
# LEAN automatically handles the continuous contract mapping if configured
future = self.add_crypto_future("BTCUSDT", Resolution.MINUTE)
self.future_symbol = future.symbol
# 4. Strategy Variables
self.entry_threshold = 0.01 # Enter when spread is 1%
self.exit_threshold = 0.001 # Exit when spread is 0.1%
self.spread_window = RollingWindow[float](10) # For logging/analysis
# Warm up period to ensure data is ready
self.set_warm_up(timedelta(minutes=5))
def on_data(self, data: Slice):
# Ensure we have data for both symbols
if not (data.contains_key(self.spot_symbol) and data.contains_key(self.future_symbol)):
return
# Get current prices
spot_price = data[self.spot_symbol].close
future_price = data[self.future_symbol].close
# Calculate Spread: (Future - Spot) / Spot
# Positive Spread = Contango (Future is more expensive)
# Negative Spread = Backwardation (Spot is more expensive)
spread = (future_price - spot_price) / spot_price
self.spread_window.add(spread)
# --- Execution Logic ---
# Check if we are currently invested
if not self.portfolio.invested:
# ENTRY: Contango Arbitrage (Buy Spot, Sell Future)
if spread > self.entry_threshold:
self.debug(f"Entry Long Arb | Spread: {spread:.4f} | Spot: {spot_price} | Fut: {future_price}")
# Allocate 50% to Spot (Long) and 50% to Future (Short)
self.set_holdings(self.spot_symbol, 0.45)
self.set_holdings(self.future_symbol, -0.45)
# ENTRY: Backwardation Arbitrage (Sell Spot, Buy Future)
# Note: Requires margin to short Spot
elif spread < -self.entry_threshold:
self.debug(f"Entry Short Arb | Spread: {spread:.4f} | Spot: {spot_price} | Fut: {future_price}")
# Allocate 50% to Spot (Short) and 50% to Future (Long)
self.set_holdings(self.spot_symbol, -0.45)
self.set_holdings(self.future_symbol, 0.45)
else:
# EXIT: Convergence
# We check the absolute value of the spread. If it is near zero, we exit.
if abs(spread) < self.exit_threshold:
self.debug(f"Exit Convergence | Spread: {spread:.4f}")
self.liquidate()
def on_order_event(self, order_event: OrderEvent):
# Log filled orders for debugging
if order_event.status == OrderStatus.FILLED:
self.log(f"Order Filled: {order_event.symbol} - {order_event.direction} @ {order_event.fill_price}")
def on_end_of_algorithm(self):
self.log(f"Final Portfolio Value: {self.portfolio.total_portfolio_value}")
Brokerage Model (BrokerageName.BINANCE):
AccountType.MARGIN is selected to allow shorting (selling assets you don't own), which is required for the futures leg of the trade.Data Handling (on_data):
data.contains_key to ensure both price feeds are available before calculating the spread.Position Sizing (set_holdings):
0.45 (45%) instead of 0.50 (50%) to leave a small buffer for fees and price fluctuations. This prevents the algorithm from being rejected due to "Insufficient Buying Power."Directionality:
Q: Why do we use set_holdings instead of market_order?
A: set_holdings automatically calculates the quantity needed to reach a specific percentage of your portfolio value. In arbitrage, balancing the dollar value of the Spot and Future legs is crucial to remain "Delta Neutral" (immune to market direction). set_holdings handles this math for you.
Q: How does this bot handle funding rates?
A: This specific script focuses on price convergence. However, in a real Perpetual Futures trade, you would pay or receive a funding rate every 8 hours. In QuantConnect, funding rates are automatically deducted/added to your cash balance by the Lean engine when using supported brokerages like Binance or Bitfinex.
Q: Can I trade this on other pairs like ETHUSDT?
A: Yes. Simply change the ticker in the initialize method:
self.spot_symbol = self.add_crypto("ETHUSDT", Resolution.MINUTE).symbol
self.future_symbol = self.add_crypto_future("ETHUSDT", Resolution.MINUTE).symbol