複数の危機指標を1本の危機スコアにまとめ、SQLiteへ保存する

複数の危機指標を1本の危機スコアにまとめ、SQLiteへ保存する

前回までに、FREDやStooqから取得した市場データをもとに、危機監視用の特徴量を作成できるようにしました。

具体的には、VIX、HY OAS、BBB OAS、イールドカーブ、NFCI、ANFCI、SOFR、SLOOS、金、原油、ドル円などを整形し、research.sqlitecrisis_features テーブルへ保存するところまで進めています。

ただし、特徴量を作っただけでは、まだ少し使いづらいです。

たとえば、ある日の状態を見たときに、次のような判断が必要になります。

  • VIXは高いのか
  • 信用スプレッドは広がっているのか
  • NFCIやANFCIは悪化しているのか
  • イールドカーブは危険方向に動いているのか
  • 全体として通常なのか、警戒すべきなのか

これを毎回、人間が個別に見て判断するのは大変です。

そこで今回は、複数の危機特徴量をまとめて、0〜100点の「危機スコア」に変換する build_crisis_score.py を作成しました。

この記事では、危機スコアの考え方、実装、実行結果、SQLite上での確認までを整理します。

今回作るもの

今回作る build_crisis_score.py の役割は、バラバラの危機特徴量を1本の「危険度メーター」にまとめることです。

処理の流れは次のようになります。

  1. research.sqlitecrisis_features を読み込む
  2. スコア計算に使う特徴量だけを横持ちに戻す
  3. 各特徴量の向きをそろえる
  4. 0〜100点の危機スコアを計算する
  5. normal / caution / warning / risk_off の状態判定を付ける
  6. crisis_score テーブルへ保存する

最終的には、次のようなテーブルを作ります。

date        score   regime
2026-03-10  22.5   normal
2026-03-11  38.1   caution
2026-03-12  67.4   warning
2026-03-13  81.2   risk_off

これにより、個別の特徴量を見るだけでなく、「今の市場環境がどの程度危険なのか」を1つの数値として確認できるようになります。

前回までの流れ

ここまでの処理は、大きく3段階に分けています。

  1. fetch_fred_to_sqlite.py で市場データを取得する
  2. build_crisis_features.py で危機監視用の特徴量を作る
  3. build_crisis_score.py で特徴量を危機スコアにまとめる

イメージとしては、build_crisis_features.py は材料を切る工程、build_crisis_score.py は材料を料理して最終判断しやすくする工程です。

特徴量のままだと、VIX、信用スプレッド、金融環境指数、イールドカーブなどをそれぞれ見て判断する必要があります。

危機スコアに変換しておくことで、後から可視化したり、過去の危機局面と比較したりしやすくなります。

危機スコアに使う特徴量

今回は、まずシンプルに次の8つの特徴量を使います。

  • vix_z20
  • hy_oas_z20
  • bbb_oas_z20
  • hy_minus_bbb_z20
  • nfci_z20
  • anfci_z20
  • yc_10y_2y_z20
  • yc_10y_3m_z20

z20 は、20日ベースのz-scoreです。

ざっくり言えば、「直近20日と比べて、どのくらい異常な水準にあるか」を見るための値です。

このうち、VIX、信用スプレッド、NFCI、ANFCIは、値が高いほど危険方向と考えます。

一方で、イールドカーブ系は少し扱いが異なります。

yc_10y_2y_z20yc_10y_3m_z20 は、低下方向、つまり逆イールド方向に進むほど危険として扱いたいため、スコア計算時に符号を反転します。

regime 判定の考え方

スコアは0〜100点で計算し、次のように状態名を付けます。

  • normal:通常
  • caution:注意
  • warning:警戒
  • risk_off:退避寄り

今回の初期ルールでは、次の閾値にしています。

0以上 35未満   : normal
35以上 60未満  : caution
60以上 80未満  : warning
80以上         : risk_off

この閾値は、最初から完成形として決め打ちするものではありません。

まずは初期版として動かし、過去の危機局面でどのように反応するかを見ながら調整していく前提です。

build_crisis_score.py を作成する

今回作成したスクリプトは次の通りです。

import sqlite3
import pandas as pd
import numpy as np

RESEARCH_DB = "research.sqlite"

# 危機スコアの開始日
SCORE_START_DATE = "1997-01-01"

# True にすると保存前に crisis_score を全削除
RESET_CRISIS_SCORE = True

# 使用する特徴量と重み
# 最初はシンプルに、危機検知に効きやすいものを中心にする
SCORE_FEATURES = {
    "vix_z20": 1.2,
    "hy_oas_z20": 1.4,
    "bbb_oas_z20": 1.0,
    "hy_minus_bbb_z20": 1.2,
    "nfci_z20": 1.0,
    "anfci_z20": 1.0,
    "yc_10y_2y_z20": 0.8,
    "yc_10y_3m_z20": 0.8,
}

# 値が高いほど危険な方向にそろえるための符号
# イールドカーブ系は z が低いほど危険になりやすいので -1 をかける
FEATURE_DIRECTION = {
    "vix_z20": 1.0,
    "hy_oas_z20": 1.0,
    "bbb_oas_z20": 1.0,
    "hy_minus_bbb_z20": 1.0,
    "nfci_z20": 1.0,
    "anfci_z20": 1.0,
    "yc_10y_2y_z20": -1.0,
    "yc_10y_3m_z20": -1.0,
}

def load_features_wide(db_path: str, feature_weights: dict[str, float]) -> pd.DataFrame:
    feature_names = list(feature_weights.keys())
    placeholders = ",".join(["?"] * len(feature_names))

    sql = f"""
    SELECT date, feature_name, value
    FROM crisis_features
    WHERE feature_name IN ({placeholders})
    ORDER BY date
    """

    with sqlite3.connect(db_path) as conn:
        df = pd.read_sql_query(sql, conn, params=feature_names)

    if df.empty:
        raise RuntimeError("crisis_features から対象特徴量を取得できませんでした")

    df["date"] = pd.to_datetime(df["date"])
    df["value"] = pd.to_numeric(df["value"], errors="coerce")

    wide = df.pivot(index="date", columns="feature_name", values="value").sort_index()
    wide.columns.name = None
    return wide

def normalize_to_unit_interval(s: pd.Series, clip_min: float = -3.0, clip_max: float = 3.0) -> pd.Series:
    x = s.clip(lower=clip_min, upper=clip_max)
    return (x - clip_min) / (clip_max - clip_min)

def compute_score(wide: pd.DataFrame) -> pd.DataFrame:
    df = wide.copy()

    # スコア対象期間
    df = df[df.index >= SCORE_START_DATE]

    if df.empty:
        raise RuntimeError(f"{SCORE_START_DATE} 以降のデータがありません")

    # 方向調整後の値を作る
    oriented_cols = []
    for col in SCORE_FEATURES:
        if col not in df.columns:
            df[col] = np.nan

        directed = df[col] * FEATURE_DIRECTION[col]
        adj_col = f"{col}_adj"
        df[adj_col] = directed
        oriented_cols.append(adj_col)

    # 全特徴量が揃っている日だけでスコア計算
    adj_df = df[oriented_cols].copy()
    valid_mask = adj_df.notna().all(axis=1)

    # 各特徴量を 0〜1 に押し込む
    norm_df = pd.DataFrame(index=adj_df.index)
    for col in oriented_cols:
        norm_df[col] = normalize_to_unit_interval(adj_df[col])

    # 重み付き平均
    weights = np.array([SCORE_FEATURES[col.replace("_adj", "")] for col in oriented_cols], dtype=float)
    weight_sum = weights.sum()

    score_raw = norm_df.mul(weights, axis=1).sum(axis=1) / weight_sum
    score_100 = score_raw * 100.0

    df["score"] = np.where(valid_mask, score_100, np.nan)

    # regime 判定
    df["regime"] = np.select(
        [
            df["score"] >= 80,
            df["score"] >= 60,
            df["score"] >= 35,
        ],
        [
            "risk_off",
            "warning",
            "caution",
        ],
        default="normal"
    )

    # score が NaN の日は regime も null 扱い
    df.loc[df["score"].isna(), "regime"] = None

    # 補助列
    df["valid_feature_count"] = adj_df.notna().sum(axis=1)
    df["required_feature_count"] = len(oriented_cols)

    # 出力列
    keep_cols = list(SCORE_FEATURES.keys()) + ["score", "regime", "valid_feature_count", "required_feature_count"]
    out = df[keep_cols].copy()
    out.index.name = "date"
    return out

def init_research_db(db_path: str) -> None:
    with sqlite3.connect(db_path) as conn:
        conn.execute("""
        CREATE TABLE IF NOT EXISTS crisis_score (
            date TEXT PRIMARY KEY,
            score REAL,
            regime TEXT,
            valid_feature_count INTEGER,
            required_feature_count INTEGER
        );
        """)
        conn.commit()

def reset_crisis_score_table(db_path: str) -> None:
    with sqlite3.connect(db_path) as conn:
        conn.execute("DELETE FROM crisis_score;")
        conn.commit()

def save_score(db_path: str, score_df: pd.DataFrame) -> int:
    out = score_df.reset_index()[["date", "score", "regime", "valid_feature_count", "required_feature_count"]].copy()
    out["date"] = pd.to_datetime(out["date"]).dt.strftime("%Y-%m-%d")
    out = out.replace({np.nan: None})

    rows = list(out.itertuples(index=False, name=None))

    with sqlite3.connect(db_path) as conn:
        cur = conn.executemany("""
            INSERT OR REPLACE INTO crisis_score(
                date, score, regime, valid_feature_count, required_feature_count
            )
            VALUES (?, ?, ?, ?, ?)
        """, rows)
        conn.commit()
        return cur.rowcount

def main():
    print("Loading crisis features from research.sqlite ...")
    wide = load_features_wide(RESEARCH_DB, SCORE_FEATURES)
    print(f"Loaded columns: {list(wide.columns)}")
    print(f"Raw shape: {wide.shape}")
    print(f"Raw date range: {wide.index.min().date()} -> {wide.index.max().date()}")

    print("Computing crisis score ...")
    score_df = compute_score(wide)
    print(f"Score start date: {SCORE_START_DATE}")
    print(f"Score shape: {score_df.shape}")
    print(f"Score date range: {score_df.index.min().date()} -> {score_df.index.max().date()}")
    print("Latest rows:")
    print(score_df.tail(10).to_string())

    print("Preparing research.sqlite ...")
    init_research_db(RESEARCH_DB)

    if RESET_CRISIS_SCORE:
        print("Resetting existing crisis_score ...")
        reset_crisis_score_table(RESEARCH_DB)

    print("Saving to research.sqlite ...")
    n = save_score(RESEARCH_DB, score_df)
    print(f"Upserted {n} rows into crisis_score")

if __name__ == "__main__":
    main()

スコア計算のポイント

今回の処理では、各特徴量を次のように扱っています。

  • vix_z20:高いほど危険
  • hy_oas_z20:高いほど危険
  • bbb_oas_z20:高いほど危険
  • hy_minus_bbb_z20:高いほど危険
  • nfci_z20 / anfci_z20:高いほど危険
  • yc_10y_2y_z20 / yc_10y_3m_z20:低いほど危険なので、内部で符号を反転

そのあと、各値を -3+3 の範囲にクリップし、0〜1に変換しています。

最後に、特徴量ごとの重みをかけて平均し、0〜100点のスコアに変換します。

かなり単純な方式ですが、初期版としては扱いやすいです。

複雑なモデルにする前に、まずは「どの指標が効いて、どのような日にスコアが上がるのか」を確認しやすい形にしています。

実行する

スクリプトを実行します。

python build_crisis_score.py

実行結果は次のようになりました。

Loading crisis features from research.sqlite ...
Loaded columns: ['anfci_z20', 'bbb_oas_z20', 'hy_minus_bbb_z20', 'hy_oas_z20', 'nfci_z20', 'vix_z20', 'yc_10y_2y_z20', 'yc_10y_3m_z20']
Raw shape: (13221, 8)
Raw date range: 1990-01-01 -> 2026-03-13
Computing crisis score ...
Score start date: 1997-01-01
Score shape: (10664, 12)
Score date range: 1997-01-01 -> 2026-03-13
Latest rows:
             vix_z20  hy_oas_z20  bbb_oas_z20  hy_minus_bbb_z20  nfci_z20  anfci_z20  yc_10y_2y_z20  yc_10y_3m_z20      score   regime  valid_feature_count  required_feature_count
date
2026-03-04  0.807812    0.052784     0.296578         -0.090732  1.276155   1.271257      -2.056812       0.833333  59.438821  caution                    8                       8
2026-03-05  2.348306    0.374235     0.548017          0.262678  1.154408   1.150212      -1.504600       2.079823  62.003113  warning                    8                       8
2026-03-06  3.463609    1.650751     1.060845          1.916295  2.025600   2.031908      -0.285523       2.342101  73.181938  warning                    8                       8
2026-03-07  2.600021    1.462178     0.960036          1.672412  1.777279   1.782367      -0.201299       1.968407  70.396756  warning                    8                       8
2026-03-08  2.135261    1.307370     0.868604          1.479875  1.592672   1.597124      -0.101895       1.705840  67.745484  warning                    8                       8
2026-03-09  0.959510    1.644490     1.060392          1.852276  1.451470   1.455653      -1.471454       0.663579  70.417150  warning                    8                       8
2026-03-10  0.734039    0.398062     0.682883          0.265274  1.344845   1.349088      -0.380809       1.157302  58.952546  caution                    8                       8
2026-03-11  0.496507    0.606357     1.462599          0.200174  1.272788   1.277532      -0.837150       1.943590  59.548978  caution                    8                       8
2026-03-12  1.174326    1.286405     2.076794          0.811393  1.151525   1.155586      -2.917442       2.247268  68.063244  warning                    8                       8
2026-03-13  1.061141    1.206051     1.860624          0.740831  1.046573   1.050116      -2.327336       1.906621  66.160164  warning                    8                       8
Preparing research.sqlite ...
Resetting existing crisis_score ...
Saving to research.sqlite ...
Upserted 10664 rows into crisis_score

実行結果を確認する

まず、対象の8特徴量は正しく読み込めています。

Loaded columns: ['anfci_z20', 'bbb_oas_z20', 'hy_minus_bbb_z20', 'hy_oas_z20', 'nfci_z20', 'vix_z20', 'yc_10y_2y_z20', 'yc_10y_3m_z20']
Raw shape: (13221, 8)

crisis_features から、スコア計算に必要な8系列を取得できています。

また、スコア対象期間は 1997-01-01 以降にしています。

Score start date: 1997-01-01
Score shape: (10664, 12)
Score date range: 1997-01-01 -> 2026-03-13

1997年以降にしている理由は、HY OASやBBB OASなどの信用スプレッド系が揃いやすくなるためです。

直近では、必要な特徴量がすべて揃っています。

valid_feature_count = 8
required_feature_count = 8

つまり、直近のスコアは8つの特徴量すべてを使って計算できています。

直近の判定を見る

直近の結果を見ると、2026-03-13 時点では次のようになっています。

2026-03-13
score  = 66.160164
regime = warning

今回のルールでは、60以上80未満は warning です。

つまり、通常よりもかなり警戒が必要な状態として判定されています。

直近の流れを見ると、次のようになっています。

  • 2026-03-05 〜 2026-03-09:warning
  • 2026-03-10 〜 2026-03-11:caution
  • 2026-03-12 〜 2026-03-13:再び warning

この結果だけを見ると、危機度はやや高止まりしているように見えます。

特に、VIX、信用スプレッド、NFCI、ANFCI、イールドカーブが複合的に効いて、warning に入っている状態です。

SQLiteで保存結果を確認する

スクリプト上では保存成功と表示されていますが、念のためSQLite側でも確認します。

sqlite3 research.sqlite

全体件数を確認する

まず、crisis_score テーブルの期間と件数を確認します。

SELECT
  MIN(date) AS min_date,
  MAX(date) AS max_date,
  COUNT(*) AS total_rows,
  COUNT(score) AS non_null_score_rows
FROM crisis_score;

結果です。

1997-01-01|2026-03-13|10664|10641

total_rows は10664行、score が入っている行は10641行です。

差分は23行あります。

この23行は、主に開始直後に必要な特徴量がまだ揃わず、スコアを計算できなかった日です。

z-scoreやOAS系の開始タイミングを考えると、この程度のNULLは自然です。

regime の分布を確認する

次に、状態判定の分布を確認します。

SELECT regime, COUNT(*)
FROM crisis_score
GROUP BY regime
ORDER BY regime;

結果です。

|23
caution|5819
normal|2159
risk_off|172
warning|2491

空欄の23行は、スコアがNULLだった行です。

分布を見ると、risk_off は172行だけなので、本当に強い警戒日に絞られています。

一方で、caution が5819行と多めです。

これは、今回の初期ルールがやや警戒寄りに出る設定になっていることを示しています。

危機監視レーダーの初期版としては、見逃しを減らす意味で悪くありません。

ただし、実運用に近づける場合は、閾値の再調整が必要になりそうです。

直近20件を確認する

直近の状態も確認します。

SELECT *
FROM crisis_score
ORDER BY date DESC
LIMIT 20;

結果です。

2026-03-13|66.1601642050738|warning|8|8
2026-03-12|68.0632443681154|warning|8|8
2026-03-11|59.5489775950895|caution|8|8
2026-03-10|58.9525456470283|caution|8|8
2026-03-09|70.417150420088|warning|8|8
2026-03-08|67.7454844082668|warning|8|8
2026-03-07|70.3967555220573|warning|8|8
2026-03-06|73.181937747609|warning|8|8
2026-03-05|62.003113251451|warning|8|8
2026-03-04|59.4388207509542|caution|8|8
2026-03-03|75.8919993309965|warning|8|8
2026-03-02|69.8629812897748|warning|8|8
2026-03-01|75.4666737319145|warning|8|8
2026-02-28|79.0368612873017|warning|8|8
2026-02-27|82.9290503469762|risk_off|8|8
2026-02-26|67.1483747736185|warning|8|8
2026-02-25|61.6260975193054|warning|8|8
2026-02-24|68.2708045600426|warning|8|8
2026-02-23|70.2039103816834|warning|8|8
2026-02-22|55.9179940527823|caution|8|8

直近20日では、cautionwarning が多く、2026-02-27には risk_off も出ています。

この時点では、スコアの出力自体は正常で、かつ市場ストレスが高めに出ていることが分かります。

スコアの範囲を確認する

次に、スコア全体の最小値、最大値、平均値を確認します。

SELECT
  MIN(score) AS min_score,
  MAX(score) AS max_score,
  AVG(score) AS avg_score
FROM crisis_score
WHERE score IS NOT NULL;

結果です。

10.4194420962834|95.8463821606999|48.8659829060574

スコアは10台から95台まで広がっています。

0〜100の範囲にある程度きれいに分布しており、極端に圧縮されすぎているわけでも、すぐに100付近へ張り付いているわけでもありません。

この点は初期版としてかなり良さそうです。

ただし、平均値が約48.9なので、現在の閾値だと caution が出やすい設計になっています。

高スコア日を確認する

危機スコアが本当にそれらしい動きをしているかを見るために、高スコア日も確認します。

SELECT *
FROM crisis_score
WHERE score IS NOT NULL
ORDER BY score DESC
LIMIT 30;

結果です。

2010-05-07|95.8463821606999|risk_off|8|8
2015-12-11|93.3092821957565|risk_off|8|8
2021-11-26|91.7035074032255|risk_off|8|8
2020-02-24|91.542824771873|risk_off|8|8
2014-12-12|91.3011661827053|risk_off|8|8
2017-08-11|91.2436196337221|risk_off|8|8
2014-01-24|90.9186781571242|risk_off|8|8
2010-05-06|90.8914322713623|risk_off|8|8
2012-04-09|90.6755493669421|risk_off|8|8
2011-08-04|90.5537196635391|risk_off|8|8
2019-08-05|90.4771314565138|risk_off|8|8
2025-04-04|90.430609983188|risk_off|8|8
2008-11-20|90.4095448819216|risk_off|8|8
2012-05-18|90.3618194167048|risk_off|8|8
2020-02-25|90.3572181114431|risk_off|8|8
2012-04-10|90.2671832128906|risk_off|8|8
2019-08-02|89.8603388690827|risk_off|8|8
2015-01-06|89.7160919089642|risk_off|8|8
2005-08-12|89.3633469118001|risk_off|8|8
2011-08-08|89.3315833141199|risk_off|8|8
2012-05-17|89.2828788634447|risk_off|8|8
2011-08-05|88.728786140397|risk_off|8|8
2007-07-20|88.5542891280665|risk_off|8|8
2011-09-22|88.5004086115123|risk_off|8|8
2019-05-07|88.3930700778644|risk_off|8|8
2008-11-21|88.1187676954605|risk_off|8|8
2011-03-16|87.8833871024975|risk_off|8|8
1999-09-24|87.6823211868518|risk_off|8|8
2010-05-08|87.5707801679763|risk_off|8|8
2006-05-19|87.0409367188216|risk_off|8|8

上位日を見ると、かなりそれらしい日付が並んでいます。

  • 2008年11月:金融危機局面
  • 2010年5月:フラッシュクラッシュ前後
  • 2011年8月:欧州債務危機・米国格下げ局面
  • 2020年2月:コロナショック初動
  • 2019年8月:市場が荒れた局面

もちろん、これだけで完成とは言えません。

ただ、少なくとも「明らかに市場が荒れた日」を上位に持ってくる力はありそうです。

今回の結果から分かったこと

今回の build_crisis_score.py は、初期版としては正常に動いています。

確認できたことは次の通りです。

  • crisis_features から必要な8特徴量を取得できた
  • 1997年以降のスコアを計算できた
  • crisis_score テーブルへ保存できた
  • 直近では必要特徴量がすべて揃っている
  • 高スコア日に過去の市場混乱期が含まれている

一方で、改善点もあります。

特に、normal よりも caution が多いため、今の閾値はやや警戒寄りです。

これは資産保全型の危機監視レーダーとしては悪くありませんが、日常的に使う場合は少し過敏かもしれません。

今後の調整候補

次に調整するなら、主に3つあります。

1. regime 閾値の再調整

現在は次の設定です。

normal   : 35未満
caution  : 35以上 60未満
warning  : 60以上 80未満
risk_off : 80以上

ただ、caution が多いため、たとえば次のように閾値を上げる案があります。

normal   : 45未満
caution  : 45以上 65未満
warning  : 65以上 85未満
risk_off : 85以上

このあたりは、過去の危機局面での反応を見ながら決めるのがよさそうです。

2. 特徴量の重み調整

現在は、VIXやOAS系がやや強めに効く設計です。

そのため、信用スプレッドやボラティリティが動いたときに、スコアが上がりやすくなります。

今後は、NFCI、ANFCI、イールドカーブとのバランスを見ながら、重みを調整する余地があります。

3. 過去危機イベントとの照合

一番重要なのは、過去の危機イベントと照合することです。

たとえば、次のような局面でスコアがどう動いたかを確認します。

  • 1998年:LTCM危機
  • 2000年前後:ITバブル崩壊
  • 2008年:リーマンショック
  • 2011年:欧州債務危機
  • 2020年:コロナショック
  • 2023年:米地銀不安

単に危機当日にスコアが高いかを見るだけでなく、危機の何日前から上がり始めたかを見る必要があります。

ここが確認できると、危機監視レーダーとしての実用性をかなり判断しやすくなります。

注意点

今回作った危機スコアは、投資判断を自動化するための完成モデルではありません。

あくまで、市場環境を俯瞰するための監視指標です。

特に、スコアが高いから必ず下落する、スコアが低いから安全、というものではありません。

市場の状態を整理するための補助指標として使い、過去データで妥当性を確認しながら調整していく必要があります。

まとめ

今回は、複数の危機特徴量を1本の危機スコアにまとめる build_crisis_score.py を作成しました。

research.sqlitecrisis_features から必要な特徴量を読み込み、0〜100点のスコアと normal / caution / warning / risk_off の状態判定を作成しています。

実行結果として、crisis_score テーブルに10664行を保存できました。

高スコア日には、2008年の金融危機、2010年のフラッシュクラッシュ前後、2011年の欧州債務危機、2020年のコロナショック初動などが含まれており、初期版としてはかなりそれらしい動きになっています。

一方で、現在の閾値では caution が多めに出ているため、今後は過去危機イベントと照合しながら、閾値や重みを調整していきます。

次回は、過去の主要危機日を指定して、その前後で crisis_score がどう動いたかを確認するSQLまたはスクリプトを作成します。

カテゴリ

開発ログ

タグ候補

Python, SQLite, FRED, 金融データ, 危機監視, 市場分析, pandas, データ分析

タイトル候補

  • 複数の危機指標を1本の危機スコアにまとめ、SQLiteへ保存する
  • VIX・OAS・NFCI・イールドカーブから危機スコアを作る
  • Pythonで市場の危機監視スコアを作成し、SQLiteへ保存する

メタディスクリプション案

FREDやStooqから取得した市場データをもとに、VIX、信用スプレッド、NFCI、イールドカーブなどの特徴量を1本の危機スコアへ変換し、SQLiteへ保存するPythonスクリプトを作成した記録です。

コメント

タイトルとURLをコピーしました