---
title: "Crypto Trades"
description: "All fiat and Crypto currencies are individual assets. When you buy a pair like BTCUSD, you trade USD for BTC."
canonical: https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/crypto-trades
markdown: https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/crypto-trades.md
language: python
source: https://github.com/QuantConnect/Documentation/tree/master/03%20Writing%20Algorithms/22%20Trading%20and%20Orders/07%20Crypto%20Trades
modified: 2026-10-30
index: https://www.quantconnect.com/llms.txt
---

# Crypto Trades

[Documentation](https://www.quantconnect.com/docs/v2.md) > [Writing Algorithms](https://www.quantconnect.com/docs/v2/writing-algorithms.md) > Trading and Orders > Crypto Trades

> Code examples are shown in Python. Append `?lang=csharp` to this URL for the C# version of this page.

## Introduction

All fiat and Crypto currencies are individual assets. When you buy a pair like BTCUSDT, you trade USDT for BTC. In this case, LEAN removes some USDT from your portfolio cash book and adds some BTC. The virtual pair BTCUSDT represents your position in the trade, but the virtual pair doesn't actually exist. It simply represents an open trade.

## Place Trades

When you place Crypto trades, don't use the `calculate_order_quantity` or `set_holdings` methods. Instead, calculate the order quantity based on the currency amounts in your [cash book](https://www.quantconnect.com/docs/v2/writing-algorithms/portfolio/cashbook.md) and place manual orders.

The following code snippet demonstrates how to allocate 90% of your portfolio to BTC.

```python
def on_data(self, data: Slice):
    self.set_crypto_holdings(self._symbol, .9)

def set_crypto_holdings(self, symbol, percentage):
    crypto = self.securities[symbol]
    base_currency = crypto.base_currency

    # Calculate the target quantity in the base currency
    target_quantity = percentage * (self.portfolio.total_portfolio_value - self.settings.free_portfolio_value) / base_currency.conversion_rate
    quantity = target_quantity - base_currency.amount

    # Round down to observe the lot size
    lot_size = crypto.symbol_properties.lot_size
    quantity = round(quantity / lot_size) * lot_size

    if self.is_valid_order_size(crypto, quantity):
        self.market_order(symbol, quantity)

# Brokerages have different order size rules
# Binance considers the minimum volume (price x quantity):
def is_valid_order_size(self, crypto, quantity):
    return abs(crypto.price * quantity) > crypto.symbol_properties.minimum_order_size
```

The preceding example doesn't take into account order fees. You can add a 0.1% buffer to accommodate it.

The following example demonstrates how to form an equal-weighted Crypto portfolio and stay within the [cash buffer](https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/position-sizing.md#06-Buying-Power-Buffer).

```python
def on_data(self, data: Slice):
    percentage = (1 - self.settings.free_portfolio_value_percentage) / len(self.symbols);
    for symbol in self.symbols:
        self.set_crypto_holdings(symbol, percentage)
```

You can replace the `settings.free_portfolio_value_percentage` for a class variable (e.g. `self.cash_buffer`).

When you place Crypto trades, ensure you have a sufficient balance of the base or quote currency before each trade. If you hold multiple assets and you want to put all of your capital into BTCUSDT, you need to first convert all your non-BTC assets into USDT and then purchase BTCUSDT.

By default, the account currency is USD and your starting cash is $100,000. Some Crypto exchanges don't support fiat currencies, which means you can't convert the $100,000 USD to the pair's [quote currency](https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/key-concepts.md#05-Quote-Currency). In this case, [set your account currency](https://www.quantconnect.com/docs/v2/writing-algorithms/initialization.md#03-Set-Account-Currency) to the quote currency with a positive quantity.

```python
self.set_account_currency("USDT", 1000)   # Set the account currency to Tether and its quantity to 1,000 USDT
```

For a full example of placing crypto trades, see the [BasicTemplateCryptoAlgorithm](https://github.com/QuantConnect/Lean/blob/master/Algorithm.Python/BasicTemplateCryptoAlgorithm.py).

## Liquidate Positions

If you use the `liquidate` method to liquidate a Crypto position, it only liquidates the quantity of the virtual pair. Since the virtual pair BTCUSDT may not represent all of your BTC holdings, don't use the `liquidate` method to liquidate Crypto positions. Instead, calculate the order quantity based on the currency amounts in your cash book and place manual orders. The following code snippet demonstrates how to liquidate a BTCUSDT position.

```python
def on_data(self, data: Slice):
    self.liquidate_crypto(self._symbol)

def liquidate_crypto(self, symbol):
    crypto = self.securities[symbol]
    base_currency = crypto.base_currency

    # Avoid negative amount after liquidate
    quantity = min(crypto.holdings.quantity, base_currency.amount)

    # Round down to observe the lot size
    lot_size = crypto.symbol_properties.lot_size
    quantity = (round(quantity / lot_size) - 1) * lot_size

    if self.is_valid_order_size(crypto, quantity):
        self.market_order(symbol, -quantity)
```

The order fees don't respect the lot size. When you try to liquidate a position, the absolute value of the base currency quantity can be less than the lot size and greater than zero. In this case, your algorithm holds a position that you can't liquidate, yet `self.portfolio[symbol].invested` is `False`. For more information about `self.portfolio[symbol].invested`, see [Holdings Status](https://www.quantconnect.com/docs/v2/writing-algorithms/portfolio/key-concepts.md#03-Holdings-Status). The following code snippet demonstrates how to determine if you can liquidate a position:

```python
def on_data(self, data: Slice):
    crypto = self.securities[self._symbol]
    if abs(crypto.holdings.quantity) > crypto.symbol_properties.lot_size:
        self.liquidate_crypto(self._symbol)
```

## Examples

The following examples demonstrate some common practices for crypto holdings.

### Example 1: ETH-BTC Proxy Trading

The following algorithm trades trend-following of the ETHBTC crypto pair using a 20-day EMA indicator. To reduce friction (e.g. slippage), we trade the more liquid and popular BTCUSDT and ETHUSDT pair instead. For example, if ETHBTC is above EMA (uptrend), we sell all BTCUSDT holdings and buy ETHUSDT with the same USDT value, going flat on BTC while holding ETH.

```python
class CryptoHoldingsAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)
        self.set_account_currency("USDT", 1000000)

        # We would like to trade the EMA cross between 2 popular cryptos: BTC & ETH,
        # so we request ETHBTC data to find trading opportunities.
        self.ethbtc = self.add_crypto("ETHBTC", Resolution.DAILY, market=Market.COINBASE).symbol
        # Trade through BTCUSDT & ETHUSDT, though, since stable coin trades have lower friction costs and higher liquidity.
        btcusdt = self.add_crypto("BTCUSDT", Resolution.DAILY, market=Market.COINBASE)
        ethusdt = self.add_crypto("ETHUSDT", Resolution.DAILY, market=Market.COINBASE)
        self.btcusdt = btcusdt.symbol
        self.ethusdt = ethusdt.symbol

        # Simulate an account with various crypto cash through holdings.
        # Set the cash book directly with the crypto amounts
        btcusdt.holdings.set_holdings(btcusdt.price, 5)
        btcusdt.base_currency.add_amount(5)
        ethusdt.holdings.set_holdings(ethusdt.price, 22.5)
        ethusdt.base_currency.add_amount(22.5)

        # Add automatic updating of the EMA indicator for trend trade signal emission.
        self._ema = self.ema(self.ethbtc, 20, Resolution.DAILY)
        # Warm up the indicator for its readiness usage immediately.
        self.warm_up_indicator(self.ethbtc, self._ema, Resolution.DAILY)

    def on_data(self, slice: Slice) -> None:
        bar = slice.bars.get(self.ethbtc)
        btc = slice.bars.get(self.btcusdt)
        eth = slice.bars.get(self.ethusdt)

        if bar and self._ema.is_ready and btc and eth:
            ema = self._ema.current.value
            btc_holdings = self.portfolio.cash_book["BTC"].amount
            eth_holdings = self.portfolio.cash_book["ETH"].amount

            # ETHBTC's current price is higher than EMA, suggesting an uptrend.
            if bar.close > ema and not self.portfolio[self.btcusdt].is_short:
                # To follow the up trend of ETHBTC, sell ALL BTCUSDT and buy ETHUSDT with the same value.
                if btc_holdings > 0:
                    btc_value = btc_holdings * btc.close
                    # Sell all BTC
                    self.market_order(self.btcusdt, -btc_holdings)
                    # Buy ETH with the same USDT value
                    eth_size = btc_value / eth.close
                    self.market_order(self.ethusdt, eth_size)
            # ETHBTC's current price is below the EMA, suggesting a downtrend.
            elif bar.close < ema and not self.portfolio[self.btcusdt].is_long:
                # To follow the downtrend of ETHBTC, sell ALL ETHUSDT and buy BTCUSDT with the same value.
                if eth_holdings > 0:
                    eth_value = eth_holdings * eth.close
                    # Sell all ETH
                    self.market_order(self.ethusdt, -eth_holdings)
                    # Buy BTC with the same USDT value
                    btc_size = eth_value / btc.close
                    self.market_order(self.btcusdt, btc_size)
```
