Ask me anything
Ithy Logo
Sign in to save

Python-Based Cryptocurrency Trading Strategy

How to automate your cryptocurrency trades with Python | Opensource.com

This document outlines a comprehensive cryptocurrency trading strategy designed for high win rates, low drawdowns, and relatively high trading frequency, specifically for BNB, ETH, and SOL, using a maximum of four technical indicators, and operating on a daily (1D) timeframe. The strategy is implemented using Python and various libraries.

Core Strategy Design

The strategy focuses on identifying high-probability trading opportunities while minimizing risk. It combines multiple technical indicators to confirm entry and exit points, aiming for a balance between profitability and risk management.

Trading Assets

The strategy is exclusively designed for trading the following cryptocurrencies:

  • Binance Coin (BNB)
  • Ethereum (ETH)
  • Solana (SOL)

Timeframe

The strategy operates on a daily (1D) timeframe, which provides a balance between capturing trends and filtering out short-term noise.

Objectives

The primary objectives of this strategy are:

  • High Win Rate: Prioritize reliable signals to reduce the number of losing trades.
  • Low Drawdown: Implement strict risk management, including stop-loss orders, to control potential losses.
  • High Trading Frequency: Utilize a combination of indicators and parameters to identify frequent trading opportunities without sacrificing accuracy.

Technical Indicators

The strategy employs a combination of four technical indicators to generate trading signals. These indicators are chosen for their effectiveness in identifying potential entry and exit points.

Moving Averages (MA)

Moving averages are used to identify the overall trend direction. Both short-term and long-term moving averages are used to generate crossover signals.

  • Short-term MA: A shorter period MA (e.g., 10 or 20 days) reacts quickly to price changes.
  • Long-term MA: A longer period MA (e.g., 50 days) provides a smoother view of the trend.

Relative Strength Index (RSI)

The RSI is a momentum indicator that measures the magnitude of recent price changes to evaluate overbought or oversold conditions.

  • Overbought: RSI values above 70 suggest that the asset may be overvalued and prone to a price correction.
  • Oversold: RSI values below 30 suggest that the asset may be undervalued and poised for a price increase.

Bollinger Bands

Bollinger Bands consist of a middle band (typically a 20-day moving average) and upper and lower bands, which are standard deviations away from the middle band. They help identify price volatility and potential breakout points.

  • Upper Band: Prices approaching the upper band may indicate an overbought condition.
  • Lower Band: Prices approaching the lower band may indicate an oversold condition.

Moving Average Convergence Divergence (MACD)

The MACD is a trend-following momentum indicator that shows the relationship between two moving averages of a security’s price. It helps identify trend strength and potential reversals.

  • MACD Line: Calculated by subtracting the 26-period Exponential Moving Average (EMA) from the 12-period EMA.
  • Signal Line: A 9-period EMA of the MACD line.
  • Crossovers: When the MACD line crosses above the signal line, it indicates a potential buy signal; when it crosses below, it indicates a potential sell signal.

Average Directional Index (ADX)

The ADX is used to measure the strength of a trend. It helps to filter out trades when the market is not trending strongly.

  • ADX Value: An ADX value above 25 indicates a strong trend.

Trading Logic

The trading logic combines the signals from the technical indicators to generate buy and sell signals.

Buy Signals

A buy signal is generated when the following conditions are met:

  • Moving Average Crossover: The short-term MA crosses above the long-term MA, indicating an upward trend.
  • RSI Oversold: The RSI is below 30 and starts to rise, indicating a potential recovery from an oversold condition.
  • Price near Lower Bollinger Band: The price is near or below the lower Bollinger Band, suggesting a potential price increase.
  • MACD Crossover: The MACD line crosses above the signal line, confirming bullish momentum.
  • ADX Confirmation: The ADX is above 25, indicating a strong trend.

Sell Signals

A sell signal is generated when the following conditions are met:

  • Moving Average Crossover: The short-term MA crosses below the long-term MA, indicating a downward trend.
  • RSI Overbought: The RSI is above 70 and starts to decline, indicating a potential pullback from an overbought condition.
  • Price near Upper Bollinger Band: The price is near or above the upper Bollinger Band, suggesting a potential price decrease.
  • MACD Crossover: The MACD line crosses below the signal line, confirming bearish momentum.
  • ADX Confirmation: The ADX is above 25, indicating a strong trend.

Python Implementation

The strategy is implemented using Python and several libraries, including pandas, numpy, ta, yfinance, and freqtrade. Here's a breakdown of the implementation:

Required Libraries

Install the necessary libraries using pip:


pip install pandas numpy matplotlib ta yfinance python-binance freqtrade
    

Data Acquisition

Historical price data for BNB, ETH, and SOL is obtained using the yfinance library or the Binance API via python-binance.


import yfinance as yf
import pandas as pd
from binance.client import Client

# Using yfinance
symbols = ['BNB-USD', 'ETH-USD', 'SOL-USD']
start_date = '2020-01-01'
end_date = '2023-01-01'
data = {symbol: yf.download(symbol, start=start_date, end=end_date, interval='1d') for symbol in symbols}

# Using python-binance
API_KEY = 'your_api_key'
API_SECRET = 'your_api_secret'
client = Client(API_KEY, API_SECRET)

def get_historical_data(symbol, interval, start_date):
    klines = client.get_historical_klines(symbol, interval, start_date)
    data = pd.DataFrame(klines, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume', 
                                         'close_time', 'quote_asset_volume', 'number_of_trades', 
                                         'taker_buy_base_asset_volume', 'taker_buy_quote_asset_volume', 'ignore'])
    data['timestamp'] = pd.to_datetime(data['timestamp'], unit='ms')
    data.set_index('timestamp', inplace=True)
    data = data[['open', 'high', 'low', 'close', 'volume']].astype(float)
    return data

symbols = ['BNBUSDT', 'ETHUSDT', 'SOLUSDT']
data = {symbol: get_historical_data(symbol, '1d', '2023-01-01') for symbol in symbols}
    

Technical Indicator Calculation

The technical indicators are calculated using the pandas_ta or ta library.


import pandas_ta as ta
from ta.momentum import RSIIndicator
from ta.trend import MACD, ADXIndicator
from ta.volatility import BollingerBands

def add_indicators(df):
    # Using pandas_ta
    df['MA'] = ta.sma(df['Close'], length=20)
    df['RSI'] = ta.rsi(df['Close'], length=14)
    df['BB_upper'], df['BB_middle'], df['BB_lower'] = ta.bbands(df['Close'], length=20, std=2)
    df['MACD'] = ta.macd(df['Close'], fast=12, slow=26, signal=9)['MACD_12_26_9']
    df['MACD_signal'] = ta.macd(df['Close'], fast=12, slow=26, signal=9)['MACDs_12_26_9']
    
    # Using ta
    df['rsi'] = RSIIndicator(close=df['close'], window=14).rsi()
    macd = MACD(close=df['close'], window_slow=26, window_fast=12, window_sign=9)
    df['macd'] = macd.macd()
    df['macd_signal'] = macd.macd_signal()
    bb = BollingerBands(close=df['close'], window=20, window_dev=2)
    df['bb_upper'] = bb.bollinger_hband()
    df['bb_lower'] = bb.bollinger_lband()
    adx = ADXIndicator(high=df['high'], low=df['low'], close=df['close'], window=14)
    df['adx'] = adx.adx()
    return df

for symbol in symbols:
    data[symbol] = add_indicators(data[symbol])
    

Signal Generation

Buy and sell signals are generated based on the conditions described in the trading logic.


def generate_signals(df):
    df['Buy_Signal'] = (df['Close'] < df['BB_lower']) & (df['RSI'] < 30) & (df['MACD'] > df['MACD_signal']) & (df['adx'] > 25)
    df['Sell_Signal'] = (df['Close'] > df['BB_upper']) & (df['RSI'] > 70) & (df['MACD'] < df['MACD_signal']) & (df['adx'] > 25)
    
    df['buy_signal'] = (
        (df['rsi'] < 30) &
        (df['macd'] > df['macd_signal']) &
        (df['close'] < df['bb_lower']) &
        (df['adx'] > 25)
    )
    df['sell_signal'] = (
        (df['rsi'] > 70) &
        (df['macd'] < df['macd_signal']) &
        (df['close'] > df['bb_upper']) &
        (df['adx'] > 25)
    )
    return df

for symbol in symbols:
    data[symbol] = generate_signals(data[symbol])
    

Backtesting

A simple backtesting framework is implemented to evaluate the strategy's performance.


def backtest(df, initial_balance=10000):
    balance = initial_balance
    position = 0
    portfolio = []
    for i in range(1, len(df)):
        if df['Buy_Signal'].iloc[i] and position == 0:
            position = balance / df['Close'].iloc[i]
            balance = 0
            portfolio.append({'Date': df.index[i], 'Action': 'Buy', 'Price': df['Close'].iloc[i], 'Position': position, 'Capital': balance})
        elif df['Sell_Signal'].iloc[i] and position > 0:
            balance = position * df['Close'].iloc[i]
            position = 0
            portfolio.append({'Date': df.index[i], 'Action': 'Sell', 'Price': df['Close'].iloc[i], 'Position': position, 'Capital': balance})
    final_balance = balance + position * df['Close'].iloc[-1]
    return final_balance

results = {symbol: backtest(data[symbol]) for symbol in symbols}
print(results)
    

Risk Management

Risk management is crucial for any trading strategy. The following measures are implemented:

  • Stop Loss: A stop-loss order is set at a certain percentage below the entry price (e.g., 5%) to limit potential losses.
  • Take Profit: A take-profit order is set at a certain percentage above the entry price (e.g., 5%) to secure profits.
  • Position Sizing: The position size for each trade is determined based on the risk tolerance and available capital.

stop_loss = 0.95
take_profit = 1.05

def backtest_with_risk_management(df, initial_balance=10000):
    balance = initial_balance
    position = 0
    portfolio = []
    for i in range(1, len(df)):
        if df['Buy_Signal'].iloc[i] and position == 0:
            position = balance / df['Close'].iloc[i]
            balance = 0
            buy_price = df['Close'].iloc[i]
            portfolio.append({'Date': df.index[i], 'Action': 'Buy', 'Price': buy_price, 'Position': position, 'Capital': balance})
        elif position > 0:
            current_price = df['Close'].iloc[i]
            if current_price <= buy_price * stop_loss or current_price >= buy_price * take_profit:
                balance = position * current_price
                position = 0
                portfolio.append({'Date': df.index[i], 'Action': 'Sell', 'Price': current_price, 'Position': position, 'Capital': balance})
    final_balance = balance + position * df['Close'].iloc[-1]
    return final_balance
    
results_with_risk = {symbol: backtest_with_risk_management(data[symbol]) for symbol in symbols}
print(results_with_risk)
    

Strategy Optimization

The strategy parameters can be optimized using techniques such as grid search or Bayesian optimization to find the best settings for the given market conditions.


from itertools import product

ma_lengths = [10, 20, 50]
rsi_thresholds = [30, 40]
bb_std = [1, 2]

best_params = None
best_final_capital = 0

for ma_length, rsi_threshold, bb_std_value in product(ma_lengths, rsi_thresholds, bb_std):
    for symbol in symbols:
        df = data[symbol]
        df['MA'] = ta.sma(df['Close'], length=ma_length)
        df['RSI'] = ta.rsi(df['Close'], length=14)
        df['BB_upper'], df['BB_middle'], df['BB_lower'] = ta.bbands(df['Close'], length=20, std=bb_std_value)
        df['MACD'] = ta.macd(df['Close'], fast=12, slow=26, signal=9)['MACD_12_26_9']
        df['MACD_signal'] = ta.macd(df['Close'], fast=12, slow=26, signal=9)['MACDs_12_26_9']
        df['Buy_Signal'] = (df['Close'] < df['BB_lower']) & (df['RSI'] < rsi_threshold) & (df['MACD'] > df['MACD_signal'])
        df['Sell_Signal'] = (df['Close'] > df['BB_upper']) & (df['RSI'] > 100 - rsi_threshold) & (df['MACD'] < df['MACD_signal'])
    final_capital = backtest(df)
    if final_capital > best_final_capital:
        best_final_capital = final_capital
        best_params = (ma_length, rsi_threshold, bb_std_value)

print(f"Best Parameters: {best_params}, Best Final Capital: {best_final_capital}")
    

Visualization

The trading signals can be visualized using matplotlib to analyze the strategy's performance.


import matplotlib.pyplot as plt

def plot_signals(df, symbol):
    plt.figure(figsize=(14, 7))
    plt.plot(df['Close'], label='Close Price', alpha=0.5)
    plt.scatter(df.index[df['Buy_Signal']], df['Close'][df['Buy_Signal']], label='Buy Signal', marker='^', color='green')
    plt.scatter(df.index[df['Sell_Signal']], df['Close'][df['Sell_Signal']], label='Sell Signal', marker='v', color='red')
    plt.title(f'{symbol} Trading Signals')
    plt.legend()
    plt.show()

for symbol in symbols:
    plot_signals(data[symbol], symbol)
    

Freqtrade Implementation

The strategy can also be implemented using the freqtrade library, which provides a robust framework for backtesting and live trading.


from freqtrade.strategy import IStrategy
import pandas as pd

class CryptoStrategy(IStrategy):
    minimal_roi = {
        "0": 0.05,
        "30": 0.03,
        "60": 0.01
    }
    stoploss = -0.10
    timeframe = '1d'

    def populate_indicators(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        dataframe['ma_short'] = dataframe['close'].rolling(window=20).mean()
        dataframe['ma_long'] = dataframe['close'].rolling(window=50).mean()
        delta = dataframe['close'].diff(1)
        up_days = delta.copy()
        up_days[delta <= 0] = 0
        down_days = abs(delta.copy())
        down_days[delta > 0] = 0
        RS_up = up_days.ewm(com=13 - 1, adjust=False).mean()
        RS_down = down_days.ewm(com=13 - 1, adjust=False).mean().add(1e-5)
        dataframe['rsi'] = RS_up / RS_down
        dataframe['rsi'] = 100.0 - (100.0 / (1.0 + dataframe['rsi']))
        dataframe['bb_middle'] = dataframe['close'].rolling(window=20).mean()
        dataframe['bb_upper'] = dataframe['bb_middle'] + 2 * dataframe['close'].rolling(window=20).std()
        dataframe['bb_lower'] = dataframe['bb_middle'] - 2 * dataframe['close'].rolling(window=20).std()
        dataframe['macd'] = dataframe['close'].ewm(span=12, adjust=False).mean() - dataframe['close'].ewm(span=26, adjust=False).mean()
        dataframe['signal'] = dataframe['macd'].ewm(span=9, adjust=False).mean()
        return dataframe

    def populate_buy_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        dataframe.loc[
            (
                (dataframe['ma_short'] > dataframe['ma_long']) &
                (dataframe['rsi'] < 30) &
                (dataframe['close'] > dataframe['bb_upper']) &
                (dataframe['macd'] > dataframe['signal'])
            ),
            'buy'] = 1
        return dataframe

    def populate_sell_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        dataframe.loc[
            (
                (dataframe['ma_short'] < dataframe['ma_long']) &
                (dataframe['rsi'] > 70) &
                (dataframe['close'] < dataframe['bb_lower']) &
                (dataframe['macd'] < dataframe['signal'])
            ),
            'sell'] = 1
        return dataframe
    
from freqtrade import Configuration
from freqtrade.backtesting import backtest

config = Configuration({
    'strategy': 'CryptoStrategy',
    'exchange': {
        'name': 'binance',
        'key': 'YOUR_EXCHANGE_KEY',
        'secret': 'YOUR_EXCHANGE_SECRET',
    },
    'pair_whitelist': ['BNB/USDT', 'ETH/USDT', 'SOL/USDT'],
    'stake_currency': 'USDT',
})

backtest(config, strategy=CryptoStrategy, timerange='2022-01-01..2023-01-01', refresh_pairs=False)
    

Conclusion

This document provides a detailed guide to designing and implementing a cryptocurrency trading strategy using Python. The strategy combines multiple technical indicators, risk management techniques, and backtesting to achieve high win rates, low drawdowns, and relatively high trading frequency. The provided code snippets and explanations should serve as a solid foundation for further development and optimization. Remember to always test your strategies thoroughly and be cautious when trading with real funds.

References


Last updated January 6, 2025

AI-assisted research. Sources, methodology and corrections · Source-checked guides and tools · Explore published answers

Ask Ithy AI