0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FXの売買シグナルを自動通知する【yfinance × AWS Lambda × Discord】

0
Last updated at Posted at 2026-03-08

はじめに

最近、経済の勉強がてらFXを始めました。
チャート(為替状況を現したグラフ)が気になってしまい本来やるべきことに集中できなくなってしまったので、特定の条件下で(移動平均のゴールデンクロス・デッドクロスを検知時)Discordに通知するツールを作りました。
インフラエンジニアがPythonの勉強がてら個人開発した記録です。

完成イメージ:

  • USD/JPYの1時間足データをyfinanceで取得
  • MA25とMA75のクロスを検知
  • Discordに通知
  • AWS Lambda + EventBridgeで1時間ごとに自動実行
  • 費用:ほぼ無料

構成

要素 使用技術 理由
データ取得 yfinance 無料・簡単
シグナル検知 移動平均クロス(MA25/MA75) シンプルで初心者向け
通知 Discord Webhook 無料・簡単
実行環境 AWS Lambda + EventBridge 無料枠内で運用可能

システム構成図


コード

Webhook URLは環境変数から取得します。コードに直書きするとGitHubなどに上げたとき漏洩するリスクがあるためです。

import os
import yfinance as yf
import pandas as pd
import requests
from datetime import datetime

WEBHOOK_URL = os.environ["DISCORD_WEBHOOK_URL"]  # 環境変数から取得
SYMBOL = "USDJPY=X"
SHORT_MA = 25
LONG_MA = 75

def notify(message):
    # Discordが落ちていてもLambdaがエラー終了しないようにtry/exceptで囲む
    try:
        requests.post(WEBHOOK_URL, json={"content": message}, timeout=10)
    except Exception as e:
        print(f"通知失敗: {e}")

def check_signal():
    df = yf.download(SYMBOL, period="30d", interval="1h", progress=False)
    if df.empty:
        notify("⚠️ データ取得失敗")
        return
    # yfinanceはバージョンによってMultiIndexカラムを返すため、ここで正規化しておく
    if isinstance(df.columns, pd.MultiIndex):
        df.columns = df.columns.get_level_values(0)
    df["short_ma"] = df["Close"].rolling(SHORT_MA).mean()
    df["long_ma"] = df["Close"].rolling(LONG_MA).mean()
    prev = df.iloc[-2]
    curr = df.iloc[-1]
    price = round(float(curr["Close"]), 3)
    now = datetime.now().strftime("%Y-%m-%d %H:%M")
    if prev["short_ma"] < prev["long_ma"] and curr["short_ma"] > curr["long_ma"]:
        notify(f"🟡 【買いシグナル】ゴールデンクロス検知!\nUSD/JPY: {price}円\n⏰ {now}")
    elif prev["short_ma"] > prev["long_ma"] and curr["short_ma"] < curr["long_ma"]:
        notify(f"🔴 【売りシグナル】デッドクロス検知!\nUSD/JPY: {price}円\n⏰ {now}")
    else:
        print(f"[{now}] シグナルなし|USD/JPY: {price}円")

def lambda_handler(event, context):
    check_signal()
    return {"statusCode": 200, "body": "OK"}

AWS Lambdaへのデプロイ手順

1. Lambda関数を作成

AWSコンソール → Lambda → 関数の作成

  • 関数名:fx-alert
  • ランタイム:Python 3.12(CloudShellのPythonバージョンに合わせるため)
  • リージョン:ap-northeast-1(東京)

2. デプロイパッケージをAWS CloudShellで作成

⚠️ Windowsローカルでpip installしたライブラリはLambdaで動きません。

LambdaはLinux環境で動くため、Windows上でインストールしたnumpyなどのバイナリは使えません。AWS CloudShellはブラウザ上で使えるLinux環境なので、ここでパッケージを作成します。

AWSコンソール右上の >_ アイコンからCloudShellを開いて以下を実行:

mkdir fx_lambda && cd fx_lambda

# Linux用バイナリを明示的に指定してインストール
pip install yfinance requests pandas \
  --target . \
  --only-binary=:all: \
  --platform manylinux2014_x86_64 \
  --python-version 3.12

# fx_alert.pyを作成(上記のコードを貼り付け)
cat > fx_alert.py << 'EOF'
# ここに上記のコードを貼り付け
EOF

# zip化
zip -r ~/fx_alert.zip .

3. S3経由でアップロード

yfinance + numpy + pandasを含めるとzipが70MBを超え、Lambdaへの直接アップロード制限に引っかかります。S3経由なら250MBまで対応できます。

# S3バケット作成
aws s3 mb s3://fx-alert-deploy-YYYYMMDD --region ap-northeast-1

# S3にアップロード
aws s3 cp ~/fx_alert.zip s3://fx-alert-deploy-YYYYMMDD/

# Lambdaにデプロイ
aws lambda update-function-code \
  --function-name fx-alert \
  --s3-bucket fx-alert-deploy-YYYYMMDD \
  --s3-key fx_alert.zip \
  --region ap-northeast-1

# 使い終わったらS3を削除(コスト節約)
aws s3 rm s3://fx-alert-deploy-YYYYMMDD/fx_alert.zip
aws s3 rb s3://fx-alert-deploy-YYYYMMDD

4. ハンドラとタイムアウトを設定

Lambdaのデフォルトハンドラは lambda_function.lambda_handler ですが、ファイル名が fx_alert.py なので変更が必要です。またyfinanceのデータ取得に時間がかかるため、タイムアウトも60秒に伸ばします(デフォルトは3秒)。

aws lambda update-function-configuration \
  --function-name fx-alert \
  --handler fx_alert.lambda_handler \
  --timeout 60 \
  --region ap-northeast-1

5. 環境変数にWebhook URLを登録

aws lambda update-function-configuration \
  --function-name fx-alert \
  --environment "Variables={DISCORD_WEBHOOK_URL=https://discordapp.com/api/webhooks/YOUR_WEBHOOK_URL}" \
  --region ap-northeast-1

AWSコンソールからも設定できます:Lambda → fx-alert → 設定タブ → 環境変数 → 編集

6. 動作確認

aws lambda invoke \
  --function-name fx-alert \
  --region ap-northeast-1 \
  response.json && cat response.json

{"statusCode": 200, "body": "OK"} が返れば成功です。

7. EventBridgeで定期実行を設定

# 1時間ごとのルール作成
aws events put-rule \
  --name fx-alert-schedule \
  --schedule-expression "rate(1 hour)" \
  --state ENABLED \
  --region ap-northeast-1

# Lambda実行権限を付与
aws lambda add-permission \
  --function-name fx-alert \
  --statement-id fx-alert-event \
  --action lambda:InvokeFunction \
  --principal events.amazonaws.com \
  --source-arn $(aws events describe-rule --name fx-alert-schedule --region ap-northeast-1 --query 'Arn' --output text) \
  --region ap-northeast-1

# ターゲットにLambdaを設定
aws events put-targets \
  --rule fx-alert-schedule \
  --targets "Id=fx-alert,Arn=$(aws lambda get-function-configuration --function-name fx-alert --region ap-northeast-1 --query 'FunctionArn' --output text)" \
  --region ap-northeast-1

ハマりポイントまとめ

① WindowsのライブラリはLambdaで動かない

numpyなどはOS依存のバイナリを含むため、Windowsでインストールしたものはそのまま使えません。
→ AWS CloudShellでLinux用バイナリを明示してインストールする

② Pythonバージョンのミスマッチ

CloudShellのPythonは3.9ですが、Lambda側を3.14にしていたためバージョン不一致でエラーになりました。
→ LambdaのランタイムをPython 3.12に統一する

③ zipが70MBを超える

yfinance + numpy + pandasを含めるとzipが70MB超になり、直接アップロードできません。
→ S3経由でアップロードする(250MB制限)

④ タイムアウトのデフォルトは3秒

yfinanceのデータ取得に数秒かかるため、デフォルトの3秒ではタイムアウトします。
→ タイムアウトを60秒に変更する


費用

サービス 費用
Lambda 無料(月100万回まで)
EventBridge 無料
S3 デプロイ後に削除すれば無料

合計:ほぼ無料で運用できます。


注意事項

  • yfinanceは15〜20分の遅延があります。スキャルピングには使えません。デイトレード向けです
  • あくまでシグナル通知のみです。売買判断・実行は手動です
  • 投資は自己責任でお願いします

おわりに

今回は通知機能(それもラグのある・・・)のみですが、今後は以下の実装もしてみたいです。

  • OANDA APIでリアルタイムデータに移行
  • 自動売買ロジックの実装
  • バックテストによる戦略の検証
0
2
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
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?