2026年5月3日日曜日

kabuステーションAPIを使って現在のポジション一覧を取得するPythonスクリプト

kabuステーションAPIを使って現在のポジション一覧を取得するPythonスクリプト

概要

前回 kabuステーションAPIを使用して認証トークンを取得しました
今回はポジションの一覧を取得します

環境

  • M2 Pro Mac mini
  • macOS 26.4.1
  • UTM 4.7.5
    • Windows11 Home
  • kabuステーション 5.40.0.0
  • nginx 1.30.0
  • Python 3.12.13

ポジションの一覧を取得する Python スクリプト

  • vim list_positions.py
import requests

# =========================
# 設定
# =========================
API_PASSWORD = "xxx"  # kabuステーションで設定したAPIパスワード
BASE_URL = "http://192.168.65.9:28081/kabusapi"  # 検証環境


# =========================
# トークン取得
# =========================
def get_token():
    url = f"{BASE_URL}/token"

    body = {"APIPassword": API_PASSWORD}

    res = requests.post(url, json=body)

    if res.status_code != 200:
        print("トークン取得エラー:", res.status_code, res.text)
        return None

    return res.json().get("Token")


# =========================
# ポジション取得
# =========================
def get_positions(token):
    url = f"{BASE_URL}/positions"

    headers = {"X-API-KEY": token}

    res = requests.get(url, headers=headers)

    # トークン期限切れ対応
    if res.status_code == 401:
        print("トークン期限切れ → 再取得")
        token = get_token()
        if not token:
            return None

        headers["X-API-KEY"] = token
        res = requests.get(url, headers=headers)

    if res.status_code != 200:
        print("ポジション取得エラー:", res.status_code, res.text)
        return None

    return res.json()


# =========================
# メイン処理
# =========================
def main():
    token = get_token()

    if not token:
        print("トークン取得失敗")
        return

    positions = get_positions(token)

    if not positions:
        print("ポジションなし or 取得失敗")
        return

    print("=== 保有ポジション ===")
    for p in positions:
        print(f"""
銘柄: {p.get('Symbol')}
数量: {p.get('LeavesQty')}
取得単価: {p.get('Price')}
現在値: {p.get('CurrentPrice')}
評価損益: {p.get('ProfitLoss')}
        """)


# =========================
if __name__ == "__main__":
    main()
  • uv run python list_positions.py

最後に

次回は実際に売買する方法を紹介します

2026年5月2日土曜日

kabuステーションAPIをコールして認証トークンを取得する

kabuステーションAPIをコールして認証トークンを取得する

概要

前回 kabuステーションをWindowsのVM上に構築しました
今回は実際に API をコールしトークンを取得します

環境

  • M2 Pro Mac mini
  • macOS 26.4.1
  • UTM 4.7.5
    • Windows11 Home
  • kabuステーション 5.40.0.0
  • nginx 1.30.0
  • Python 3.12.13

Windows 上に nginx を構築

Python スクリプトを Mac 上からコールするので kabuステーションが外部からアクセスできるように nginx を構築します

Windows 上で直接 Python スクリプトを実行してもいいですがいろいろとハマるので Mac 上から実行します

worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    map $http_upgrade $connection_upgrade { 
    default upgrade;
    ''      close;
    } 

    server {
        listen       28081;
        server_name  localhost;

        proxy_http_version 1.1;
        proxy_set_header Host localhost;
        proxy_set_header Upgrade $http_upgrade; 
        proxy_set_header Connection $connection_upgrade;

        location / {
            proxy_pass   http://127.0.0.1:18081/;
        }
    }
}

28081 ポートでアクセスできるようにします
本番の場合は 18080 に proxy します

とりあえずテストなのでバイナリを直接ダブルクリックで実行すれば OK です
起動できているかは localhost:28081/kabusapi にアクセスしてみましょう
停止する場合はタスクマネージャーを使って nginx.exe プロセスを停止します

トークン取得用スクリプト

Windows 上で直接作成しても OK ですし Mac 側で編集/作成して VM 側に共有しても OK です

requests が必要なのでインストールしておきましょう

  • vim fetch_token.py
import requests

# =========================
# 設定
# =========================
API_PASSWORD = "xxx"  # kabuステーションで設定したAPIパスワード
BASE_URL = "http://192.168.65.9:28081/kabusapi"  # 検証環境


# =========================
# トークン取得
# =========================
def get_token():
    url = f"{BASE_URL}/token"

    body = {"APIPassword": API_PASSWORD}

    res = requests.post(url, json=body)

    if res.status_code != 200:
        print("ERROR:", res.status_code, res.text)
        return None

    data = res.json()
    return data.get("Token")


if __name__ == "__main__":
    token = get_token()

    if token:
        print("取得成功:")
        print(token)
    else:
        print("トークン取得失敗")
  • uv run python fetch_token.py

これでトークンが表示されれば OK です
API_PASSWORD は前回 API を有効にした際に設定したパスワードを入力しましょう

最後に

kabu ステーション API を使って API をコールするための認証トークンを取得してみました
kabu ステーション API は Windows 上に構築しスクリプトは Mac や Linux 上からコールするようにしましょう
今回はとりあえずバイナリを直接実行していますが本格的に取引を行う場合は自動起動やプロセス監視/ログ監視が必要になると思います

次回は API を使ってポジションの一覧を取得する方法を紹介します

参考サイト

2026年4月30日木曜日

Mac に kabuステーションAPI環境を構築する

Mac に kabuステーションAPI環境を構築する

概要

まずは構築しAPIを有効にします

環境

  • M2 Pro Mac mini
  • macOS 26.4.1
  • UTM 4.7.5
    • Windows11 Home
  • kabuステーション 5.40.0.0

UTM/CrystalFetch インストール

公式サイトからダウンロードしインストールします

CrystalFetch で Windows11 の ISO イメージ取得

AppleSillicon 上で動作させるのでチェックをオンにします

UTM で Windows11 の VM 作成

  • ライセンスキーがあれば入れる
  • 各種 Windows のセットアップ
  • display output is not active に注意
    • インストール時は「virtio-ramfb-gl (GPU Supported)」 Retinaオン
    • Windowsインストール後は「virtio-grpu-gl-pci (GPU Supported)」Retinaオフ
  • 全画面時の解除はCtrl+Altでマウスのフォーカスを外す

kabuステーションのダウンロードとインストール

https://download.r30.kabu.co.jp/ap/kabustation/ から exe をダウンロードしインストールします

三菱UFJ eスマート証券のアカウントでログイン

口座番号とパスワードでログインします

kabuステーションの設定からAPIを有効化

  • 右上の「歯車」
  • 「システム設定」
  • API タブ
  • 「APIを利用する」にチェック
  • APIパスワードを両方入力
  • kabuステーションを再起動し右上のAPIマークが有効になっていることを確認

動作確認

ブラウザで http://localhost:18081/kabusapi にアクセスしリクエスト不正になることを確認します

最後に

次回は API をコールするためのトークンを取得します

2026年4月21日火曜日

pipenv から uv に移行する

pipenv から uv に移行する

概要

自分のプロジェクトを pipenv から uv に置き換えたので手順を紹介します

環境

  • macOS 26.3.1
  • Python 3.12.13
  • uv 0.11.7

流れ

  1. pyproject.toml の作成
  2. Dockerfile の修正

python 準備

  • cat .python-version
3.12.13

uv インストール

  • pip install uv

pyproject.toml の作成

[project]
name = "ai-gallery"
version = "0.1.0"
description = "AI Gallery application"
requires-python = ">=3.12"
dependencies = [
    "flask",
    "google-cloud-storage",
    "redis",
    "gunicorn",
    "google-genai",
    "pillow",
    "py-vapid",
    "pywebpush",
]

[dependency-groups]
dev = [
    "black",
    "isort",
    "pyright",
    "pytest",
    "bandit",
]

[tool.black]
line-length = 88
target-version = ["py312"]

[tool.isort]
profile = "black"
line_length = 88

[tool.pyright]
pythonVersion = "3.12"

uv sync

  • uv sync

これで uv.lock が作成されます
仮想環境のパスはプロジェクト直下の .venv に作成されます

Dockerfile 修正

pipenv -> uv に変更します

diff --git a/Dockerfile b/Dockerfile
index 499f280..4ec6df7 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,12 +1,16 @@
 FROM python:3.12.13-alpine3.23
 
-COPY . /home
 WORKDIR /home
 
-RUN pip install pipenv
-RUN pipenv install
+COPY pyproject.toml uv.lock ./
+
+RUN --mount=type=cache,target=/root/.cache/pip \
+    pip install uv && \
+    uv export --no-dev | pip install --no-cache-dir -r /dev/stdin
+
+COPY . .
 
 ENV WAIT=on
 ENV MODE=1
 
-CMD ["pipenv", "run", "python", "app.py"]
+CMD ["python", "app.py"]

今回は system 環境にインストールするようにしています

uv sync でも以下のような流れで動作するのでこの当たりは好きなように修正してください

RUN --mount=type=cache,target=/root/.cache/pip \
    pip install uv && \
    uv sync --frozen --no-install-project --no-dev
CMD ["uv", "run", "--no-sync", "gunicorn", "-w", "1", "-b", "0.0.0.0:8080", "app:app"]

--no-sync オプションは実行時に uv sync を実行しないための設定です

一応 docker build -> run して動作確認しておくといいでしょう

README 修正

pipenv で実行している部分を uv に書き換えます
例えば以下のような部分を書き換えていきます

-- pipenv install -d
+- uv sync --all-groups
-- pipenv run isort . && pipenv run black . && pipenv run pyright . && npm run format
+- uv run isort . && uv run black . && uv run pyright . && npm run format
-- pipenv run pytest test/ && pipenv run bandit -r . && REDIS_PASSWORD="" pipenv run gunicorn -w 1 -b 0.0.0.0:8080 app:app
+- uv run pytest test/ && uv run bandit -r . && REDIS_PASSWORD="" uv run gunicorn -w 1 -b 0.0.0.0:8080 app:app
-- pipenv requirements > requirements.txt
+- uv export > requirements.txt

.dockerignore 修正

node_modules/
.venv/

compose.yaml 修正

pipenv -> uv のコマンド修正をします

command: pipenv run python check_new_message.py

command: uv run python check_new_message.py

各種シェルスクリプトの修正

あれば修正します
同様に pipenv -> uv で OK です

Github Actions 修正

各種 yaml ファイルを修正しましょう

diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 50d8f31..48e8a98 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -16,11 +16,11 @@ jobs:
         uses: actions/setup-python@v5
       - name: Install dependencies
         run: |
-          python -m pip install --upgrade pipenv
-          pipenv install -d
+          python -m pip install --upgrade uv
+          uv sync --all-groups
       - name: Lint with isort
         run: |
-          pipenv run isort --check --diff .
+          uv run isort --check --diff .
       - name: Lint with black
         run: |
-          pipenv run black --check .
+          uv run black --check .

実際にアクションを実行して動作確認しましょう

vscode の設定の修正

  • .vscode/settings.json
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 8a2fea7..1bd2c9b 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,4 +1,3 @@
 {
-  "python-envs.defaultEnvManager": "ms-python.python:pipenv",
   "python-envs.pythonProjects": []
 }

これでプロジェクト配下にある .venv を自動で読み込んでくれます

.env ファイルを使っている場合の注意事項

  • uv run --env-file .env

のように命じ的に指定しないと読み込んでくれないので注意しましょう

移行後

  • Pipfile と Pipfile.lock の削除
  • .isort.cfg の削除
  • pip uninstall pipenv
  • venv の削除
    • rm -rf ~/.local/share/virtualenvs/ai-gallery-nT0ArqSL
  • pipenv コマンドがプロジェクト内で使われていないか確認
    • git grep 'pipenv'
  • GAE などにもデプロイしている場合は動作確認
uv export > requirements.txt
gcloud app deploy -q

最後に

アプリ自体を修正するのは簡単ですが CI や各種スクリプトで使われているコマンドを書き換えるのが面倒かなという印象です

2026年4月6日月曜日

Dockerfile の RUN では --mount を使おう

Dockerfile の RUN では --mount を使おう

概要

ビルドを高速化しましょう

環境

  • macOS 26.3.1
  • docker 29.3.1

FROM python:3.12.11-alpine3.21

# 必要なパッケージをインストール
RUN apk add --no-cache nodejs npm

COPY . /home
WORKDIR /home

RUN pip install pipenv
RUN pipenv install

ENV REDIS_HOST=redis

# ミニファイ用ツールをインストール
RUN npm install -g clean-css-cli uglify-js

# CSS / JS をミニファイ
RUN uglifyjs static/js/fontawesome-all.min.js -o static/js/fontawesome-all.min.js
RUN cleancss -o static/css/footer.css static/css/footer.css

CMD ["pipenv", "run", "gunicorn", "-w", "1", "-b", "0.0.0.0:8080", "app:app"]

# syntax=docker/dockerfile:1.7

FROM python:3.12.11-alpine3.21

# 必要なパッケージ
RUN apk add --no-cache nodejs npm

WORKDIR /home

# -------------------------
# ① 依存関係だけ先にコピー(キャッシュ効かせる)
# -------------------------
COPY Pipfile Pipfile.lock ./

# pipenv + cache mount
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install pipenv && \
    pipenv install --deploy --system

# -------------------------
# ② npmツール(キャッシュ効かせる)
# -------------------------
RUN --mount=type=cache,target=/root/.npm \
    npm install -g clean-css-cli uglify-js

# -------------------------
# ③ アプリ本体(最後にコピー)
# -------------------------
COPY . .

ENV REDIS_HOST=redis

# -------------------------
# ④ minify(tmpfsで高速化&不要データ残さない)
# -------------------------
RUN --mount=type=tmpfs,target=/tmp \
    uglifyjs static/js/fontawesome-all.min.js -o static/js/fontawesome-all.min.js && \
    cleancss -o static/css/footer.css static/css/footer.css

CMD ["gunicorn", "-w", "1", "-b", "0.0.0.0:8080", "app:app"]

最後に

今回はライブラリのインストールなどに使いましたが一時的な認証情報を渡すときにも使えます

2026年3月29日日曜日

sshd でチャレンジレスポンス認証を試す

sshd でチャレンジレスポンス認証を試す

概要

前回 Ubuntu + pam の設定を行いました

チャレンジレスポンス認証は通常のパスワード認証と毎回違うパスワードで ssh ログインさせたりすることができます
OTP や外部の認証サーバと連携したりもできます

今回は簡単な問題をシェルスクリプトで作成しその問題が正解なら認証 OK とします

環境

  • macOS 26.3.1
  • docker 29.3.0
    • Ubuntu 24.04

pam.d/ssh

# --- Auth Phase ---
# 1. クイズを表示
auth       required     pam_echo.so info Challenge: 3 + 5 = ?

# 2. スクリプト判定(8 以外ならここで拒否される)
auth       required     pam_exec.so expose_authtok /usr/local/bin/math_challenge.sh

# 3. 資格情報 (setcred) を与えるためのモジュール
# 前のステップが required なので、ここには「8」と打った時しか到達しません。
# これを入れることで pam_setcred(): Permission denied を防ぎます。
auth       required     pam_permit.so


# --- Account & Session Phase ---
account    required     pam_permit.so

session    required     pam_unix.so
session    required     pam_env.so
# session  required     pam_loginuid.so  # Dockerでは引き続きコメントアウト

math_challenge.sh

#!/bin/bash
# 入力を読み取る
read -r USER_INPUT

# 判定(xargsで空白を除去)
ANSWER=$(echo "$USER_INPUT" | xargs)

if [ "$ANSWER" = "8" ]; then
    # 成功時は何も出力せず exit 0
    exit 0
else
    # 失敗時はデバッグ用に標準エラーへ(docker logsで見れる)
    echo "Auth Failed: Input was '$ANSWER'" >&2
    exit 1
fi

Dockerfile

FROM ubuntu:24.04

RUN apt-get update && \
    apt-get install -y openssh-server && \
    mkdir /var/run/sshd

# ユーザー作成
RUN useradd -m -s /bin/bash testuser && echo "testuser:testpass" | chpasswd

# 既存設定をリセットし、必要な設定だけを新規作成
RUN rm -rf /etc/ssh/sshd_config.d/* && \
    echo "Port 22" > /etc/ssh/sshd_config && \
    echo "ListenAddress 0.0.0.0" >> /etc/ssh/sshd_config && \
    echo "PermitRootLogin no" >> /etc/ssh/sshd_config && \
    echo "KbdInteractiveAuthentication yes" >> /etc/ssh/sshd_config && \
    echo "PasswordAuthentication no" >> /etc/ssh/sshd_config && \
    echo "UsePAM yes" >> /etc/ssh/sshd_config

# 認証スクリプトをコピー
COPY math_challenge.sh /usr/local/bin/math_challenge.sh
RUN chmod +x /usr/local/bin/math_challenge.sh

# PAM設定を上書き
COPY pam.d/sshd /etc/pam.d/sshd

# (重要) CHALLENGE-RESPONSEを有効化
RUN sed -i 's/KbdInteractiveAuthentication no/KbdInteractiveAuthentication yes/' /etc/ssh/sshd_config

EXPOSE 22

CMD ["/usr/sbin/sshd", "-D", "-e"]

compose.yaml

services:
  ssh:
    build: .
    container_name: ssh-challenge
    ports:
      - "2222:22"

動作確認

  • docker compose up -d --build
  • ssh -o PreferredAuthentications=keyboard-interactive testuser@localhost -p 2222

これで「8」と入力すればログインできるようになります
それ以外はログインエラーになります
さすがにチャレンジするパスワードが簡単すぎるので本当はもっと複雑な問題にします

最後に

Ubuntu24.04 でチャレンジレスポンス認証を試してみました
pam と連携することでより複雑なチャレンジレスポンス認証が実装できます

あとはこのスクリプトをより改良して外部の認証機構と連携したりすることができます

2026年3月28日土曜日

Ubuntu で pam 認証を有効にする

Ubuntu で pam 認証を有効にする

概要

PAM (Pluggable Authentication Modules) は通常の sshd 認証と違い外部の認証機構などを使って認証する仕組みです
sshd と組み合わせることで OTP などを実現できます
今回は Ubuntu 24.04 で pam を設定する方法を紹介します

環境

  • macOS 26.3.1
  • docker 29.3.0
    • Ubuntu 24.04

pam.d/sshd

# PAM構成の基本
auth       required     pam_unix.so nullok_secure
account    required     pam_unix.so
session    required     pam_unix.so
session    required     pam_loginuid.so

Dockerfile

FROM ubuntu:24.04

RUN apt-get update && \
    apt-get install -y openssh-server && \
    mkdir /var/run/sshd

# ユーザー作成
RUN useradd -m -s /bin/bash testuser && echo "testuser:testpass" | chpasswd

# 既存設定をリセットし、必要な設定だけを新規作成
RUN rm -rf /etc/ssh/sshd_config.d/* && \
    echo "Port 22" > /etc/ssh/sshd_config && \
    echo "ListenAddress 0.0.0.0" >> /etc/ssh/sshd_config && \
    echo "PermitRootLogin no" >> /etc/ssh/sshd_config && \
    echo "KbdInteractiveAuthentication yes" >> /etc/ssh/sshd_config && \
    echo "PasswordAuthentication no" >> /etc/ssh/sshd_config && \
    echo "UsePAM yes" >> /etc/ssh/sshd_config

# PAM設定コピー
COPY pam.d/sshd /etc/pam.d/sshd

EXPOSE 22

CMD ["/usr/sbin/sshd", "-D"]

compose.yaml

services:
  ssh:
    build: .
    container_name: ssh-challenge
    ports:
      - "2222:22"

動作確認

  • docker compose up -d --build
  • ssh -o PreferredAuthentications=keyboard-interactive testuser@localhost -p 2222

最後に

Ubuntu24.04 の sshd の場合設定が少し特殊なので注意しましょう
基本はデフォルトを削除し新規に作成するのが確実です

次回は pam を使ってチャレンジレスポンス認証を試します