Bot トレードロジック 機械学習・データサイエンス

仮想通貨botの開発記録#94(2024/8/25)「AIツール"Cursor"を使ったbot開発【その1】&デルタニュートラルbotの雛形」

2024年8月25日

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

今回は「Cursorを使ったbot開発」についてまとめていきます。

Yodaka

本記事ではCursorの導入botのプロトタイプのコードCursorの機能の概要を紹介します。

注意

本記事の内容は筆者の学習のまとめです。金融取引等における成果を保証するものではなく、充分な検証もなされてはいないため記載されているコードをそのまま利用することは絶対に避けて下さい。

Cursorとは?

Cursorでは

  • AIのサポートに基づいたコードの自動生成
  • AIによるファイルやフォルダ内のコードの説明やプロジェクト全体の調査
  • コードの稼働チェックやバグ取り・修正・編集

を行うことができます。

Yodaka

詳細な説明は以下の通りです。

  • Prompt Bar: Ctrl/Cmd Kを押すと表示されるバーで、通常の入力や@記号を使って他のコンテキストを参照できます。
  • インライン生成: コードが選択されていない状態でCtrl/Cmd Kを押すと、入力したプロンプトに基づいて新しいコードが生成されます。
  • インライン編集: 編集したいコードを選択してからPrompt Barに入力することで、そのコードを直接編集できます。
  • フォローアップ指示: コード生成後に追加の指示を与えることで、AIがその指示に基づいて再生成を行います。
  • デフォルトコンテキスト: Cursorは、手動で追加した@記号以外にも、関連ファイルや最近表示したファイルなど、コード生成を改善するためのコンテキストを自動的に探します。
  • クイック質問: Prompt BarでOption/Alt Enterを押すと、選択したコードや関連するコンテキストに関する質問にCursorが回答します。その後、「do it」などの簡単な指示でコードを生成することが可能です。

概要

Yodaka

Cursorのざっくりした説明です。

Cursorは、VS Codeをフォークしたコードエディタで、AIとの連携を強化した開発環境を提供する。

VS CodeからCursorに移行する際、拡張機能、テーマ、設定、キー設定をインポートできる。

Cursorは定期的に最新のVS Codeバージョンに基づいて更新される。

Cursorは独立したアプリケーションであるため、AIとの統合をより深く行うことができるが、特定の機能(Cursor TabやCMD-Kなど)は既存のプラグインとして提供することができない。

設定は、画面右上の歯車ボタンやショートカットキーを使用してアクセスできる。

Cursorのアクティビティバーは、チャットスペースを確保するためにデフォルトで水平配置されているが、設定で垂直配置に変更することも可能。

料金プラン

https://www.cursor.com/pricing

基本的な機能は無料で使えますが、追加料金を支払うことで使用制限を拡張することも可能です。

Yodaka

Cursorの料金プランについての要約です。

  • サブスクリプションプラン:
    • Hobby: 14日間のProトライアル、50回の遅いプレミアムモデル使用、200回のcursor-small使用、2000回の通常コンプリーション使用が可能。
    • Pro: 月500回の高速プレミアムモデル使用、無制限の遅いプレミアムモデル使用、無制限のcursor-small使用、無制限の通常コンプリーション使用、月10回のClaude Opus使用が可能。
    • Business: Proプランと同じ使用制限に加えて、追加の特典がある。
  • プレミアムモデル: GPT-4、GPT-4o、Claude 3.5 Sonnetがプレミアムモデルとして扱われる。
  • Proトライアル: 新規ユーザーは14日間のProトライアルがあり、その後はHobbyプランに戻る。
  • 高速と遅いリクエスト: ピーク時には高速プレミアムリクエストが制限され、遅いキューに移動されることがある。追加の高速リクエストは設定ページで購入可能。
  • 使用状況の確認: 使用状況はCursorの設定ページで確認できる。サブスクリプションの開始日に基づいて毎月リセットされる。
  • オプションの使用量ベースの料金: 超過使用に対して月単位で料金が課されるオプションがあり、ハードリミットを設定できる。エラーやキャンセルの場合は料金が発生しない。

まずは使ってみる

公式HPからアプリのダウンロードとインストールを完了させて起動しました。

対話型のインターフェースの使用感はChatGPTなどと似ています。

今までの生成系AIとの違いは、Cursorがエディタとして稼働しているという点です。

そのため、コードのコピー&ペーストをしなくてもコード生成やチェックを行うことができます。

Yodaka

インターフェース一つで作業が完結するため、作業効率が飛躍的に高まります。

デルタニュートラルbotの雛形

Yodaka

手始めに「pythonを使って仮想通貨のデルタニュートラル戦略を実行する自動取引botの雛形」を書かせました。

【雛形1】

import ccxt
import time
import logging
import traceback
from decimal import Decimal

# ログの設定
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# レート制限を考慮した待機時間(秒)
API_RATE_LIMIT_WAIT = 1
import numpy as np

class DeltaNeutralBot:
    def __init__(self, exchange_id, api_key, secret):
        self.exchange = getattr(ccxt, exchange_id)({
            'apiKey': api_key,
            'secret': secret,
        })
        self.base_asset = 'BTC'
        self.quote_asset = 'USDT'
        self.symbol = f'{self.base_asset}/{self.quote_asset}'
        self.target_delta = 0  # デルタニュートラル目標

    def fetch_market_data(self):
        ticker = self.exchange.fetch_ticker(self.symbol)
        return ticker['last']

    def calculate_position_delta(self):
        balance = self.exchange.fetch_balance()
        base_position = balance[self.base_asset]['total']
        quote_position = balance[self.quote_asset]['total']
        current_price = self.fetch_market_data()
        
        delta = base_position - (quote_position / current_price)
        return delta

    def execute_trade(self, side, amount):
        try:
            order = self.exchange.create_market_order(self.symbol, side, amount)
            print(f"Executed {side} order: {amount} {self.base_asset}")
            return order
        except Exception as e:
            print(f"Error executing trade: {e}")
            return None

    def rebalance(self):
        current_delta = self.calculate_position_delta()
        adjustment_needed = self.target_delta - current_delta
        
        if abs(adjustment_needed) > 0.01:  # 閾値を設定
            if adjustment_needed > 0:
                self.execute_trade('buy', adjustment_needed)
            else:
                self.execute_trade('sell', abs(adjustment_needed))

    def run(self):
        while True:
            try:
                self.rebalance()
                time.sleep(60)  # 1分ごとにリバランス
            except Exception as e:
                print(f"Error in main loop: {e}")
                time.sleep(60)

if __name__ == "__main__":
    bot = DeltaNeutralBot('binance', 'YOUR_API_KEY', 'YOUR_SECRET_KEY')
    bot.run()

【雛形2】

Yodaka

1の雛形をベースにして「実際に稼働させた時に想定される問題点とそれらへの対策をコードに組み込み、変更点にはコメントを書く」ように指示しました。

import os
    import time
    import ccxt
    import numpy as np
    from dotenv import load_dotenv

    class ImprovedDeltaNeutralBot:
        def __init__(self):
            load_dotenv()  # .env ファイルから環境変数を読み込む(セキュリティ向上)
            self.exchange = ccxt.binance({
                'apiKey': os.getenv('API_KEY'),
                'secret': os.getenv('SECRET_KEY'),
            })
            self.base_asset = 'BTC'
            self.quote_asset = 'USDT'
            self.symbol = f'{self.base_asset}/{self.quote_asset}'
            self.target_delta = 0  # デルタニュートラル目標
            self.rebalance_interval = 300  # リバランス間隔を5分に設定(頻繁な取引を避ける)
            self.min_trade_amount = 0.001  # 最小取引量(BTC)を設定(取引所の制限に対応)
            self.volatility_window = 20  # ボラティリティ計算用のウィンドウサイズ
            self.price_history = []

        def fetch_market_data(self):
            ticker = self.exchange.fetch_ticker(self.symbol)
            self.price_history.append(ticker['last'])
            if len(self.price_history) > self.volatility_window:
                self.price_history.pop(0)
            return ticker['last']

        def calculate_position_delta(self):
            balance = self.exchange.fetch_balance()
            base_position = balance[self.base_asset]['total']
            quote_position = balance[self.quote_asset]['total']
            current_price = self.fetch_market_data()
            
            delta = base_position - (quote_position / current_price)
            return delta

        def calculate_volatility(self):
            # ボラティリティを計算(市場の変動に応じて取引を調整)
            if len(self.price_history) < self.volatility_window:
                return None
            return np.std(self.price_history) / np.mean(self.price_history)

        def execute_trade(self, side, amount):
            try:
                if amount < self.min_trade_amount:
                    print(f"Trade amount {amount} is below minimum {self.min_trade_amount}. Skipping trade.")
                    return None
                order = self.exchange.create_market_order(self.symbol, side, amount)
                print(f"Executed {side} order: {amount} {self.base_asset}")
                return order
            except ccxt.NetworkError as e:
                # ネットワークエラーの場合はリトライ(接続の問題に対処)
                print(f"Network error: {e}. Retrying in 60 seconds...")
                time.sleep(60)
                return self.execute_trade(side, amount)
            except ccxt.ExchangeError as e:
                # 取引所のエラーの場合はスキップ(一時的な取引所の問題に対処)
                print(f"Exchange error: {e}. Skipping this trade.")
                return None
            except Exception as e:
                # その他の予期せぬエラーの処理
                print(f"Unexpected error executing trade: {e}")
                return None

        def rebalance(self):
            current_delta = self.calculate_position_delta()
            adjustment_needed = self.target_delta - current_delta
            
            volatility = self.calculate_volatility()
            if volatility:
                # ボラティリティに基づいて閾値を動的に調整(市場の状況に応じて取引を調整)
                threshold = max(0.01, volatility * 2)
            else:
                threshold = 0.01

            if abs(adjustment_needed) > threshold:
                if adjustment_needed > 0:
                    self.execute_trade('buy', adjustment_needed)
                else:
                    self.execute_trade('sell', abs(adjustment_needed))

        def run(self):
            while True:
                try:
                    self.rebalance()
                    time.sleep(self.rebalance_interval)
                except Exception as e:
                    # メインループでのエラー処理(プログラムの継続性を確保)
                    print(f"Error in main loop: {e}")
                    time.sleep(60)

    if __name__ == "__main__":
        bot = ImprovedDeltaNeutralBot()
        bot.run()

Cursorの主要な機能まとめ

Yodaka

今回使った主な機能と調べたことをまとめておきます。

Cmd K

Cursorの「Cmd K」(Windows/Linuxでは「Ctrl K」)機能についての要約です。

以下の機能を使うことができます。

  • Prompt Bar: Ctrl/Cmd Kを押すと表示されるバーで、通常の入力や@記号を使って他のコンテキストを参照できます。
  • インライン生成: コードが選択されていない状態でCtrl/Cmd Kを押すと、入力したプロンプトに基づいて新しいコードが生成されます。
  • インライン編集: 編集したいコードを選択してからPrompt Barに入力することで、そのコードを直接編集できます。
  • フォローアップ指示: コード生成後に追加の指示を与えることで、AIがその指示に基づいて再生成を行います。
  • デフォルトコンテキスト: Cursorは、手動で追加した@記号以外にも、関連ファイルや最近表示したファイルなど、コード生成を改善するためのコンテキストを自動的に探します。
  • クイック質問: Prompt BarでOption/Alt Enterを押すと、選択したコードや関連するコンテキストに関する質問にCursorが回答します。その後、「do it」などの簡単な指示でコードを生成することが可能です。

Cursorの内蔵ターミナルで「Ctrl/⌘ K」を押すと、ターミナルの下部にプロンプトバーが表示されます。このプロンプトバーに実行したいアクションを入力すると、ターミナル Cmd Kがコマンドを生成します。生成されたコマンドは、Escキーで確認するか、Ctrl/⌘ + Enterで即座に実行できます。

デフォルトでは、Terminal Cmd Kは最近のターミナル履歴やプロンプトバーに入力した指示をコンテキストとして使用します。

コードベースのインデックス化

Cursorのコードベースインデックスについての要約です。

  • コードベースのインデックス化: コードベースのインデックスを作成することで、@codebaseやCtrl/⌘ Enterを使用する際に、より正確な回答が得られるようになります。Cursorはコードベース内の各ファイルに対してエンベディングを計算し、これを使って回答の精度を向上させます。インデックスはコードベースの最新の変更と自動的に同期されます。
  • 高度な設定: デフォルトでは、プライバシーモードが有効でない限り、Cursorはコードベース内のすべてのファイルをインデックス化します。設定を拡張して、自動インデックス化の有効化や、インデックス化から除外するファイルを指定することもできます。AIが読む必要のない大きなコンテンツファイルを除外することで、回答の精度が向上する場合があります。

高度な機能

Cursorの高度な機能についての要約です。

  • モデルの選択: Cursor Chat、Ctrl/⌘ K、Terminal Ctrl/⌘ Kでは、使用するAIモデルを簡単に切り替えることができます。
  • モデルのドロップダウン: AI入力ボックスの下にあるドロップダウンメニューから、使用したいモデルを選択できます。デフォルトで使用できるモデルには、GPT-4o、GPT-4、Claude 3.5 Sonnet、cursor-smallがあります。cursor-smallは、GPT-4ほど賢くはないものの、速度が速く無制限に使用できます。追加のモデルは、Cursor Settings > Models > Model Namesで追加できます。
  • ロングコンテキスト専用モデル: ロングコンテキストチャットでは、対応するモデルのみ選択可能です。例として、gpt-4o-128k、gemini-1.5-flash-500k、claude-3-haiku-200kなどがあります。
  • コンテキストウィンドウ: 通常のチャットでは約20,000トークン、Cmd-Kでは約10,000トークンに制限しています。ロングコンテキストチャットでは、モデルがサポートする最大のコンテキストウィンドウを使用します。

各シンボルの意味

CursorのAI入力ボックスでの@シンボルの使用についての要約です。

  • 基本的な使い方: Cmd K、Chat、Terminal Cmd KなどのAI入力ボックスで「@」を入力すると、関連する提案のリストが表示されます。入力に応じて自動的に最も関連性の高い提案が表示されます。
  • キーボードショートカット: 提案リスト内を上下の矢印キーでナビゲートし、Enterキーで提案を選択できます。カテゴリー(例: Files)の場合、そのカテゴリー内の関連項目のみが表示されるようにフィルタリングされます。
  • Cmd K用ショートカット: Cmd Kで@シンボルを使用する際、上下の矢印キーで提案リストをナビゲートし、Enterキーで選択したコンテキスト項目を展開/折りたたむことができます。ファイル参照については、Ctrl/⌘ Mを使用してファイルの読み取り戦略を切り替えることができます。
Yodaka

詳しい解説は別の記事でまとめる予定です。

まとめ

今回は「Cursorを使ってデルタニュートラルbotを作ってみる」という内容でした。

少し触った実感としては、botのプロトタイプ量産や、コードのチェック(デバッグやテスト)をとても早く済ませられるため、開発効率を高めるのに役立つと感じました。

また、bot開発にとどまらず個人開発の効率を飛躍的に高めてくれるツールであるとも感じました。

AIに書かせたものをそのまま運用はしませんが、少なくともプロトタイプの量産は楽になりそうです。 ロジックの細かい部分の修正などはマシンに任せると早くて正確なので、最後の詰めやより精度の高い取引bot開発に活用していきます。

ユーザーの言語化能力”が重要であるという点はこれまでの生成AI活用と共通しているので、引き続き書籍や論文を読み込んで基礎部分の理解と学習を丁寧に進める時間も確保します。 根本的な部分の理解ができていると応用が効くので、AIへの指示も様々なバリエーションを試すことができる。機械に頼り過ぎて生成されたコードの意味が分からないようでは本末転倒なので、コードリーディングの技術も磨くことを欠かさないようにしたいですね。

Yodaka

結局はこれからやっていくこともこれまでやってきたことと本質的に変わらない気がします。

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

宿題

  • 基礎部分の理解と学習を丁寧に進める
    書籍や参考文献を読みこむ
    実践する
  • 発展的なコーディングの練習をする
    毎日10種類、Cursorに書かせたコードの一部を写経する
    →分からない部分はすぐに質問して理解を深める
  • Dockerのプロジェクトを一気に組み上げられないかどうか試す
  • 環境構築をAIに任せられないかどうか試す

-Bot, トレードロジック, 機械学習・データサイエンス