競艇の収支管理をClaude APIで自動化したら、月の損失が4万円から1.2万円に圧縮できた。3ヶ月試して出した結論がこれだ。
「月いくら負けてるか、正確に答えられる?」
以前の自分に聞いたら、絶対に答えられなかった。ざっくり「まあトントンか、ちょっとマイナスくらい」という感覚で賭け続けていた。それが間違いだった。
この記事では、私が実際に作った収支管理システムの設計から実装まで、コードを交えて全部紹介する。競艇・競馬を定期的に楽しんでいるエンジニアなら、1日あれば同じものが作れる。
感情で賭けていた頃の話
私が競艇を始めたのは3年ほど前だ。最初はレースの面白さにハマり、週末だけ賭けていた。そのうち平日のナイターも、出張先でもスマホで買うようになった。
問題は、損失の実感がなかったことだ。
1レース500円、1000円という金額は「安い」と感じる。でも週3回レース場に行って、1日あたり10レース賭けていたら、負け続ければ月に3〜5万円が消える。それを「ざっくりトントンかな」で片付けていた。
ある月、クレジットカードの明細を見て青ざめた。競艇関連の支出だけで6万2000円あった。その月、的中したレースで戻ってきたのは2万円ちょっと。純損失は約4万円だ。
問題は金額よりも、それを把握していなかった自分だった。エンジニアとして、システムのコストはセント単位で管理するのに、個人の娯楽費は完全にブラックボックスだった。
「これはまずい」と思って、収支管理のツールを自分で作ることにした。
システムの全体設計
シンプルさを最優先にした。続けられないシステムは意味がない。
【全体の流れ】
1. 賭けた記録を手入力(またはCSVインポート)
↓
2. SQLiteに保存(ローカルDB)
↓
3. 週次・月次でClaude APIが収支を分析
↓
4. 危険サインを検知したらSlack通知
↓
5. 分析レポートをMarkdownで保存
使った技術はこれだけだ。
- Python 3.12
- SQLite(ローカルDB、セットアップ不要)
- Claude API(anthropic ライブラリ)
- Slack Incoming Webhook(通知用)
外部サービスはSlackだけ。DBはローカルのSQLiteなので、個人情報をクラウドに送らずに済む。
実装①:賭け履歴の記録
まずデータを記録する仕組みから作る。
データベース設計
import sqlite3
from datetime import datetime
def init_db(db_path="gambling.db"):
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS bets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT NOT NULL,
venue TEXT NOT NULL,
race_no INTEGER NOT NULL,
bet_type TEXT NOT NULL,
combination TEXT NOT NULL,
stake INTEGER NOT NULL,
return_amount INTEGER DEFAULT 0,
profit INTEGER GENERATED ALWAYS AS (return_amount - stake) STORED,
note TEXT
)
""")
conn.commit()
conn.close()
カラムはシンプルに。日付・会場・レース番号・賭け式・購入金額・払戻金額の6項目だ。利益は自動計算カラムにして、手入力ミスを減らした。
記録の入力
def add_bet(date, venue, race_no, bet_type, combination, stake, return_amount=0, note=""):
conn = sqlite3.connect("gambling.db")
cursor = conn.cursor()
cursor.execute("""
INSERT INTO bets (date, venue, race_no, bet_type, combination, stake, return_amount, note)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""", (date, venue, race_no, bet_type, combination, stake, return_amount, note))
conn.commit()
conn.close()
# 使い方の例
add_bet(
date="2026-05-20",
venue="戸田",
race_no=6,
bet_type="2連単",
combination="1-3",
stake=1000,
return_amount=0 # 外れ
)
レース後に結果が出たら、return_amount を更新するだけでいい。
月次サマリーの集計
def get_monthly_summary(year, month, db_path="gambling.db"):
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
cursor.execute("""
SELECT
COUNT(*) as total_bets,
SUM(stake) as total_stake,
SUM(return_amount) as total_return,
SUM(profit) as total_profit,
ROUND(SUM(return_amount) * 100.0 / SUM(stake), 1) as recovery_rate,
COUNT(CASE WHEN return_amount > 0 THEN 1 END) as hit_count
FROM bets
WHERE strftime('%Y', date) = ? AND strftime('%m', date) = ?
""", (str(year), f"{month:02d}"))
row = cursor.fetchone()
conn.close()
return {
"total_bets": row[0],
"total_stake": row[1],
"total_return": row[2],
"total_profit": row[3],
"recovery_rate": row[4],
"hit_count": row[5],
"hit_rate": round(row[5] * 100 / row[0], 1) if row[0] > 0 else 0
}
実装②:Claude APIで収支分析・アドバイス
ここが一番面白い部分だ。集計した数値をそのまま見るだけではなく、Claude APIに「傾向分析とアドバイス」をさせる。
分析用プロンプトの設計
import anthropic
from datetime import datetime, timedelta
def analyze_with_claude(summary: dict, recent_bets: list) -> str:
client = anthropic.Anthropic()
# 直近30日の収支データをテキストに変換
bet_details = "\n".join([
f"- {b['date']} {b['venue']}競艇 第{b['race_no']}R: "
f"賭け金{b['stake']:,}円 → 払戻{b['return_amount']:,}円 ({b['bet_type']})"
for b in recent_bets[-30:] # 直近30件
])
prompt = f"""以下の競艇収支データを分析してください。
【直近30日の収支サマリー】
- 総賭け件数: {summary['total_bets']}件
- 総賭け金額: {summary['total_stake']:,}円
- 総払戻金額: {summary['total_return']:,}円
- 純損益: {summary['total_profit']:,}円
- 回収率: {summary['recovery_rate']}%
- 的中率: {summary['hit_rate']}%({summary['hit_count']}件 / {summary['total_bets']}件)
【賭け履歴の詳細】
{bet_details}
以下の観点で分析してください:
1. 回収率の傾向(改善中か悪化中か)
2. 賭け方のパターンで気になる点(金額が増えている傾向など)
3. 次の30日間へのアドバイス(具体的に2〜3点)
4. このデータで最も心配な点(正直に教えてください)
厳しくても構いません。事実ベースで教えてください。"""
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}]
)
return response.content[0].text
実際にClaudeが返してきた分析例
先月のデータを入れたら、こんな返答が来た(一部要約)。
回収率71%は平均的な競艇ユーザーの水準ですが、直近2週間で賭け金額が平均1,200円から2,100円に増加しています。これは「取り返したい」という心理が影響している可能性があります。特に外れが続いた翌日の賭け金額が平均35%高くなっているパターンが見られます。
これを見たとき、「当たっている」と思った。感覚ではわかっていなかったが、データに出ていた。連敗した次の日は無意識に賭け金を増やしていた。
実装③:危険サインの自動検知
感情で判断しているとき、人間は自分で気づけない。そこでSlack通知を入れた。
危険サインの定義
自分で決めたルールはこの3つだ。
- 3連敗以上が続いている(連続して的中ゼロ)
- 今月の損失が設定上限(1.5万円)を超えた
- 1レースの賭け金が上限(3000円)を超えた
import requests
import json
SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
MONTHLY_LOSS_LIMIT = 15000 # 月の損失上限(円)
SINGLE_BET_LIMIT = 3000 # 1レースの上限(円)
CONSECUTIVE_LOSS_LIMIT = 3 # 連敗上限
def check_danger_signs(db_path="gambling.db"):
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# 今月の損失を計算
now = datetime.now()
cursor.execute("""
SELECT SUM(profit) FROM bets
WHERE strftime('%Y-%m', date) = ?
""", (now.strftime("%Y-%m"),))
monthly_profit = cursor.fetchone()[0] or 0
# 直近の連敗チェック
cursor.execute("""
SELECT return_amount FROM bets
ORDER BY date DESC, id DESC
LIMIT ?
""", (CONSECUTIVE_LOSS_LIMIT,))
recent = cursor.fetchall()
consecutive_losses = all(r[0] == 0 for r in recent)
# 直近の1レース賭け金チェック
cursor.execute("""
SELECT stake FROM bets ORDER BY id DESC LIMIT 1
""")
last_stake = (cursor.fetchone() or [0])[0]
conn.close()
alerts = []
if monthly_profit < -MONTHLY_LOSS_LIMIT:
alerts.append(f"月の損失が上限を超えました: {abs(monthly_profit):,}円の損失(上限: {MONTHLY_LOSS_LIMIT:,}円)")
if consecutive_losses and len(recent) >= CONSECUTIVE_LOSS_LIMIT:
alerts.append(f"{CONSECUTIVE_LOSS_LIMIT}連敗中です。少し休憩を検討してください。")
if last_stake > SINGLE_BET_LIMIT:
alerts.append(f"直近のベット額が上限超過: {last_stake:,}円(上限: {SINGLE_BET_LIMIT:,}円)")
if alerts:
send_slack_alert(alerts)
return alerts
def send_slack_alert(alerts: list):
message = "【競艇収支 危険サイン検知】\n\n" + "\n".join(f"• {a}" for a in alerts)
requests.post(SLACK_WEBHOOK_URL, json={"text": message})
このSlack通知が来たとき、スマホを開いて「あ、また取り返そうとしてた」と気づいた。それだけで行動を止められる。
週次の自動レポート
毎週月曜の朝、自動でレポートが来るようにcronを設定した。
# weekly_report.py(毎週月曜 09:00 に実行)
def generate_weekly_report():
now = datetime.now()
summary = get_monthly_summary(now.year, now.month)
# 今週のベット履歴を取得
week_ago = (now - timedelta(days=7)).strftime("%Y-%m-%d")
conn = sqlite3.connect("gambling.db")
cursor = conn.cursor()
cursor.execute("SELECT * FROM bets WHERE date >= ? ORDER BY date", (week_ago,))
recent_bets = [
{"date": r[1], "venue": r[2], "race_no": r[3],
"bet_type": r[4], "combination": r[5],
"stake": r[6], "return_amount": r[7]}
for r in cursor.fetchall()
]
conn.close()
# Claude APIで分析
analysis = analyze_with_claude(summary, recent_bets)
# Slack通知
report = f"【週次 競艇収支レポート】\n\n今月累計損益: {summary['total_profit']:,}円\n回収率: {summary['recovery_rate']}%\n\n{analysis}"
send_slack_alert([report])
使ってみての効果と変化
3ヶ月使い続けた結果を正直に報告する。
導入前(2025年11月)
- 月の損失: 約4万円(ただし把握していなかった)
- 1レースの賭け金: 感覚で500〜5000円
- 連敗後の行動: 気づかずに賭け金を増額
導入後3ヶ月の平均
- 月の損失: 約1.2万円
- 1レースの賭け金: 300〜1500円(上限を意識するようになった)
- 連敗後の行動: Slack通知が来たら翌日は休む
損失が約70%削減されたのは、Claude APIの分析が賢かったというより、「見える化」したことで自分の行動が変わったからだ。
Claudeが返してくる分析は毎回鋭い。「取り返したい心理が金額に出ている」「土日より平日のほうが回収率が高い(冷静に賭けているから)」「購入数が多い日ほど1レース単価が下がっている(分散できている)」。データから行動パターンを読み取ってくれる。
改善したいところ(正直に)
良いことばかり書いても意味がないので、課題も書く。
1. 手入力が面倒
レース後に手入力するのが習慣化するまでに2週間かかった。今でも忘れることがある。競艇公式のSPAT4や楽天競馬のAPIを叩いて自動取得できれば理想だが、利用規約の確認が必要で手をつけていない。
2. Claudeの分析が毎回似たようなことを言う
プロンプトを工夫しているが、「回収率を意識してください」「連敗後は冷静に」という内容は繰り返しになりがち。もう少しプロンプトを変えて、前回の分析と比較する形にしたい。
3. 確率の根拠が弱い
「土日より平日のほうが回収率が高い」という傾向は出ているが、サンプル数がまだ少ない。3ヶ月のデータでは統計的に結論を出すには早い。半年以上のデータが溜まってから判断しようと思っている。
まとめ
競艇・競馬の収支管理をClaude APIで自動化した話を書いた。
システムの構成は単純だ。SQLiteで履歴を記録し、週次でClaude APIに分析させ、危険サインが出たらSlack通知する。コードは全部合わせて200行程度で動く。
一番大事なのは「ツールを作ったから管理できる」ではなく、「記録と分析の習慣が行動を変える」という点だ。Claudeの分析が鋭いのは確かだが、それ以上に「今月いくら負けているか、毎週確認している自分」が存在することが行動を変えた。
エンジニアなら半日あれば同じ仕組みが作れる。競艇・競馬に限らず、パチンコや株のデイトレードでも同じ構造で使える。「なんとなく損している気がするが把握できていない」という状況は、システムで解決できる類いの問題だ。
ソースコードはGitHubに公開予定なので、興味があれば参考にしてみてほしい。
*本記事はギャンブルを推奨するものではありません。公営競技(競艇・競馬など)への参加は成年(18歳以上または20歳以上)に限られます。収支管理ツールは娯楽の範囲内での利用を前提としています。*
関連ツールを見る
この記事で紹介したツール・サービスをまとめてチェック。
![]()