Bot プログラミングスキル 書籍・論文・web記事

仮想通貨botの開発記録#47(2024/1/20)「C級Botterへのステップ②価格取得の方法&ccxtライブラリの熟読」

2024年1月20日

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

参考にしたのは【Botter Advent Calendar2023】仮想通貨で月に1万円を稼ぐC級Botterになろうです。

また、今回は「現在の価格とは何をもってそのように定義するのか?」という点についても掘り下げることができました。

参考にしたのは2-4."現在価格"の考察です。

ccxtライブラリを参照してChatGPTと共にコードを書く

Yodaka

今回は例題1に取り組みました。

例題1:fetchOrderBookを使って任意の取引所(現物)のETH-USDT価格を取得しましょう
例題2: 今、0.01ethを任意のperpがある取引所でロングしているとします。例えば、Kucoin futuresのETH-USDTMにポジションがあるとします。
 fetchPositions fetch_balance を用いて、ロング/ショートをどの程度持っているか表示し、フリーなUSDTバランスをprintで表示させましょう。
例題 3: createOrderを用いて0.01 ethを購入し、10秒後に売却しましょう。レバは3倍とし、market orderを使用しましょう。

下準備⓪:取引クラスのインスタンス化

ccxtライブラリは、さまざまな仮想通貨取引所と通信するためのPythonライブラリであり、各取引所に対応するクラスを提供しています。これらのクラスは、取引所のAPIにアクセスするためのメソッドや機能を提供しています。

取引所クラスをインスタンス化するとは、その取引所にアクセスするための具体的なオブジェクトを作成することを指します。これにより、その取引所と対話できるようになります。

もう少し簡単に言うと「取引クラスのインスタンス化」とは

その取引所のAPIにアクセスでき、取引や市場データの取得などさまざまな操作を行う準備

のことです。

Yodaka

簡単な例を見てみましょう。

import ccxt

# 取引所クラスをインスタンス化する例
exchange = ccxt.binance({
    'apiKey': 'your_api_key',
    'secret': 'your_secret_key',
})

# 取引所の情報を取得
symbol = 'BTC/USDT'
ticker = exchange.fetch_ticker(symbol)

print(ticker)

この例では、Binance取引所のクラス(ccxt.binance)をインスタンス化しています。この取引所クラスにはBinance APIにアクセスするためのキー情報が渡されています。その後、fetch_tickerメソッドを使用して指定された通貨ペア('BTC/USDT')の最新の価格情報を取得しています。

Yodaka

このようにしてccxtライブラリでさまざまな操作を行う下準備をします。

下準備①:リストの取得

Yodaka

ccxtがサポートしている取引所の一覧を取得します。

#ccxtでサポートされている取引所のリストを取得
import ccxt
from pprint import pprint#見やすくするためにpprintを使用

pprint (ccxt.exchanges)

下準備②:トークンペアのリストの取得

Yodaka

任意の取引所で取引可能なトークンペアのリストを取得します。

import ccxt

def get_exchange_symbols(exchange_name):
    try:
        # 取引所のインスタンスを作成
        exchange = getattr(ccxt, exchange_name)()

        # 取引所のシンボル(トークンのペア)を取得
        symbols = exchange.load_markets().keys()

        return symbols

    except ccxt.NetworkError as e:
        print(f"Network error: {e}")
    except ccxt.ExchangeError as e:
        print(f"Exchange error: {e}")
    except Exception as e:
        print(f"An error occurred: {e}")

# 取引所の名前(KuCoinの場合は 'kucoin' を指定)
exchange_name = 'kucoin'

# 取引所のシンボル(トークンのペア)を取得
symbols = get_exchange_symbols(exchange_name)

# 結果を表示
print(f"{exchange_name}の取引可能なシンボル(トークンのペア):")
for symbol in symbols:
    print(symbol)
Yodaka

実際にこのコードを実行すると、とても長いリストが延々と並ぶことになります。表示される量が膨大で分かりにくいため、コードを修正することにしました。

取引可能なトークンペアの検出

Yodaka

その取引所で扱うトークンペアの有無を調べるためのコードを書かせました。

import ccxt

def check_token_pair(exchange_name, base_currency, quote_currency):
    try:
        # 取引所のインスタンスを作成
        exchange = getattr(ccxt, exchange_name)()

        # 取引所のシンボル(トークンのペア)を取得
        symbols = exchange.load_markets().keys()

        # 検証したいトークンのペア
        target_pair = f"{base_currency}/{quote_currency}"

        # 検証したいトークンのペアが取引所で取引可能かどうかを検出
        is_pair_available = target_pair in symbols

        # 結果を表示
        if is_pair_available:
            print(f"{exchange_name}で{target_pair}は取引可能です。")
        else:
            print(f"{exchange_name}で{target_pair}は取引不可能です。")

    except ccxt.NetworkError as e:
        print(f"Network error: {e}")
    except ccxt.ExchangeError as e:
        print(f"Exchange error: {e}")
    except Exception as e:
        print(f"An error occurred: {e}")

# 取引所の名前(KuCoinの場合は 'kucoin' を指定)
exchange_name = 'kucoin'

# 検証したいトークンのベース通貨とクォート通貨を指定
base_currency = 'BTC'
quote_currency = 'USDT'

# トークンのペアが取引所で取引可能かどうかを検出
check_token_pair(exchange_name, base_currency, quote_currency)
Yodaka

コード終盤の「検証したいトークンのベース通貨とクォート通貨を指定」の部分に指定の通貨シンボルを入力すると「その取引所で取引可能な通貨ペアかどうか」を調べることができます。

例題1の回答

練習:fetch_ticker

Yodaka

まずは、Tickerを参照する場合は、以下のコードで価格を取得してみました。
fetch_tickerメソッドを使います。
指定するのはsymbolだけなので、シンプルで分かりやすいですね。

#Tickerを参照して現物取引価格を取得
import ccxt

# 取引所クラスをインスタンス化する
exchange = ccxt.binance({
    'apiKey': '***',
    'secret': '***',
})

# 取引所の情報を取得
symbol = 'ETH/USDT'
ticker = exchange.fetch_ticker(symbol)

print(ticker)

本題:fetch_order_book

Yodaka

次に、例題2につなげることを意識してkucoinでの価格取得用コードを書きました。
MMBot開発につなげることを見越して、Bid/Ask/スプレッドを取得しています。

import ccxt

exchange = ccxt.kucoin()#任意の取引所を設定
symbol = 'ETH/USDT'

# マーケット情報をロード
exchange.load_markets()

# 取引ペアが存在するか確認
if symbol in exchange.symbols:
    orderbook = exchange.fetch_order_book(symbol)
    bid = orderbook['bids'][0][0] if len(orderbook['bids']) > 0 else None
    ask = orderbook['asks'][0][0] if len(orderbook['asks']) > 0 else None
    spread = (ask - bid) if (bid and ask) else None
    print(exchange.id, 'market price', {'bid': bid, 'ask': ask, 'spread': spread})
else:
    print(f"Symbol {symbol} not found.")
Yodaka

load_marketを使わない場合は以下のような書き方もできます。

import ccxt

# KuCoinの取引所を初期化
exchange = ccxt.kucoin()

# マーケットとシンボルを指定 (例: ETH/USDT)
symbol = 'ETH/USDT'

# fetchOrderBookを使ってETH-USDTの価格情報を取得
order_book = exchange.fetch_order_book(symbol)

# ベストビッドとベストアスクの価格を表示
best_bid_price = order_book['bids'][0][0]
best_ask_price = order_book['asks'][0][0]

print(f'KuCoin ETH/USDT - Best Bid Price: {best_bid_price}, Best Ask Price: {best_ask_price}')
Yodaka

主に参考にしたのはccxtドキュメントの以下の部分です。

market-price:現在の最良の価格(クエリ市場価格)を取得し,ビッド/アスク/スプレッドを計算する。
Loading Markets:ccxtを迂回して、取引所からデータを取得する。→通信の負担が減ってBotの速度が上がる。
Order Book:板情報の取得。ビッド(買い)価格とアスク(売り)価格、出来高、その他のデータを含むオープン注文に関する情報。市場の深さ。取引の意思決定プロセス。

「現在の価格とは?」

Yodaka

「現在の価格」という概念が私にとって非常に曖昧だったので、勉強しました。
執行する戦略のタイプに応じて現在の価格の定義は変わるということが分かりました。

【宿題】

・MMbotを作る際には取引所の仕様を「板情報・Ticker・約定」の3観点でデータを比較して研究する
・使用する取引所やモジュールの仕様が書いてあるドキュメントを読み込む(取得している価格データは何を参照したものなのかを確認する)
・Bot稼働ログのデータ分析で何を確認したいのかを明確にする

例題1の回答:番外編(ChatGPT)

Yodaka

ChatGPTにも出力させてみました。少しだけ修正を加えています。

【Binance用】

#例題1:fetchOrderBookを使って任意の取引所(現物)のETH-USDT価格を取得しましょう
import ccxt

# Binanceの取引所を初期化
exchange = ccxt.binance({
    'apiKey': binance_api_key,
    'secret': binance_secret_key,
})

# マーケットとシンボルを指定
symbol = 'ETH/USDT'

# fetchOrderBookを使ってETH-USDTの価格情報を取得
order_book = exchange.fetch_order_book(symbol)

# ベストビッドとベストアスクの価格を表示
best_bid_price = order_book['bids'][0][0]
best_ask_price = order_book['asks'][0][0]

print(f'Binance ETH/USDT - Best Bid Price: {best_bid_price}, Best Ask Price: {best_ask_price}')

【Kucoin用】

import ccxt

# KuCoinの取引所を初期化
exchange = ccxt.kucoin({
    'apiKey': kucoin_api_key,
    'secret': kucoin_secret_key,
})

# マーケットとシンボルを指定 (例: ETH/USDT)
symbol = 'ETH/USDT'

# fetchOrderBookを使ってETH-USDTの価格情報を取得
order_book = exchange.fetch_order_book(symbol)

# ベストビッドとベストアスクの価格を表示
best_bid_price = order_book['bids'][0][0]
best_ask_price = order_book['asks'][0][0]

print(f'KuCoin ETH/USDT - Best Bid Price: {best_bid_price}, Best Ask Price: {best_ask_price}')
Yodaka

ちなみにChatGPTに回答させると、最初はAPIキーとAPIシークレットの入力も含めたコードで返してきました。しかし、パブリックAPIを利用する場合はAPIキーとAPIシークレットの両方とも必要ないため、それを指摘すると改善したコードを返してきました。

基本的な理解が伴っていないと、AIが書いたコードをそのまま使うことになってしまうため、ドキュメントの熟読は必須だなと感じた瞬間でした。

価格取得の方法:基本+5種(Binance用)

Yodaka

最後に「ccxtライブラリを使って任意の取引所(この例ではBinance)からETH-USDT価格を取得する方法」をまとめておきます。今後の執行戦略によって使い分けていこうと思います。

基本:Tickerメソッドを使用して情報を取得する方法

#Tickerを参照して現物取引価格を取得
import ccxt

# 取引所クラスをインスタンス化する例
exchange = ccxt.binance({
    'apiKey': '***',
    'secret': '***',
})

# 取引所の情報を取得
symbol = 'ETH/USDT'
ticker = exchange.fetch_ticker(symbol)

print(ticker)
  1. import ccxt: ccxtライブラリをインポートする。
  2. exchange = ccxt.binance({'apiKey': '***', 'secret': '***'}): BinanceのAPIキーとシークレットキーを使用して、Binance取引所のクラスをインスタンス化する。このクラスはBinanceとの通信を可能にします。
  3. symbol = 'ETH/USDT': 取得したい通貨ペアを指定する。この例ではETH(イーサリアム)とUSDT(Tether)の組み合わせです。
  4. ticker = exchange.fetch_ticker(symbol): 指定された通貨ペアの最新のticker情報を取得する。ticker情報には、現在の価格や過去の取引量などが含まれる。
  5. print(ticker): 取得したticker情報をコンソールに表示する。これにより、最新の価格や取引に関する情報が表示される。
Yodaka

このコードを実行するためには、BinanceのAPIキーとシークレットキーが必要です。

fetch_marketsメソッドを使用してマーケット一覧を取得する方法

import ccxt

# 任意の取引所クラスをインスタンス化
exchange = ccxt.binance()  # 例: Binanceを選択

# マーケット一覧を取得
markets = exchange.fetch_markets()

# ETH-USDTのマーケット情報を検索
eth_usdt_market = next((market for market in markets if market['symbol'] == 'ETH/USDT'), None)

if eth_usdt_market:
    # 価格の取得
    ticker = exchange.fetch_ticker(eth_usdt_market['symbol'])
    print(ticker)
else:
    print("ETH/USDT market not found.")

この方法では、fetch_tickerメソッドにsymbol引数を指定して、ETH-USDTの価格情報を取得します。取引所によっては通貨ペアの表記が異なることがあるため、適切な通貨ペアを指定する必要があります。

load_marketsメソッドを使用してmarketsディクショナリを取得する方法

import ccxt

# 任意の取引所クラスをインスタンス化
exchange = ccxt.binance()  # 例: Binanceを選択

# マーケット情報をロード
markets = exchange.load_markets()

# ETH-USDTのマーケット情報を取得
eth_usdt_market = markets['ETH/USDT']

# 価格の取得
ticker = exchange.fetch_ticker(eth_usdt_market['symbol'])

# 結果の表示
print(ticker)

この方法では、load_marketsメソッドを使用して取引所のマーケット情報をロードし、ETH-USDTのマーケット情報を取得します。その後、取引所のfetch_tickerメソッドに適切なsymbolを渡して価格を取得します。

fetch_tickersメソッドを使用して全ての価格情報を取得する方法

import ccxt

# 任意の取引所クラスをインスタンス化
exchange = ccxt.binance()  # 例: Binanceを選択

# 全ての価格情報を取得
tickers = exchange.fetch_tickers()

# ETH-USDTの価格情報を取得
eth_usdt_ticker = tickers['ETH/USDT']

# 結果の表示
print(eth_usdt_ticker)

この方法では、fetch_tickersメソッドを使用して全ての通貨ペアの価格情報を取得し、その中からETH-USDTの価格情報を取り出します。

fetch_order_bookメソッドを使用して板情報を取得する方法

import ccxt

# 任意の取引所クラスをインスタンス化
exchange = ccxt.binance()  # 例: Binanceを選択

# ETH-USDTの板情報を取得
order_book = exchange.fetch_order_book('ETH/USDT')

# 最良の買い気配と売り気配の価格を表示
print("Best Bid Price:", order_book['bids'][0][0])
print("Best Ask Price:", order_book['asks'][0][0])

この方法では、fetch_order_bookメソッドを使用してETH-USDTの板情報を取得し、最良の買い気配価格と売り気配価格を表示します。

fetchOHLCVメソッドを使用してローソク足データを取得する方法

#fetchOHLCVメソッドを使用してローソク足データを取得する方法
import ccxt
from datetime import datetime

# 任意の取引所クラスをインスタンス化
exchange = ccxt.binance()  # 例: Binanceを選択

# ETH-USDTのローソク足データを取得
ohlcv = exchange.fetch_ohlcv('ETH/USDT', timeframe='1h', limit=1)

if ohlcv:
    # 最新のローソク足データを表示
    latest_candle = ohlcv[0]
    timestamp, open, high, low, close, volume = latest_candle
    date = datetime.fromtimestamp(timestamp / 1000.0)

    # 各項目を表示
    print(f"Timestamp: {timestamp}")
    print(f"Date: {date}")
    print(f"Open Price: {open}")
    print(f"High Price: {high}")
    print(f"Low Price: {low}")
    print(f"Close Price: {close}")
    print(f"Volume: {volume}")
else:
    print("No OHLCV data available.")

この方法では、fetch_ohlcvメソッドを使用してETH-USDTの指定した時間枠のローソク足データを取得します。上記の例では1時間足の最新のローソク足データを表示しています。

参考箇所

CCXTドキュメント:Public API

まとめ

C級Botterになろう」の例題で「任意の取引所の現物価格を取得する」というものに取り組んでいる時に「何をもって(現在の)価格とするのだろう?」という疑問があったのですが解決しました。

執行する戦略によって答えが変わるのですね。

分類できてスッキリしました。

また、ChatGPTにコードを書かせてその根拠を公式ドキュメントから探すという方法も有効だと気が付きました。

この調子で学習を進めていきます。

【宿題】

①やり方が分からなかったら、とりあえずChatGPTにコードを書かせてその根拠を公式ドキュメントから探す
②もう一度自分の手でコードを書く

-Bot, プログラミングスキル, 書籍・論文・web記事