2023-11-12

クロス取引を防止するための入札前処理

python

自動取引

先日構築した自動取引プログラムを動かしていたら、少量だが意図せずクロス取引が発生して証券会社から注意を受けた。 そもそもクロス取引というものを正しく理解できていなかったので調査した。

https://www.jpx.co.jp/regulation/preventing/manipulation/index.html#heading_17

JPXのサイトに詳しく記載があった。 クロス取引の禁止自体は金融商品取引法の159条に記載があるようだ。 売買を活発に見せる対等売買、 売り買い不均衡な対等売買あたりがクロス取引関連の説明になっている。 クロス取引を行うことによって売買高を増加させたり株価を意図的に操作できてしまうため相場操縦に該当する可能性がある。

クロス取引は証券会社によっては注文段階で弾いてくれるようだが、今使っている証券会社はそのような安全装置を設けてはいないようだ。

発生した状況

寄りで前日までのポジションを解消する反対取引と、新規買いを同時に行う取引ロジックを動かしていた。(毎日の計算でスコアの良かったN銘柄をロングで持ち続けるイメージ) このロジックだと複数日に渡って同じ銘柄を持ち続けたい場合に、都度寄りで1回売って再度買うという挙動になるためクロス取引が発生してしまう。 そのため複数日持ち続ける場合には事前にクロス取引を回避するため、事前に不要な売買をキャンセルする必要がある。

例えば前日にある銘柄を100株持っていて翌日は200株持ちたいときには、差分の100だけ新規買いを行う。 ただ真剣にこの問題を考えると考慮するパターンが多くややめんどくさい。 おおよそ以下の8種類の取引パターンがあるのと、前日・翌日のポジションが0だった場合のエッジケースを考える必要がある。

想定されるパターン

実装

頭いい人がちゃんと考えればもしかしたらもっと簡潔にかけるのかもしれないが、変な小細工してバグらせるもの嫌なのでナイーブに実装した。 怒涛の条件分岐で読みにくいがやっていることは表にまとめたものを素直に実装しているだけだ。

引数には前日のポジションと翌日の目標ポジションを与える。これらの値から必要な反対取引、新規取引の量を計算して返してくれる。 今更だが前日を基準にしてロジック作るより、翌日を基準に条件分岐したほうがシンプルな気がしてきた。

python
Copied!
def cancel_cross_trading(n1_position: dict[str, int], n0_position: dict[str, int]) -> [dict[str, int], dict[str, int]]:
    """
    Args:
        n1_position (dict[str, int]): 前日のポジション
        n0_position (dict[str, int]): 翌日のポジション
    Returns:
        tuple[dict[str, int], dict[str, int]]: (counter_order, new_order)
    """

    counter_orders: dict[str, int] = {}
    new_orders: dict[str, int] = {}
    symbols = set(n1_position.keys()) | set(n0_position.keys())

    for symbol in symbols:
        amount_n0 = n0_position.get(symbol, 0)
        amount_n1 = n1_position.get(symbol, 0)

        if amount_n1 == 0:
            new_orders[symbol] = amount_n0
            continue
        if amount_n1 < 0:
            # 前日がショートポジションの場合
            if amount_n0 == amount_n1:
                continue
            if amount_n0 < amount_n1:
                new_orders[symbol] = amount_n0 - amount_n1
                continue
            if amount_n0 > amount_n1 and amount_n0 <= 0:
                counter_orders[symbol] = amount_n0 - amount_n1
                continue
            if amount_n0 > amount_n1 and amount_n0 > 0:
                counter_orders[symbol] = -amount_n1
                new_orders[symbol] = amount_n0
                continue
        if amount_n1 > 0:
            # 前日がロングポジションの場合
            if amount_n0 == amount_n1:
                continue
            if amount_n0 > amount_n1:
                new_orders[symbol] = amount_n0 - amount_n1
                continue
            if amount_n0 < amount_n1 and amount_n0 >= 0:
                counter_orders[symbol] = amount_n0 - amount_n1
                continue
            if amount_n0 < amount_n1 and amount_n0 < 0:
                counter_orders[symbol] = -amount_n1
                new_orders[symbol] = amount_n0
                continue
    return counter_orders, new_orders

まとめ

ちゃんと相場操縦にならないよう対策しておかないと悪意無く不正な取引をしてしまう可能性があるので怖い。 現状のロジックだと見せ玉まわりは問題なさそうだが、指値を多用するロジックを使う場合には指摘される可能性があるので注意したい。