ペーパートレードとは?本番投入前にボットを検証する具体的な方法


※本記事にはアフィリエイト広告(プロモーション)が含まれています。

バックテストで良い成績が出た戦略でも、いきなり本番資金を入れるのは不安なものです。過去のバックテストの罠ウォークフォワード検証を通過しても、それはあくまで「過去の価格データ」に対する結果でしかありません。本番のAPI応答遅延やエラー処理、注文執行のタイミングまで確認するには、もう一段階の検証が必要です。それが「ペーパートレード」です。

ペーパートレードとは

ペーパートレードとは、実際の資金を動かさずに、リアルタイムの相場に対してボットのロジックを走らせる検証方法です。「デモトレード」「紙トレード」と呼ばれることもあります。仮想の残高・仮想のポジションを内部で管理し、注文は取引所に送らず、代わりに現在の価格情報を使って約定したものとして記録します。

バックテストとの違いは「時間の流れ方」です。

項目 バックテスト ペーパートレード
使うデータ 過去のOHLCVをまとめて処理 リアルタイムの値動きを1本ずつ処理
検証できること 戦略ロジックの過去成績 ロジック+実行タイミング+エラー処理
所要時間 数秒〜数分 実際の時間の経過分だけかかる
API呼び出し 基本的に発生しない(過去データ取得のみ) 本番同様に発生する

バックテストは「戦略そのもの」を高速に検証するのに向いていますが、ペーパートレードは「ボットというプログラム全体」が本番同様の条件で正しく動くかを検証するのに向いています。

バックテストだけでは分からないこと

バックテストの精度をどれだけ上げても、以下のような要素は本番相当の環境でしか確認できません。

  • APIのレイテンシ: 価格取得から発注までの間に相場が動く時間差
  • レートリミットへの抵触: レートリミット設計が甘いと、想定より早く制限に引っかかる
  • 異常系での挙動: 接続が切れた、注文がタイムアウトした、板が薄くて約定しない、といったケース
  • 再起動時の状態復元: プロセスが落ちて再起動したときに二重発注が起きないか(再起動と状態復元で詳しく解説しています)
  • ログの十分性: 障害調査に必要な情報がログに残っているか(ログ設計の基本を参照)

これらは過去データを一括処理するバックテストでは再現しづらく、ボットを実際に「動かしっぱなし」にして初めて見えてくる問題です。

ペーパートレードの実装パターン

実装方法は大きく2つに分かれます。

パターン1: 取引所のテストネット・デモ口座を使う

一部の取引所はテスト用のAPIエンドポイントやデモ口座を提供しています。実際のAPIと同じレスポンス形式で仮想残高を返してくれるため、実装の手間が少ないのが利点です。ただし、以下の点に注意してください。

  • 対応している取引所・銘柄が限られる
  • 板の厚みや約定のタイミングが本番と異なることがある
  • テストネットの仕様は変更されることがあるため、必ず公式の最新情報を確認してください

パターン2: 自作の疑似発注ロジックを組み込む

より汎用的なのは、ボット自身に「本番発注」と「疑似発注」を切り替えるフラグを持たせる方法です。実際の価格取得ロジックはそのまま使い、発注部分だけをモックに差し替えます。

import time
import logging
from dataclasses import dataclass, field

logger = logging.getLogger("paper_trade")

@dataclass
class PaperAccount:
    balance_quote: float = 100_000.0  # 仮想の日本円残高など
    position_base: float = 0.0
    orders: list = field(default_factory=list)

    def buy(self, symbol: str, price: float, amount: float):
        cost = price * amount
        if cost > self.balance_quote:
            logger.warning(f"残高不足のため見送り: cost={cost}, balance={self.balance_quote}")
            return None
        self.balance_quote -= cost
        self.position_base += amount
        order = {"side": "buy", "symbol": symbol, "price": price, "amount": amount, "ts": time.time()}
        self.orders.append(order)
        logger.info(f"[PAPER] BUY {symbol} price={price} amount={amount}")
        return order

    def sell(self, symbol: str, price: float, amount: float):
        if amount > self.position_base:
            logger.warning(f"保有数量不足のため見送り: amount={amount}, position={self.position_base}")
            return None
        self.position_base -= amount
        self.balance_quote += price * amount
        order = {"side": "sell", "symbol": symbol, "price": price, "amount": amount, "ts": time.time()}
        self.orders.append(order)
        logger.info(f"[PAPER] SELL {symbol} price={price} amount={amount}")
        return order


class OrderExecutor:
    def __init__(self, exchange, dry_run: bool = True):
        self.exchange = exchange
        self.dry_run = dry_run
        self.paper_account = PaperAccount()

    def create_order(self, symbol: str, side: str, amount: float, price: float):
        if self.dry_run:
            if side == "buy":
                return self.paper_account.buy(symbol, price, amount)
            return self.paper_account.sell(symbol, price, amount)
        # dry_run=False のときだけ本番のccxt発注が実行される
        return self.exchange.create_order(symbol, "limit", side, amount, price)

ポイントは、価格取得・シグナル計算・ログ出力・エラーハンドリングといった「発注以外の全ロジック」を本番とまったく同じコードパスで通すことです。発注部分だけをdry_runフラグで分岐させることで、本番投入時に別のコードを動かすことによる「ペーパートレードでは動いたのに本番では動かない」という事態を防げます。

ペーパートレードで確認すべきポイント

ペーパートレードは最低でも数日〜数週間、できれば普段の監視ルーティンを回しながら継続するのが望ましいです。確認すべきなのは以下のような項目です。

  • 想定通りのタイミングでシグナルが発生しているか: ログに出力される価格・指標値とチャートを突き合わせて確認する
  • エラー発生時に安全側へ倒れているか: 取引所障害時のフェイルセーフ設計が機能するか、意図的に接続を切って確認する
  • プロセスを再起動しても状態が壊れないか: 疑似残高やポジションの状態をファイルやDBに永続化し、再起動後に復元できるか
  • リソースが枯渇しないか: メモリリークやログファイルの肥大化がないか、長時間稼働で確認する
  • 通知が正しく飛ぶか: Discord通知などの監視系が想定通り動作するか

これらはユニットテスト(pytestとモックでの検証)ではカバーしにくい、「時間の経過」と「実際のAPI呼び出し」が絡む部分です。ユニットテストとペーパートレードは役割が異なるため、どちらか一方で済ませようとしないことが大切です。

本番移行時の注意点

ペーパートレードで問題が見つからなくなったからといって、いきなり想定額を全額投入するのは避けるべきです。一般的には次のような段階を踏むことが推奨されます。

  1. 想定額よりも小さい金額(生活に影響のない範囲)でまず稼働させる
  2. 成績指標を一定期間記録し、ペーパートレード時の傾向と乖離がないか確認する
  3. 問題がなければ段階的に投入額を引き上げる
  4. どの段階でもボットを止めるべき基準をあらかじめ決めておく

本番の相場では、ペーパートレードでは想定していなかった約定拒否やスリッページが発生することもあります。ペーパートレードは「本番に近い条件での事前確認」であって「本番と完全に同じ結果を保証するもの」ではない、という前提を持っておくことが重要です。

PR

ペーパートレードは数日〜数週間、ボットを常時起動させ続ける必要があります。開発・検証用の環境を安く用意したい場合は、時間課金にも対応したVPSが候補になります。

サービス開発やテスト環境に便利な【ConoHa】

まとめ

  • ペーパートレードは、リアルタイムの値動きに対して疑似発注でボットを検証する手法
  • バックテストでは分からない、APIレイテンシ・レートリミット・異常系・再起動時の挙動などを確認できる
  • 実装は「取引所のテストネットを使う」か「発注部分だけをdry_runフラグでモック化する」のどちらか
  • 発注以外のロジックは本番と同じコードパスを通すことで、移行時のギャップを減らせる
  • 問題がなくなっても、本番投入は小さい金額から段階的に行うのが基本