0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

kabuステーションAPIの発注エラーを「リトライしてよいか」で分類する(実運用で踏んだコードつき)

0
Posted at

この記事は、個人で作っている自動売買ボットでのエラー処理の考え方の記録です。分類は私の判断で、証券会社の公式な見解ではありません。特定の銘柄や売買手法をすすめるものでもありません。

はじめに

kabuステーションAPIで発注すると、失敗したときにエラーコードが返ってきます。コードの一覧は公式のドキュメントにあります。

ただ、自動売買のボットを書くときに本当に知りたいのは、コードの意味そのものより、**「このエラーが返ってきたら、ボットはどうすればいいのか」**です。同じ注文をすぐ出し直していいのか、数量を変えるべきか、待つべきか、人が直すまで止めるべきか。

この記事では、公式の一覧のうち、現物株を成行で売買するボットに関係するものを、「リトライしてよいか」で6つに分類します。あわせて、私が実際に運用で踏んだエラーと、その結果どうなったかも書いておきます。

コードの一覧は 2026年10月時点の公式リポジトリ(kabucom/kabusapi)の内容をもとにしています。各コードの正確な定義は、必ず公式の一覧で確認してください。

先に一番大事なこと:発注の失敗は HTTP 500 で返ってくることがある

私の環境では、余力不足(Code=21)で注文が断られたとき、HTTP のステータスは 500 でした。

sendorder 5xx: 500 body={"Code":21,"Message":"可能額が不足しております。ご注文内容をご確認ください"}

HTTP 500 と聞くと「サーバー側の一時的なエラーだから、リトライすればいい」と考えがちです。ですが、発注の API でこれをやると危険です。

  • 中身が Code=21 なら、何度出し直しても同じ理由で断られる
  • 中身のない本当のサーバーエラーなら、注文が実は通っている可能性がある。そのまま出し直すと、二重に発注してしまう

なので、発注の API でエラーが返ってきたら、HTTP のステータスより先に、本文の Code を見るのが出発点です。

6つの分類

分類 ボットの動き 代表的なコード
① 設定・パラメータの誤り 止めて人に知らせる。 何度出しても同じ 15, 16, 17, 18, 19, 100001, 100031, 4001005, 4002007〜4002017
② 口座の手続き・権限 止めて人に知らせる。 証券会社側の手続きが要る 2, 32, 100202, 100203, 100204
③ 余力・上限 条件を変えるか、その回は見送る 21, 100051, 100294
④ 混雑・回数制限 少し待ってから出し直す HTTP 429, 4001006
⑤ 認証 トークンを取り直して1回だけ出し直す。 だめなら止める HTTP 401, 4001007, 4001009, 4001013, 4001017
⑥ 結果がわからない 出し直す前に、注文と建玉を照会する 本文のない HTTP 500、タイムアウト、-1

ここからは、それぞれの分類について、なぜそう扱うのかを書きます。

① 設定・パラメータの誤り:止めて人に知らせる

取引の単位、呼値(値段の刻み)、値幅の制限、受渡しの方法、預り区分など、注文の中身が間違っているものです。代表的なのは次のコードです。

  • 15(取引数量単位)、16(取引数量)、17(呼値)、18(制限値幅)、19(取引値段)
  • 100001(受渡方法)、100031(預り区分)
  • 4001005(パラメータ変換)、4002007〜4002017(各パラメータの不正)

コードを直さない限り、同じ注文は何度出しても同じエラーになります。リトライはせず、Discord などで知らせて、その注文は打ち切ります。

実際に踏んだもの:現物買いの組み合わせを実注文で試したとき、私の口座では次のようになりました。

試した値 結果
FundType="AA", DelivType=2 通った
FundType="02" 預り区分のエラー(100031)
DelivType=0 パラメータ変換のエラー
DelivType=3 auマネーコネクト未契約のエラー

どの組み合わせが通るかは、口座の設定(kabuステーションの「システム設定」の「注文」の設定など)によって変わる可能性があります。公式の一覧でも、4001005 は「kabuステーションの設定と相違がないか確認」とされています。

② 口座の手続き・権限:止めて人に知らせる

口座番号、口座の種別、同意書の手続きなど、証券会社の側で手続きをしないと直らないものです。100202(約諾書未読)は、最良執行市場の同意書や信用取引の同意書など、必要な手続きをしていないときに返ってきます。

実際に踏んだもの:最初に実弾を動かしたとき、信用取引の同意、アルゴ取引の同意、発注先の市場の指定で、3つ続けて詰まりました(そのときのエラーコードは、手元の記録からは確認できていません)。市場の指定については、公式の一覧に「市場に 1(東証)を指定して現物を発注しようとした場合」のエラー(100378)があり、私のボットでは発注のときだけ市場を 9(SOR)にしています(板情報の照会は @1 のまま)。

どれも、ボットの中では直せません。止めて知らせるだけにします。

③ 余力・上限:条件を変えるか、見送る

21(可能額不正=余力不足)、100051(ハードリミット)、100294(注文上限)などです。同じ注文を出し直しても通りませんが、数量を減らせば通ることがあります。

実際に踏んだもの:ここがいちばん苦労したところです。

  • 口座の余力の数字には、受渡し前の拘束が反映されていないことがあった。数字の上では足りているのに Code=21 になった
  • 成行の注文では、余力の満額までは買えなかった。私の口座では、余力の 81.8% の注文は通り、85.6% の注文は Code=21 になった

対策として「Code=21 なら100株ずつ減らして出し直す」処理を入れたところ、今度は、もとからあった「5秒以内の連続発注を止める」安全装置にぶつかって、出し直しが1回も送られていませんでした。この話は別の記事に書いています。

数量を減らして出し直すなら、ほかのリトライ制御と一緒に動かして確かめることをおすすめします。

④ 混雑・回数制限:少し待ってから出し直す

HTTP 429(スロットリング)や 4001006(API の実行回数)です。注文の中身には問題がないので、少し間をおけば通ります。

私のボットでは、板情報の取得で 429 が返ってきたら2秒待って次の回に回し、疎通確認では3秒おきに最大3回まで出し直しています。発注で返ってきた場合も、同じように数秒待ってから出し直すのがよいと考えています。

⑤ 認証:トークンを取り直して1回だけ

HTTP 401 や、4001007・4001017(kabuステーションにログインしていない)、4001009(API キーの不一致)、4001013(API パスワードの不正)です。

  • トークンが期限切れになっただけなら、取り直せば通る。1回だけ取り直して出し直す
  • 取り直してもだめなら、kabuステーションが起動していない、ログインしていない、パスワードが違う、のどれか。ボットの中では直せないので止める

実際に踏んだもの:朝の疎通確認が、前日のトークンを使い回していて、毎朝 401 で失敗していました。トークンをファイルから読むとき、取得からの経過時間を確かめずに使っていたのが原因で、取得時刻を見て古ければ取り直す処理を通すように直しました。

⑥ 結果がわからない:出し直す前に照会する

本文に Code のない HTTP 500、タイムアウト、-1(異常終了)などです。一番危ないのはここで、注文が証券会社に届いて約定している可能性があります。

このときは、すぐに出し直さず、まず建玉(または注文)を照会して、通っているかを確かめます。

# 発注で HTTP 5xx が返ってきたときの流れ(抜粋・簡略化)
if 500 <= r.status_code < 600:
    code, msg = parse_error_body(r.text)          # 本文の Code / Message
    if code in NON_RETRYABLE_ORDER_CODES:          # ①② など:出し直しても無駄
        notify(f"🚨 注文拒否(リトライ不可) {symbol} Code={code} {msg}")
        return False

    # 結果がわからない → まず建玉を照会する(実は通っているかもしれない)
    time.sleep(1)
    qty, avg_price = get_live_position(symbol)
    if qty > 0:
        return True                                # 通っていた。出し直さない

    # 通っていないことを確かめてから、トークンを取り直して1回だけ出し直す
    refresh_token()
    r2 = send_order(body)
    ...
# リトライしても無駄なコード(私のボットの設定)
NON_RETRYABLE_ORDER_CODES = {"15", "16", "17", "18", "19", "100031"}
# 21(余力不足)は「数量を減らして出し直す」処理に回すので、ここには入れていない

「確かめてから出し直す」を守っていれば、二重発注はかなり防げます。逆に言えば、照会の API がそもそも失敗しているとき(中継サーバーに届かない、など)は、出し直さずに止めるのが安全です。

コピーして使える分類(Python)

上の6分類を、そのまま関数にしたものです。コードは文字列で比べています。自分のボットで使う注文の種類に合わせて、公式の一覧と照らしながら足し引きしてください。

# 発注エラーの分類(現物・成行のボット向け)
# 各コードの定義は公式の一覧で確認すること:
# https://kabucom.github.io/kabusapi/ptal/error.html

STOP_AND_NOTIFY = {      # ①② 設定・パラメータ・口座の手続き。出し直しても無駄
    "2", "11", "15", "16", "17", "18", "19", "32",
    "100001", "100031", "100202", "100203", "100204", "100368", "100378",
    "4001005", "4002007", "4002008", "4002009", "4002010", "4002011",
    "4002012", "4002013", "4002014", "4002016", "4002017",
}
REDUCE_OR_SKIP = {"21", "100051", "100294"}                     # ③ 余力・上限
WAIT_AND_RETRY = {"4001006"}                                    # ④ 回数制限(HTTP 429 も)
REAUTH_ONCE = {"4001007", "4001009", "4001013", "4001017"}      # ⑤ 認証(HTTP 401 も)


def classify_order_error(http_status, code):
    """発注 API のエラーを、ボットがとるべき行動に分類する。

    戻り値:
      "wait_retry"     少し待ってから出し直す
      "reauth_once"    トークンを取り直して1回だけ出し直す。だめなら止める
      "stop"           止めて人に知らせる
      "reduce_or_skip" 数量を減らすか、その回は見送る
      "check_first"    結果不明。出し直す前に注文・建玉を照会する
    """
    code = str(code) if code is not None else ""
    if http_status == 429 or code in WAIT_AND_RETRY:
        return "wait_retry"
    if http_status == 401 or code in REAUTH_ONCE:
        return "reauth_once"
    if code in STOP_AND_NOTIFY:
        return "stop"
    if code in REDUCE_OR_SKIP:
        return "reduce_or_skip"
    if code in ("", "-1"):
        return "check_first"      # ⑥ 本文に Code がない 5xx、タイムアウト、異常終了
    return "stop"                 # 知らないコードは、止める側に倒す

使う側は、戻り値ごとに処理を分けるだけです。

action = classify_order_error(r.status_code, err.get("Code"))
if action == "check_first":
    qty, _ = get_live_position(symbol)
    if qty > 0:
        return "filled"          # 実は通っていた。出し直さない

分類をコードに落とすときの注意

1. 分からないコードは「止める」側に倒す。
公式の一覧にも「記載以外のコードが返却された場合はお問い合わせください」とあります。知らないコードが返ってきたら、リトライではなく、止めて知らせるのが安全です。

2. メッセージの文字列だけで判定しない。
私のボットには今も「余力不足」という文字列での判定が残っていますが、文言は変わることがあります。判定は Code を主にして、メッセージは通知に載せるだけにします。

3. 「リトライ不可」の一覧を、ほかのリトライ処理と一緒に見直す。
Code=21 を「リトライ不可」から外して、数量を減らして出し直すようにしたとき、別の安全装置とぶつかりました。エラーの分類を変えたら、その先の処理が既存の制御とどう重なるかも確かめる必要があります。

おわりに

エラーコードの一覧は「意味」を教えてくれますが、「ボットがどうするべきか」は教えてくれません。私は、同じ注文を出し直して意味があるか、出し直す前に確かめることがあるか、の2つで分けるようになってから、エラー処理で迷うことが減りました。

この分類は、現物株を成行で売買する、私の小さなボットでの判断です。信用取引や先物、指値・逆指値を使う場合は、関係するコードも扱いも変わるはずです。公式の一覧と照らし合わせながら、自分のボットに合わせて組み替えてください。


書いた人

会社員のかたわら、Raspberry Pi で日本株の自動売買システムを作って運用しています。検証や実装で失敗した話を中心に書いています。Zenn では「perkin」として、バックテストと検証の話を書いています。

ほかに、暮らしのお金を計算するサイト「いくらかかるの研究所」も個人開発しています。エアコンの年間の電気代を、機種・部屋・地域・住宅の性能から計算するシミュレーターなどを置いています(Astro + Cloudflare 製)。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?