Bot トレードロジック プログラミングスキル

仮想通貨botの開発記録#89(2024/8/21)「トレンドフォローbotとバックテスト」

2024年8月21日

前回の記事に引き続き、今回も仮想通貨botの開発状況をまとめていきます。

今回は、トレンドフォローbotとバックテストについてまとめます。

Yodaka

シンプルな戦略とその検証をするコードです。コードの一部はChatGPTに書かせています。

トレンドフォローbotのコード(雛形)

Yodaka

移動平均線のクロスオーバーに基づいて取引をするシンプルなbotです。

import ccxt
import pandas as pd
import time
from datetime import datetime

exchange = ccxt.binance({
    'apiKey': '',
    'secret': ''
})
symbol = 'BTC/USD'
timeframe = '1m'  # 1分足のデータ

def fetch_data(symbol, timeframe):
    bars = exchange.fetch_ohlcv(symbol, timeframe, limit=100)
    df = pd.DataFrame(bars, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
    df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
    return df

def calculate_signals(df):
    df['MA20'] = df['close'].rolling(window=20).mean()
    df['MA50'] = df['close'].rolling(window=50).mean()
    df['Signal'] = 0    
    df.loc[df['MA20'] > df['MA50'], 'Signal'] = 1
    df.loc[df['MA20'] < df['MA50'], 'Signal'] = -1
    df['Position'] = df['Signal'].diff()
    return df

def run_bot():
    while True:
        df = fetch_data(symbol, timeframe)
        df = calculate_signals(df)
        
        latest_position = df.iloc[-1]['Position']
        if latest_position > 0:
            print(f"{datetime.now()} - Buy signal")
            #購入ロジック
            
        elif latest_position < 0:
            print(f"{datetime.now()} - Sell signal")
            #売却ロジック
        
        time.sleep(60)

if __name__ == '__main__':
    run_bot()
関連記事
仮想通貨botの開発記録#81(2024/7/27)「基本的な取引ロジックの雛形まとめ①」

続きを見る

バックテスト用のコード

import ccxt
import pandas as pd
import matplotlib.pyplot as plt

exchange = ccxt.binance()

symbol = 'BTC/USDT'  # BinanceではUSDではなくUSDTを使います
timeframe = '1m'

def fetch_historical_data(symbol, timeframe, since):
    bars = exchange.fetch_ohlcv(symbol, timeframe, since=since, limit=1000)
    df = pd.DataFrame(bars, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
    df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
    return df

def calculate_signals(df):
    df['MA20'] = df['close'].rolling(window=20).mean()
    df['MA50'] = df['close'].rolling(window=50).mean()
    df['Signal'] = 0
    df.loc[df['MA20'] > df['MA50'], 'Signal'] = 1
    df.loc[df['MA20'] < df['MA50'], 'Signal'] = -1
    df['Position'] = df['Signal'].shift()
    return df

def backtest_strategy(df):
    initial_balance = 10000
    balance = initial_balance
    position = 0  # ポジション:0 = ノーポジション, 1 = ロング, -1 = ショート
    
    for i in range(1, len(df)):
        if df.iloc[i]['Position'] == 1 and position == 0:
            # Buy
            position = 1
            buy_price = df.iloc[i]['close']
            print(f"Buy at {buy_price}")
        
        elif df.iloc[i]['Position'] == -1 and position == 1:
            # Sell
            position = 0
            sell_price = df.iloc[i]['close']
            profit = (sell_price - buy_price) / buy_price * balance
            balance += profit
            print(f"Sell at {sell_price}, Profit: {profit}")
    
    return balance

if __name__ == '__main__':
    since = exchange.parse8601('2023-01-01T00:00:00Z')
    df = fetch_historical_data(symbol, timeframe, since)
    df = calculate_signals(df)
    
    final_balance = backtest_strategy(df)
    print(f"Final balance: {final_balance}")
Yodaka

上記のパラメータをいじることで、戦略の有効性を確認することができます。

変更可能なパラメータ

  • 使用する取引所
  • 取引する通貨
  • 運用期間
  • 採用する時間枠(timeframe) = ◯分足,◯時間足,◯日足
  • 移動平均の数値(◯日移動平均での比較を参入・退出のシグナルとするのか)
  • 移動平均の算出方法(単純移動平均・修正移動平均・過重移動平均)

追加が必要なこと

  • 取引手数料(取引所ごとに仕様が異なるので個別に設定する)
  • 収益率の算出(リターンが何%になるのか)
  • 損切りと利食いのライン設定ロジック
  • 資産の総額に対して損切りと利食いの注文サイズを調整するロジック
  • 利益が出た期間と損失を出した期間の市場分析
  • 取得可能な限りの全過去データ
  • 過去データを元にランダムな期間を抽出してバックテストを行う

損益をグラフで描写

import ccxt
import pandas as pd
import matplotlib.pyplot as plt

exchange = ccxt.binance()

symbol = 'BTC/USDT'  # BinanceではUSDではなくUSDTを使います
timeframe = '1d'

def fetch_historical_data(symbol, timeframe, since):
    bars = exchange.fetch_ohlcv(symbol, timeframe, since=since, limit=1000)
    df = pd.DataFrame(bars, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
    df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
    return df

def calculate_signals(df):
    df['MA20'] = df['close'].rolling(window=30).mean()
    df['MA50'] = df['close'].rolling(window=50).mean()
    df['Signal'] = 0
    df.loc[df['MA20'] > df['MA50'], 'Signal'] = 1
    df.loc[df['MA20'] < df['MA50'], 'Signal'] = -1
    df['Position'] = df['Signal'].shift()
    return df

def backtest_strategy(df):
    initial_balance = 10000
    balance = initial_balance
    position = 0  # ポジション:0 = ノーポジション, 1 = ロング, -1 = ショート
    balance_over_time = []  # 各時点でのバランスを記録するリスト
    
    for i in range(1, len(df)):
        if df.iloc[i]['Position'] == 1 and position == 0:
            # Buy
            position = 1
            buy_price = df.iloc[i]['close']
            print(f"Buy at {buy_price}")
        
        elif df.iloc[i]['Position'] == -1 and position == 1:
            # Sell
            position = 0
            sell_price = df.iloc[i]['close']
            profit = (sell_price - buy_price) / buy_price * balance
            balance += profit
            print(f"Sell at {sell_price}, Profit: {profit}")
        
        balance_over_time.append(balance)
    
    # 時系列のバランスをDataFrameに変換
    balance_df = pd.DataFrame({
        'timestamp': df.iloc[1:len(balance_over_time)+1]['timestamp'],
        'balance': balance_over_time
    })
    
    return balance_df

if __name__ == '__main__':
    since = exchange.parse8601('2024-01-01T00:00:00Z')
    df = fetch_historical_data(symbol, timeframe, since)
    df = calculate_signals(df)
    
    balance_df = backtest_strategy(df)
    
    # 損益の流れを時系列で可視化する
    plt.figure(figsize=(12, 6))
    plt.plot(balance_df['timestamp'], balance_df['balance'], label='Balance Over Time')
    plt.title('Balance Over Time from Backtesting Strategy')
    plt.xlabel('Time')
    plt.ylabel('Balance')
    plt.grid(True)
    plt.legend()
    plt.show()

まとめ

今回はトレンドフォローbotとそのバックテスト用のコードをまとめました。

変更できるパラメータをいろいろ試す中で、有効な戦略を磨いていきたいです。

宿題

パラメータを変更・追加して、バックテストを行う

Yodaka

今後もこの調子で仮想通貨botの開発状況をまとめていきます。

-Bot, トレードロジック, プログラミングスキル