2026年5月22日金曜日

SpringBoot で MySQL に接続する方法

SpringBoot で MySQL に接続する方法

概要

前回 SpringBoot の開発環境を構築しました
今回はアプリケーションを修正し MySQL を使ったアプリにします

環境

  • macOS 26.4.1
  • openjdk 26.0.1
  • SpringBoot 4.0.6
  • gradle 9.5.1
  • VSCode 1.121.0
  • MySQL 9.6.0

MySQL 準備

使えれば何でも OK です

  • brew install mysql

あとで説明もしますが今回は docker compose を使ってアプリ起動時に自動で mysql も起動して勝手にその mysql を使ってくれる構成なので最悪 mysql はインストールしないでも OK です

Spring Initializr を使ってプロジェクトを作成

https://start.spring.io/ でプロジェクトの雛形を作成します
前回と違うのは Dependencies に MySQL に関連する項目も選択する部分です

  • Project -> Gradle - Groovy
  • Language -> Java
  • Spring Boot -> 4.0.6
  • Dependencies -> Spring Web, Spring Data JPA, MySQL Driver, Testcontainers, Docker Compose Support
  • Java -> 26

で Generate すると demo.zip がダウンロードできます

demo.zip を展開し VSCode で開く

展開してできた demo フォルダを VSCode で開きましょう
拡張は以下をインストールします

  • Extension Pack for Java
  • Spring Boot Extension Pack

Entity ファイルの作成

データベースの構造を定義します
Spring はこの Entity ファイルの定義に従って自動的にデータベースをマイグレートとしてくれます

  • vim src/main/java/com/example/demo/User.java
package com.example.demo;

import org.jspecify.annotations.Nullable;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity // これはHibernateにこのクラスからテーブルを作成するように指示します
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private @Nullable Integer id;

    private String name;

    private String email;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

Repository の作成

Spring は Entity 用のリポジトリを作成すると最低限の CRUD 処理を暗黙的に作成してくれます

それ以外の複雑な SQL を実行したい場合はここに別途定義します
ファイルに基本的な CRUD 処理がないのは Spring が自動で生成してくれているためです

  • vim src/main/java/com/example/demo/UserRepository.java
package com.example.demo;

import org.springframework.data.repository.CrudRepository;

// これはSpringによってuserRepositoryというBeanに自動的に実装されます

public interface UserRepository extends CrudRepository<User, Integer> {

}

コントローラの作成

今回はユーザを登録する処理と登録したユーザを取得する処理を追加します

  • vim src/main/java/com/example/demo/MainController.java
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller // これは、このクラスがコントローラーであることを意味します
@RequestMapping(path = "/demo") // これはURLが/demoで始まることを意味します(アプリケーションパスの後)
public class MainController {
    @Autowired // これは、userRepositoryというBeanを取得することを意味します
    // Springによって自動的に生成されるため、データ処理に使用します
    private UserRepository userRepository;

    @PostMapping(path = "/add") // POSTリクエストを/demo/addにマッピングします
    public @ResponseBody String addNewUser(@RequestParam String name, @RequestParam String email) {
        // @ResponseBody は、返される文字列がレスポンスであり、ビュー名ではないことを意味します
        // @RequestParam は、GET または POST リクエストからのパラメータであることを意味します

        User n = new User();
        n.setName(name);
        n.setEmail(email);
        userRepository.save(n);
        return "Saved";
    }

    @GetMapping(path = "/all")
    public @ResponseBody Iterable<User> getAllUsers() {
        // これはユーザーのJSONまたはXMLを返します
        return userRepository.findAll();
    }
}

メインアプリケーションの作成

@Controller で定義したクラスは自動的に読み込まれるので特にアプリケーション側で指定する必要はありません

  • vim src/main/java/com/example/demo/AccessingDataMysqlApplication.java
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AccessingDataMysqlApplication {

    public static void main(String[] args) {
        SpringApplication.run(AccessingDataMysqlApplication.class, args);
    }

}

アプリ起動

ローカルでアプリを起動します

  • ./gradlew bootRun

各種必要な jar のダウンロードなどが始まります
基本的には ~/.gradle/ にダウンロードされます

最終的に EXECUTING になれば起動しています

また今回は docker compose plugin を使用しているためプロジェクト配下に compose.yaml を自動的に作成しその compose.yaml を bootRun 時に自動で起動します
なので mysql コンテナが自動的に作成されかつ Entity に定義したテーブル情報も自動で生成されます

mysql> use mydatabase;
Database changed

mysql> show tables;
+----------------------+
| Tables_in_mydatabase |
+----------------------+
| user                 |
| user_seq             |
+----------------------+
2 rows in set (0.002 sec)

動作確認

  • $ curl http://localhost:8080/demo/add -d name=First -d email=someemail@someemailprovider.com
Saved
  • curl http://localhost:8080/demo/all
[{"email":"someemail@someemailprovider.com","id":1,"name":"First"}]

他のデータベースに接続する方法

org.springframework.boot:spring-boot-docker-compose は自動的に compose.yaml を読み込み接続します
手動で設定した MySQL に接続する場合は application.properties に以下のように記載します

spring.application.name=demo
# update はアプリケーション起動時に、Entityに対応するテーブルがなければ作成します
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/mydatabase
spring.datasource.username=root
spring.datasource.password=

これで更にプロジェクトは以下にある compose.yaml を削除するかサブディレクトリに移動するか compose.yaml にラベルを追記すれば application.properties にあるデータベースに接続します

services:
  mysql:
    image: 'mysql:latest'
    environment:
      - 'MYSQL_DATABASE=mydatabase'
      - 'MYSQL_PASSWORD=xxx'
      - 'MYSQL_ROOT_PASSWORD=xxx'
      - 'MYSQL_USER=myuser'
    ports:
      - '3306'
    labels:
      org.springframework.boot.ignore: true

最後に

SpringBoot で MySQL に接続する方法を紹介しました
Rails や Jango の用に CRUD ツールも備わっているのでかなり簡単に接続/操作できる印象です

本番では Liquibase や Flyway などのマイグレーションツールを使ってバージョン管理しましょう

参考サイト

2026年5月21日木曜日

macOS + VSCode で SpringBoot を試す

macOS + VSCode で SpringBoot を試す

概要

VSCode を使ってコーディングできる環境を構築しアプリをビルドし動作確認する環境まで構築します

環境

  • macOS 26.4.1
  • openjdk 26.0.1
  • gradle 9.5.1
  • VSCode 1.121.0

準備

  • brew install openjdk
  • sudo ln -sfn /opt/homebrew/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk
  • brew install gradle

Spring Initializr を使ってプロジェクトを作成

https://start.spring.io/ でプロジェクトの雛形を作成します

  • Project -> Gradle - Groovy
  • Language -> Java
  • Spring Boot -> 4.0.6
  • Dependencies -> Spring Web
  • Java -> 26

で Generate すると demo.zip がダウンロードできます

demo.zip を展開し VSCode で開く

展開してできた demo フォルダを VSCode で開きましょう
拡張は以下をインストールします

  • Extension Pack for Java
  • Spring Boot Extension Pack

サンプルアプリケーション

demo 内にすでに src/main/java/com/example/demo/DemoApplication.java がありますが削除して新しいアプリを作成します

まずはコントローラを作成します
/ にアクセスしたら簡単なメッセージを返却するコントローラを作成します

  • vim src/main/java/com/example/demo/HelloController.java
package com.example.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

  @GetMapping("/")
  public String index() {
    return "Greetings from Spring Boot!";
  }

}

次にコントローラを使用するアプリケーションを追加します
これがいわゆるメインファイルになります

  • vim src/main/java/com/example/demo/Application.java
package com.example.demo;

import java.util.Arrays;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

  @Bean
  public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
    return args -> {

      System.out.println("Let's inspect the beans provided by Spring Boot:");

      String[] beanNames = ctx.getBeanDefinitionNames();
      Arrays.sort(beanNames);
      for (String beanName : beanNames) {
        System.out.println(beanName);
      }

    };
  }

}

アプリ起動

ローカルでアプリを起動します

  • ./gradlew bootRun

各種必要な jar のダウンロードなどが始まります
基本的には ~/.gradle/ にダウンロードされます

最終的に EXECUTING になれば起動しています

動作確認

  • curl http://localhost:8080
Greetings from Spring Boot

ユニットテストを追加する

ユニットテストも追加してみます
まずはテスト用のライブラリを追加します

testImplementation の行を追加しましょう
あれば追加不要です

  • vim build.gradle
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-webmvc'
	testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

src/test/java/com/example/demo/DemoApplicationTests.java がありますが削除して OK です
新たに以下を追加します

  • src/test/java/com/example/demo/HelloControllerTest.java
package com.example.demo;

import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {

  @Autowired
  private MockMvc mvc;

  @Test
  public void getHello() throws Exception {
    mvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
        .andExpect(status().isOk())
        .andExpect(content().string(equalTo("Greetings from Spring Boot!")));
  }
}

あとはテストを実行します

  • ./gradlew tasks

でタスクを確認し test タスクを実行します

  • ./gradlew test
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended

BUILD SUCCESSFUL in 4s
4 actionable tasks: 2 executed, 2 up-to-date
Consider enabling configuration cache to speed up this build: https://docs.gradle.org/9.4.1/userguide/configuration_cache_enabling.html

最後に

macOS + VSCode で Spring Boot を試してみました
まだ簡単なアプリを起動しただけなので次回はデータベースと連携してみたいと思います

後片付け

  • rm -rf ~/.gradle/
  • VSCode 拡張の削除
  • demo フォルダの削除
  • brew uninstall openjdk
  • brew uninstall gradle

参考サイト

2026年5月12日火曜日

LTX2 を M2 Pro Mac mini で動かす

LTX2 を M2 Pro Mac mini で動かす

概要

MLX 版があるのでそれを使って M2 Pro 上で動作させてみます

環境

  • M2 Pro mac mini (16GB)
  • macOS 26.4.1
  • LTX2-MLX c4f8c44
  • Python 3.12.13

インストール

  • git clone --depth=1 https://github.com/Acelogic/LTX-2-MLX.git
  • cd LTX-2-MLX
  • pyenv local 3.12.13
  • uv sync --all-groups

モデルダウンロード

  • uv run scripts/download_weights.py

注意事項

gemma3 はライセンスに同意する必要がある

  • https://huggingface.co/google/gemma-3-12b-it -> Acknowledge license
  • huggingface のアカウントが必要
  • エラーになった場合は一度 rm -rf weights/gemma-3-12b してから再度 uv run scripts/download_weights.py する

distilled は 43GB あるので動かない場合は distilled-fp8 27GB を変わりにダウンロードし使うこと

  • uv run scripts/download_weights.py 実行時に 3 - Custom - Choose individual weights を選択しダウンロードするモデルを個別に選択する

動画生成(通常)

uv run python scripts/generate.py "A realistic cat riding a skateboard on a street, smoothly moving forward, natural motion, full body visible, cinematic lighting, high detail, 4k, camera tracking shot, shallow depth of field" --height 512 --width 512

おそらくこれは M2 Pro mac mini ではエラーになります

動画生成(fp8版)

uv run python scripts/generate.py "A realistic cat riding a skateboard on a street, smoothly moving forward, natural motion, full body visible, cinematic lighting, high detail, 4k, camera tracking shot, shallow depth of field" --height 512 --width 512 --fp8

画像サイズを下げれば何とかこれでもいけるかもしれませんがおそらくこれも不可能です

動画生成(fp8版+低メモリ)

uv run python scripts/generate.py "A realistic cat riding a skateboard on a street" --height 64 --width 96 --fp8 --low-memory --frames 9

おそらくこれでないと M2 Pro mac mini のスペックでは無理かなと思います
画像サイズとプロンプトの長さ、フレーム数も下げています

動画生成(fp8版+低メモリ+fp16フラグ)

uv run python scripts/generate.py "A realistic cat riding a skateboard on a street" --height 64 --width 96 --fp8 --low-memory --frames 9 --fp16 --pipeline one-stage --cfg 5.0

トラブルシューティング

libc++abi: terminating due to uncaught exception of type std::runtime_error: [METAL] Command buffer execution failed: Impacting Interactivity (0000000e:kIOGPUCommandBufferCallbackErrorImpactingInteractivity)

基本的にはメモリ不足が原因です

There appear to be 1 leaked semaphore objects to clean up at shutdown

これもメモリ不足の場合はほとんどです

最後に

LTX2-MLX を M2 Pro mac mini で動かしてみました
何とか動きますが量子化済みのモデルでないと動かないのと生成する動画サイズもかなり小さく短いものでないと無理でした

本当にギリギリなのでもしかしたら環境によっては動かない可能性もあるのでご了承ください

参考サイト

2026年5月10日日曜日

Minecraft サーバに Timeless and Classics Zero Guns (TaCZ) をインストールする方法

Minecraft サーバに Timeless and Classics Zero Guns (TaCZ) をインストールする方法

概要

サーバにインストールしクライアント側にも同じ Mod のインストールが必要です
必ずバージョンは揃えましょう

環境

  • サーバ側
    • macOS 26.4.1
    • Minecraft 1.20.1
    • forge 47.4.10
    • TaCZ 1.1.8
  • クライアント側
    • macOS 26.4.1/Windows11Home
    • Minecraft 1.20.1
    • forge 47.4.10
    • TaCZ 1.1.8

サーバ側

compose.yaml

サーバ側は docker compose で起動します

  • vim compose.yaml
services:
  mc:
    image: itzg/minecraft-server
    tty: true
    stdin_open: true
    ports:
      - "25565:25565"
    environment:
      EULA: "TRUE"
      MODE: "creative"
      VERSION: "1.21.11"
    volumes:
      # attach the relative directory 'data' to the container's /data path
      - ./data:/data

  mc_mod:
    image: itzg/minecraft-server
    tty: true
    stdin_open: true
    ports:
      - "25566:25565"
    environment:
      EULA: "TRUE"
      MODE: "creative"
      VERSION: "1.20.1"
      TYPE: "forge"
    volumes:
      # modded world/data directory for the modded server
      - ./modded-data:/data
      # put the CurseForge mod JAR into this local directory
      - ./timeless_and_classics_zero/mods:/data/mods
      # optional config directory for mod/server configs
      - ./timeless_and_classics_zero/config:/data/config

通常のマイクラサーバとは別で TaCZ がインストールされた forge サーバを起動します

TaCZ のダウンロードとインストール

https://www.curseforge.com/minecraft/mc-mods/timeless-and-classics-zero

から tacz-1.20.1-1.1.8-release.jar をダウンロードします
サーバ側は timeless_and_classics_zero/mods 配下に配置します

  • mkdir timeless_and_classics_zero/mods
  • mkdir timeless_and_classics_zero/config
  • mkdir modded-data

起動確認

  • docker compose up -d

で問題なく起動することを確認します

クライアント側 (Mac)

forge のインストール

https://files.minecraftforge.net/net/minecraftforge/forge/index_1.20.1.html

から forge-1.20.1-47.4.10-installer.jar をダウンロードしダブルクリックでインストールします
デフォルトだと開けないのでプライバシーとセキュリティから jar を開きます

TaCZ のダウンロードとインストール

https://www.curseforge.com/minecraft/mc-mods/timeless-and-classics-zero

から tacz-1.20.1-1.1.8-release.jar をダウンロードします
Mac の場合のクライアントは以下のパスに Mod を配置します

/Users/username/Library/Application Support/minecraft/mods

起動確認

ランチャーを起動し1.20.1-forge-47.4.10 の起動構成を作成してその起動構成からマイクラを起動しましょう

あとはマルチプレイから起動した docker compose のサーバにアクセスすれば OK です

クライアント側 (Windows)

JDK のインストール

Oracle 版でも OpenJDK でも OK です

https://www.oracle.com/jp/java/technologies/downloads/

コマンドをプロンプトを起動して java コマンドで実行できるか確認します

forge のインストール

https://files.minecraftforge.net/net/minecraftforge/forge/index_1.20.1.html

から forge-1.20.1-47.4.10-installer.jar をダウンロードします
ダブルクリックだとインストールできないのでコマンドプロンプトを開いて起動します

  • java -jar forge-1.20.1-47.4.10-installer.jar

TaCZ のダウンロードとインストール

https://www.curseforge.com/minecraft/mc-mods/timeless-and-classics-zero

から tacz-1.20.1-1.1.8-release.jar をダウンロードします
Windows の場合のクライアントは以下のパスに Mod を配置します

C:\Users\xxx\AppData\Roaming\.minecraft\mods

起動確認

ランチャーを起動し1.20.1-forge-47.4.10 の起動構成を作成してその起動構成からマイクラを起動しましょう

あとはマルチプレイから起動した docker compose のサーバにアクセスすれば OK です

最後に

マルチで Mod を使って遊ぶ場合にはサーバ側とクライアント側に同じバージョンの同じ Mod をインストールする必要があるので注意しましょう

2026年5月4日月曜日

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 buy_stock.py
import time

import requests

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

SYMBOL = "1306"  # TOPIX ETF
EXCHANGE = 1  # 東証
QTY = 10  # 最小単位(検証なので10でOK)


# =========================
# トークン取得
# =========================
def get_token():
    url = f"{BASE_URL}/token"
    res = requests.post(url, json={"APIPassword": API_PASSWORD})

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

    return res.json()["Token"]


# =========================
# 共通ヘッダ
# =========================
def headers(token):
    return {"X-API-KEY": token}


# =========================
# 成行買い
# =========================
def buy_market(token):
    url = f"{BASE_URL}/sendorder"

    body = {
        "Symbol": SYMBOL,  # 銘柄コード
        "Exchange": EXCHANGE,  # 市場コード
        "SecurityType": 1,  # 商品種別 (1: 株式)
        "Side": 2,  # 売買区分 (2: 買)
        "CashMargin": 1,  # 信用区分 (1: 現物)
        "DelivType": 2,  # 受渡区分 (2: 預り金)
        "FundType": "02",  # 資産区分 (02: 保護)
        "AccountType": 4,  # 口座種別 (4: 特定)
        "Qty": QTY,  # 注文数量
        "FrontOrderType": 10,  # 執行条件 (10: 成行)
        "Price": 0,  # 注文価格
        "ExpireDay": 0,  # 注文有効期限 (0: 本日)
    }

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

    if res.status_code != 200:
        print("注文エラー:", res.status_code, res.text)
        return False

    print("注文成功:", res.json())
    return True


# =========================
# ポジション取得
# =========================
def get_positions(token):
    url = f"{BASE_URL}/positions"
    res = requests.get(url, headers=headers(token))

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

    return res.json()


# =========================
# メイン
# =========================
def main():
    token = get_token()
    if not token:
        return

    # ① 買う
    if not buy_market(token):
        return

    # 約定待ち(重要)
    print("約定待ち...")
    time.sleep(3)

    # ② ポジション取得
    positions = get_positions(token)

    print("=== 保有ポジション ===")
    if not positions:
        print("ポジションなし")
        return

    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()

売りの場合

上記のスクリプトで

  • Side -> 1
  • FundType -> " " (半角スペース2つ)

にすれば売りもできます

ログ格納場所

%appdata%\KabuS\Log

API リファレンス

https://kabucom.github.io/kabusapi/reference/index.html#operation/sendorderPost

body の各種パラメータはこちらを参照してください

あとは Github の Issue にいろいろ情報があります

https://github.com/kabucom/kabusapi/issues/

トラブルシューティング

注文エラー: 400 {"Code":1002,"Message":"数量は単位株数の倍数で入力してください"}

QTY = 10 以上にしましょう

注文エラー: 400 {"Code":4001005,"Message":"パラメータ変換エラー - 詳細はkabuSログファイルを確認してください"}

余計なパラメータがあったり不足しているパラメータがあるとこれが頻発します
自分は FundType が足りていませんでした

最後に

どうやら検証環境の場合は実際に購入することはできないようです
とりあえず以下のようになれば注文のリクエストは成功しています

注文成功: {'Result': 0, 'OrderId': None}

あとは各種パラーメタを調整すれば好きな銘柄の売買ができます

参考サイト

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 を使ってポジションの一覧を取得する方法を紹介します

参考サイト