仮想通貨取引において、クオンツ(量的)戦略は広く議論されているトピックです。取引プロセスを自動化・システム化することで、トレーダーはより効率的に取引を実行し、市場の変動にも素早く対応することができます。この記事では、仮想通貨の文脈における高頻度取引(HFT)戦略を紹介します。

HFTは、超高速な注文執行と買値・売値のスプレッド(差)を利用して利益を得ることを目的としており、仮想通貨の世界でも最も注目されている戦略の一つです。本記事では、いくつかの一般的な高頻度取引戦略を取り上げ、理解と実装を助けるためのコード例も紹介します。

1. マーケットメイキング戦略

マーケットメイキングは、高頻度取引戦略の一種であり、流動性を提供することで利益を上げることを目的としています。マーケットメイカーは、買い注文と売り注文を同時に出すことで市場の取引を活性化させ、買値と売値のスプレッドから利益を得ます。

マーケットメイキングの核となる考え方は、買い手と売り手の間に立って両サイドの価格を提示し、取引を成立させる「橋渡し」の役割を果たすことです。マーケットメイカーは、流動性を保ち、スプレッドからの利益を最大化するために、注文価格や数量を頻繁に調整します。

主な特徴:

  • 高速な注文執行:マーケットメイカーは、価格や注文量を迅速に調整できる必要があります。
  • 高い自動化:マーケットメイキング戦略は通常、アルゴリズムに基づき高い自動化がされています。
  • スプレッドによる利益:買値と売値の差から利益を得ます。

実装ステップ:

  1. 市場価格の取得:市場データを監視して最新の買値・売値を取得します。
  2. 価格戦略の設定:市場状況や戦略パラメータに基づいて、買値・売値を設定します。
  3. 注文の出し入れ:算出された価格と数量に基づいて注文を市場に出します。
  4. 監視と調整:市場の変化を継続的に追跡し、必要に応じて注文を調整します。

1.1 マーケットメイキング戦略のJavaコード例

import java.util.Random;

public class MarketMakerStrategy {
    private double buyPrice; 
    private double sellPrice; 
    private double spread; 
    private double midPrice; 
    private double minSpread; 
    private double maxSpread; 
    private double minQty; 
    private double maxQty; 
    private Random random;

    public MarketMakerStrategy(double initialBuyPrice, double initialSellPrice, double minSpread, double maxSpread, double minQty, double maxQty) {
        this.buyPrice = initialBuyPrice;
        this.sellPrice = initialSellPrice;
        this.minSpread = minSpread;
        this.maxSpread = maxSpread;
        this.minQty = minQty;
        this.maxQty = maxQty;
        this.random = new Random();
        updateMidPrice();
        updateSpread();
    }

    private void updateMidPrice() {
        midPrice = (buyPrice + sellPrice) / 2;
    }

    private void updateSpread() {
        spread = random.nextDouble() * (maxSpread - minSpread) + minSpread;
    }

    private double generateQty() {
        return random.nextDouble() * (maxQty - minQty) + minQty;
    }

    public void updateBuyPrice(double newBuyPrice) {
        buyPrice = newBuyPrice;
        updateMidPrice();
        updateSpread();
    }

    public void updateSellPrice(double newSellPrice) {
        sellPrice = newSellPrice;
        updateMidPrice();
        updateSpread();
    }

    public void generateBuyOrder() {
        double buyQty = generateQty();
        double buyOrderPrice = midPrice - spread / 2; 
        System.out.println("Generated Buy Order - Price: " + buyOrderPrice + ", Quantity: " + buyQty);
    }

    public void generateSellOrder() {
        double sellQty = generateQty();
        double sellOrderPrice = midPrice + spread / 2; 
        System.out.println("Generated Sell Order - Price: " + sellOrderPrice + ", Quantity: " + sellQty);
    }

    public static void main(String[] args) {
        MarketMakerStrategy strategy = new MarketMakerStrategy(100, 102, 0.5, 1.5, 10, 50);

        for (int i = 0; i < 10; i++) {
            double newBuyPrice = strategy.buyPrice + (strategy.random.nextDouble() - 0.5) * 2;
            double newSellPrice = strategy.sellPrice + (strategy.random.nextDouble() - 0.5) * 2;
            strategy.updateBuyPrice(newBuyPrice);
            strategy.updateSellPrice(newSellPrice);
            strategy.generateBuyOrder();
            strategy.generateSellOrder();
        }
    }
}

1.2 マーケットメイキング戦略のPythonコード例

import random

class MarketMakerStrategy:
    def __init__(self, initial_buy_price, initial_sell_price, min_spread, max_spread, min_qty, max_qty):
        self.buy_price = initial_buy_price
        self.sell_price = initial_sell_price
        self.min_spread = min_spread
        self.max_spread = max_spread
        self.min_qty = min_qty
        self.max_qty = max_qty
        self.random = random.Random()
        self.mid_price = (self.buy_price + self.sell_price) / 2
        self.spread = self.random.uniform(self.min_spread, self.max_spread)

    def update_prices(self, new_buy_price, new_sell_price):
        self.buy_price = new_buy_price
        self.sell_price = new_sell_price
        self.mid_price = (self.buy_price + self.sell_price) / 2
        self.spread = self.random.uniform(self.min_spread, self.max_spread)

    def generate_buy_order(self):
        buy_qty = self.random.uniform(self.min_qty, self.max_qty)
        buy_order_price = self.mid_price - self.spread / 2
        print(f"Generated Buy Order - Price: {buy_order_price}, Quantity: {buy_qty}")

    def generate_sell_order(self):
        sell_qty = self.random.uniform(self.min_qty, self.max_qty)
        sell_order_price = self.mid_price + self.spread / 2
        print(f"Generated Sell Order - Price: {sell_order_price}, Quantity: {sell_qty}")

strategy = MarketMakerStrategy(100, 102, 0.5, 1.5, 10, 50)

for i in range(10):
    new_buy_price = strategy.buy_price + (strategy.random.random() - 0.5) * 2
    new_sell_price = strategy.sell_price + (strategy.random.random() - 0.5) * 2
    strategy.update_prices(new_buy_price, new_sell_price)
    strategy.generate_buy_order()
    strategy.generate_sell_order()

2. アービトラージ取引(裁定取引)

アービトラージ取引とは、異なる取引所や通貨ペア間の価格差を利用して利益を得る手法です。仮想通貨市場では、複数の取引所間で価格にばらつきが生じることがよくあり、この差を利用して「安く買って高く売る」ことが可能です。

実装ステップ:

  1. 価格データの初期化:2つの取引所の価格データをシミュレーションします。実際の運用では、取引所APIからリアルタイムデータを取得し、Mapなどのデータ構造に保存します。
  2. 価格の比較:各仮想通貨について、2つの取引所間で価格差があるかをチェックします。
  3. アービトラージの機会を計算:あるコインが取引所Aで安く、取引所Bで高い場合、理論上の利益を計算し、レポートします。

実行プロセス:

  • 取引所Aのデータをループし、同じ資産が取引所Bにも存在するか確認します。
  • 存在する場合、価格差(取引所Bの価格 − 取引所Aの価格)を計算します。
  • 差が正(利益が出る)であれば、そのコイン名、両取引所の価格、想定される利益を出力します。

2.1 アービトラージ取引のJavaコード例

import java.util.HashMap;
import java.util.Map;

public class ArbitrageTradingStrategy {
    private Map<String, Double> exchangeA; 
    private Map<String, Double> exchangeB; 

    public ArbitrageTradingStrategy() {
        this.exchangeA = new HashMap<>();
        this.exchangeB = new HashMap<>();
        exchangeA.put("BTC", 10000.0);
        exchangeA.put("ETH", 500.0);
        exchangeB.put("BTC", 10100.0);
        exchangeB.put("ETH", 510.0);
    }

    public void executeArbitrage() {
        for (String currency : exchangeA.keySet()) {
            if (exchangeB.containsKey(currency)) {
                double priceDifference = exchangeB.get(currency) - exchangeA.get(currency);
                if (priceDifference > 0) {
                    System.out.println("Arbitrage Opportunity: Buy " + currency + " on Exchange A, Sell on Exchange B. Profit: " + priceDifference);
                }
            }
        }
    }

    public static void main(String[] args) {
        ArbitrageTradingStrategy strategy = new ArbitrageTradingStrategy();
        strategy.executeArbitrage();
    }
}

2.2 アービトラージ取引のPythonコード例

import java.util.HashMap;
import java.util.Map;

public class ArbitrageTradingStrategy {
    private Map<String, Double> exchangeA;
    private Map<String, Double> exchangeB;

    public ArbitrageTradingStrategy() {
        this.exchangeA = new HashMap<>();
        this.exchangeB = new HashMap<>();
        exchangeA.put("BTC", 10000.0);
        exchangeA.put("ETH", 500.0);
        exchangeB.put("BTC", 10100.0);
        exchangeB.put("ETH", 510.0);
    }

    public void executeArbitrage() {
        for (String currency : exchangeA.keySet()) {
            if (exchangeB.containsKey(currency)) {
                double priceDifference = exchangeB.get(currency) - exchangeA.get(currency);
                if (priceDifference > 0) {
                    System.out.println("Arbitrage Opportunity: Buy " + currency + " on Exchange A, Sell on Exchange B. Profit: " + priceDifference);
                }
            }
        }
    }

    public static void main(String[] args) {
        ArbitrageTradingStrategy strategy = new ArbitrageTradingStrategy();
        strategy.executeArbitrage();
    }
}

アービトラージ取引戦略クラス(ArbitrageTradingStrategy)では、2つの取引所における価格データを初期化し、execute_arbitrage メソッドを用いて利益の出るアービトラージ機会があるかどうかをチェックします。

3. 板情報のアンバランス(不均衡)監視

板情報のアンバランス監視とは、買い注文と売り注文の量を監視し、その不均衡に基づいて売買を行う戦略です。たとえば、買い注文が売り注文を大きく上回っている場合、価格の上昇が予測されるため、買い注文を出す判断材料となります。

3.1 Javaコード例

import java.util.Random;

public class OrderBookImbalanceTrackingStrategy {
    private double buyOrders;
    private double sellOrders;
    private double imbalanceThreshold;
    private Random random;

    public OrderBookImbalanceTrackingStrategy(double imbalanceThreshold) {
        this.imbalanceThreshold = imbalanceThreshold;
        this.random = new Random();
        this.buyOrders = random.nextDouble() * 100;
        this.sellOrders = random.nextDouble() * 100;
    }

    public void updateOrderBook() {
        this.buyOrders = random.nextDouble() * 100;
        this.sellOrders = random.nextDouble() * 100;
        checkImbalance();
    }

    private void checkImbalance() {
        if (buyOrders > sellOrders + imbalanceThreshold) {
            System.out.println("Imbalance detected: Buy orders significantly higher than sell orders. Execute buy trade.");
        } else if (sellOrders > buyOrders + imbalanceThreshold) {
            System.out.println("Imbalance detected: Sell orders significantly higher than buy orders. Execute sell trade.");
        } else {
            System.out.println("Order book is balanced. No trade execution needed.");
        }
    }

    public static void main(String[] args) {
        OrderBookImbalanceTrackingStrategy strategy = new OrderBookImbalanceTrackingStrategy(10);

        for (int i = 0; i < 5; i++) {
            strategy.updateOrderBook();
        }
    }
}

実装ステップ:

  1. パラメータの初期化:コンストラクタ内でアンバランスのしきい値を設定します。これにより、板が「不均衡」とみなされる基準を決めます。
  2. 板情報の更新をシミュレートupdateOrderBook メソッドを使用して板情報の更新をシミュレートします。実際の取引では、取引所のAPIやWebSocketなどからデータを取得します。
  3. アンバランスのチェックcheckImbalance メソッドにより、買い注文と売り注文のボリュームを比較します。買い注文が大きく上回っていれば「買い」シグナル、逆に売り注文が多ければ「売り」シグナルとなります。
  4. 取引の実行:検出されたアンバランスに応じて売買を行います。この簡易的な例では、実際の注文ではなくメッセージの出力にとどめています。
  5. 繰り返し処理:板情報を継続的に監視・更新し、メインループ内で不均衡のチェックを繰り返します。

3.2 Pythonコード例

import random

class OrderBookImbalanceTrackingStrategy:
    def __init__(self, imbalance_threshold):
        self.imbalance_threshold = imbalance_threshold
        self.random = random.Random()
        self.buy_orders = self.random.uniform(50, 100)
        self.sell_orders = self.random.uniform(50, 100) 


    def update_order_book(self):
        self.buy_orders = self.random.uniform(50, 100) 
        self.sell_orders = self.random.uniform(50, 100) 
        self.check_imbalance()

    def check_imbalance(self):
        if self.buy_orders > self.sell_orders + self.imbalance_threshold:
            print("Imbalance detected: Buy orders significantly higher than sell orders. Execute buy trade.")
        elif self.sell_orders > self.buy_orders + self.imbalance_threshold:
            print("Imbalance detected: Sell orders significantly higher than buy orders. Execute sell trade.")
        else:
            print("Order book is balanced. No trade execution needed.")

strategy = OrderBookImbalanceTrackingStrategy(10)  

for i in range(5):
    strategy.update_order_book()

4. テクニカル指標に基づく取引戦略

この戦略では、移動平均(MA)や相対力指数(RSI)などのテクニカル指標を用いて価格パターンを特定し、取引の意思決定を行います。これらの指標に急激な変化が見られた場合、迅速な売買を行うきっかけとなります。

実装ステップ:

  1. 価格データの準備
     過去の価格データ(例:Alltick APIを通じて取得)を配列に保存し、各種指標の計算に使用します。
  2. 移動平均の計算
     スライディングウィンドウを使って価格データを平滑化し、トレンドを識別します。各タイムステップにおける平均価格を算出します。
  3. RSIの計算
     RSIは、価格変動の速度と大きさを測定する指標で、一定期間内の上昇と下落をもとに計算されます。
  4. 取引ルールの定義
     テクニカル指標の値に基づいて売買ルールを作成します。例えば、移動平均のクロスオーバーや、RSIの買われすぎ/売られすぎの水準などです。
  5. 売買の実行
     定義した取引ルールに従って、APIを通じて売買を実行します。手数料、注文量、取引頻度、リスク管理なども考慮する必要があります。
  6. 監視と調整
     市場の動向や取引の実行状況を常時監視し、結果と市場状況に応じて戦略を調整します。

4.1 テクニカル指標のJavaコード例

public class TechnicalIndicatorTradingStrategy {
    private double[] priceData;
    private int windowSize; 
    private double[] movingAverage; 
    private double[] rsi;

    public TechnicalIndicatorTradingStrategy(double[] priceData, int windowSize) {
        this.priceData = priceData;
        this.windowSize = windowSize;
        this.movingAverage = calculateMovingAverage();
        this.rsi = calculateRSI();
    }

    private double[] calculateMovingAverage() {
        double[] ma = new double[priceData.length - windowSize + 1];
        for (int i = 0; i < ma.length; i++) {
            double sum = 0;
            for (int j = i; j < i + windowSize; j++) {
                sum += priceData[j];
            }
            ma[i] = sum / windowSize;
        }
        return ma;
    }

    private double[] calculateRSI() {
        double[] rsi = new double[priceData.length - windowSize + 1];
        double[] priceChange = new double[priceData.length - 1];
        for (int i = 0; i < priceChange.length; i++) {
            priceChange[i] = priceData[i + 1] - priceData[i];
        }
        for (int i = 0; i < rsi.length; i++) {
            double sumGain = 0, sumLoss = 0;
            for (int j = i; j < i + windowSize; j++) {
                if (priceChange[j] > 0) {
                    sumGain += priceChange[j];
                } else {
                    sumLoss -= priceChange[j];
                }
            }
            double avgGain = sumGain / windowSize;
            double avgLoss = sumLoss / windowSize;
            double rs = avgGain / avgLoss;
            rsi[i] = 100 - (100 / (1 + rs));
        }
        return rsi;
    }

    public void executeStrategy() {
    }

    public static void main(String[] args) {
        double[] priceData = {100, 105, 110, 115, 120, 115, 110, 105, 100};
        int windowSize = 3;

        TechnicalIndicatorTradingStrategy strategy = new TechnicalIndicatorTradingStrategy(priceData, windowSize);
        strategy.executeStrategy();
    }
}

4.2 テクニカル指標のPythonコード例

import numpy as np

class TechnicalIndicatorTradingStrategy:
    def __init__(self, price_data, window_size):
        self.price_data = price_data
        self.window_size = window_size
        self.moving_average = self.calculate_moving_average()
        self.rsi = self.calculate_rsi()

    def calculate_moving_average(self):
        moving_average = np.convolve(self.price_data, np.ones(self.window_size) / self.window_size, mode='valid')
        return moving_average

    def calculate_rsi(self):
        deltas = np.diff(self.price_data)
        gain = deltas.copy()
        loss = deltas.copy()
        gain[gain < 0] = 0
        loss[loss > 0] = 0
        avg_gain = np.mean(gain[:self.window_size])
        avg_loss = -np.mean(loss[:self.window_size])
        rsi = np.zeros_like(self.price_data)
        rsi[:self.window_size] = 100. - 100. / (1. + avg_gain / avg_loss)
        for i in range(self.window_size, len(self.price_data)):
            delta = deltas[i - 1]  # price change
            gain_value = max(0, delta)
            loss_value = -min(0, delta)
            avg_gain = (avg_gain * (self.window_size - 1) + gain_value) / self.window_size
            avg_loss = (avg_loss * (self.window_size - 1) + loss_value) / self.window_size
            rs = avg_gain / avg_loss
            rsi[i] = 100. - 100. / (1. + rs)
        return rsi


    def execute_strategy(self):

        pass

price_data = np.array([100, 105, 110, 115, 120, 115, 110, 105, 100])
window_size = 3

strategy = TechnicalIndicatorTradingStrategy(price_data, window_size)
strategy.execute_strategy()