{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":96164,"databundleVersionId":11418275,"sourceType":"competition"}],"dockerImageVersionId":31041,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# ■■ 0) 必要ライブラリのインポート ■■\nimport pandas as pd\nimport numpy as np\nfrom scipy.stats import pearsonr\nfrom sklearn.preprocessing import StandardScaler\nimport lightgbm as lgb","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T04:11:44.868480Z","iopub.execute_input":"2025-06-30T04:11:44.868837Z","iopub.status.idle":"2025-06-30T04:11:44.872679Z","shell.execute_reply.started":"2025-06-30T04:11:44.868815Z","shell.execute_reply":"2025-06-30T04:11:44.871917Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ■■ 1) データ読み込み & インデックス設定 ■■\ntrain = pd.read_parquet('/kaggle/input/drw-crypto-market-prediction/train.parquet')\n# 既に DatetimeIndex なら不要ですが念のため\nif not isinstance(train.index, pd.DatetimeIndex):\n    train.index = pd.to_datetime(train['timestamp'])\ntrain = train.sort_index()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T04:11:44.873959Z","iopub.execute_input":"2025-06-30T04:11:44.874135Z","iopub.status.idle":"2025-06-30T04:11:51.769231Z","shell.execute_reply.started":"2025-06-30T04:11:44.874121Z","shell.execute_reply":"2025-06-30T04:11:51.768435Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ■■ 2) Fold 列の作成（6 期間に分けた時系列 CV 用） ■■\ntrain['Fold'] = 0\ntrain.loc['2023-03-01':'2023-05-01','Fold'] = 1\ntrain.loc['2023-05-01':'2023-07-01','Fold'] = 2\ntrain.loc['2023-07-01':'2023-09-01','Fold'] = 3\ntrain.loc['2023-09-01':'2023-11-01','Fold'] = 4\ntrain.loc['2023-11-01':'2024-01-01','Fold'] = 5\ntrain.loc['2024-01-01':'2024-03-01','Fold'] = 6\nprint(train['Fold'].value_counts().sort_index())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T04:11:51.770031Z","iopub.execute_input":"2025-06-30T04:11:51.770279Z","iopub.status.idle":"2025-06-30T04:11:51.833192Z","shell.execute_reply.started":"2025-06-30T04:11:51.770254Z","shell.execute_reply":"2025-06-30T04:11:51.832396Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ■■ 3) 短期モメンタム & EMA 差分の計算 ■■\n\n# --- 既存 --- \ntrain['momentum_1m'] = train['label'] - train['label'].shift(1)\ntrain['momentum_5m'] = train['label'] - train['label'].shift(5)\ntrain['ema_5']  = train['label'].ewm(span=5,  adjust=False).mean()\ntrain['ema_10'] = train['label'].ewm(span=10, adjust=False).mean()\ntrain['ema_diff'] = train['ema_5'] - train['ema_10']\n# 欠損（先頭数行）をゼロ埋め\ntrain[['momentum_1m','momentum_5m','ema_diff']] = \\\n    train[['momentum_1m','momentum_5m','ema_diff']].fillna(0)\n\n# ←ここまで既存のコードです。以下を追加します。↓\n\n# ■■ 追加：マルチタイムスケール・モメンタム ■■\nfor span in [15, 30, 60]:\n    col = f'momentum_{span}m'\n    train[col] = train['label'] - train['label'].shift(span)\n\n# 欠損を 0 埋め\nmom_cols = [f'momentum_{span}m' for span in [15,30,60]]\ntrain[mom_cols] = train[mom_cols].fillna(0)\n\n# 追加分の動作確認\nprint(\"追加したモメンタム列：\", mom_cols)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T04:14:43.109130Z","iopub.execute_input":"2025-06-30T04:14:43.109763Z","iopub.status.idle":"2025-06-30T04:14:43.184030Z","shell.execute_reply.started":"2025-06-30T04:14:43.109740Z","shell.execute_reply":"2025-06-30T04:14:43.183421Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ■■ 4) 特徴量リスト・目的変数の定義 ■■\n# ■■ 4) 特徴量リストに追加 ■■\nfeatures = ['momentum_1m','momentum_5m','ema_diff'] + mom_cols\n\ntarget   = 'label'\nfold_col = 'Fold'\nprint(\"使用特徴量：\", features)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T04:14:58.105064Z","iopub.execute_input":"2025-06-30T04:14:58.105620Z","iopub.status.idle":"2025-06-30T04:14:58.109851Z","shell.execute_reply.started":"2025-06-30T04:14:58.105599Z","shell.execute_reply":"2025-06-30T04:14:58.109013Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ■■ 5) 特徴量リスト・目的変数の定義（マルチタイムスケール・モメンタムを追加） ■■\nmom_cols = [f'momentum_{span}m' for span in [1,5,15,30,60]]\nfeatures = mom_cols + ['ema_diff']\ntarget   = 'label'\nfold_col = 'Fold'\nprint(\"使用特徴量：\", features)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T04:15:44.003321Z","iopub.execute_input":"2025-06-30T04:15:44.003907Z","iopub.status.idle":"2025-06-30T04:15:44.008528Z","shell.execute_reply.started":"2025-06-30T04:15:44.003884Z","shell.execute_reply":"2025-06-30T04:15:44.007715Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ■■ 6) 標準化（LightGBM は必須ではありませんが安定化のため） ■■\nfrom sklearn.preprocessing import StandardScaler\n\nscaler = StandardScaler()\ntrain[features] = scaler.fit_transform(train[features])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T04:16:16.203115Z","iopub.execute_input":"2025-06-30T04:16:16.203362Z","iopub.status.idle":"2025-06-30T04:16:16.304789Z","shell.execute_reply.started":"2025-06-30T04:16:16.203347Z","shell.execute_reply":"2025-06-30T04:16:16.304019Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ■■ 7) 時系列 CV + LightGBM 学習・評価 ■■\nimport numpy as np\nfrom scipy.stats import pearsonr\n\ndef pearsonr_score(y_true, y_pred):\n    return pearsonr(y_true, y_pred)[0]\n\nlgb_params = {\n    \"objective\":     \"regression\",\n    \"metric\":        \"mae\",\n    \"learning_rate\": 0.05,\n    \"num_leaves\":    32,\n    \"verbosity\":     -1,\n}\n\ncv_scores = []\nfor f in sorted(train[fold_col].unique()):\n    is_tr = train[fold_col] != f\n    is_va = train[fold_col] == f\n\n    dtr = lgb.Dataset(train.loc[is_tr, features], label=train.loc[is_tr, target])\n    dva = lgb.Dataset(train.loc[is_va, features], label=train.loc[is_va, target], reference=dtr)\n\n    model = lgb.train(\n        lgb_params,\n        dtr,\n        num_boost_round=200,\n        valid_sets=[dva],\n        callbacks=[\n            lgb.early_stopping(stopping_rounds=50),\n            lgb.log_evaluation(period=50),\n        ]\n    )\n\n    preds = model.predict(train.loc[is_va, features])\n    score = pearsonr_score(train.loc[is_va, target], preds)\n    print(f\"Fold {f} Pearson: {score:.4f}\")\n    cv_scores.append(score)\n\nprint(\"CV mean Pearson:\", np.mean(cv_scores))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T04:16:26.512150Z","iopub.execute_input":"2025-06-30T04:16:26.512634Z","iopub.status.idle":"2025-06-30T04:16:40.899863Z","shell.execute_reply.started":"2025-06-30T04:16:26.512611Z","shell.execute_reply":"2025-06-30T04:16:40.898970Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ■■■ Hyperparameter Optimization with Optuna (suppressing intermediate logs) ■■■\n\n# 0) 必要ライブラリのインポート\nimport optuna\nimport optuna.logging\noptuna.logging.set_verbosity(optuna.logging.WARNING)  # Optunaの情報ログを抑制\n\nimport lightgbm as lgb\nimport numpy as np\nfrom sklearn.model_selection import TimeSeriesSplit\nfrom scipy.stats import pearsonr\n\n# ——————————————————————————————————————————————\n# ★ 事前準備 ★\n# 以下は既に実行済みの前提です：\n#   ・train: pandas.DataFrame（DatetimeIndex, 'label' 列あり）\n#   ・features: モデルに使う特徴量リスト（例 ['momentum_1m','momentum_5m','momentum_15m', … ,'ema_diff']）\n#   ・target = 'label'\n# ——————————————————————————————————————————————\n\ndef objective(trial):\n    # 1) 探索するパラメータ空間\n    params = {\n        \"objective\":        \"regression\",\n        \"metric\":           \"mae\",\n        \"learning_rate\":    trial.suggest_loguniform(\"learning_rate\", 1e-3, 1e-1),\n        \"num_leaves\":       trial.suggest_int(\"num_leaves\", 16, 256),\n        \"min_data_in_leaf\": trial.suggest_int(\"min_data_in_leaf\", 10, 1000),\n        \"feature_fraction\": trial.suggest_uniform(\"feature_fraction\", 0.5, 1.0),\n        \"bagging_fraction\": trial.suggest_uniform(\"bagging_fraction\", 0.5, 1.0),\n        \"bagging_freq\":     trial.suggest_int(\"bagging_freq\", 1, 10),\n        \"verbosity\":        -1,\n    }\n\n    tss = TimeSeriesSplit(n_splits=3)\n    pearson_scores = []\n\n    # 2) 時系列CV（3分割）\n    for tr_idx, va_idx in tss.split(train):\n        dtr = lgb.Dataset(train.iloc[tr_idx][features], label=train.iloc[tr_idx][target])\n        dva = lgb.Dataset(train.iloc[va_idx][features], label=train.iloc[va_idx][target], reference=dtr)\n\n        # 3) モデル学習（コールバックで早期停止・ログ抑制）\n        gbm = lgb.train(\n            params,\n            dtr,\n            num_boost_round=200,\n            valid_sets=[dva],\n            callbacks=[\n                lgb.early_stopping(stopping_rounds=30),\n                lgb.log_evaluation(period=0)   # 学習ログを出さない\n            ]\n        )\n\n        preds = gbm.predict(train.iloc[va_idx][features])\n        pearson_scores.append(pearsonr(train.iloc[va_idx][target], preds)[0])\n\n    # 4) 各foldの平均Pearson相関を返す\n    return np.mean(pearson_scores)\n\n# 5) Optunaによる最適化実行\nstudy = optuna.create_study(direction=\"maximize\")\nstudy.optimize(objective, n_trials=50)\n\n# 6) 最終結果表示\nprint(\"▶ Best parameters:\", study.best_params)\nprint(f\"▶ Best CV mean Pearson: {study.best_value:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T04:50:16.734603Z","iopub.execute_input":"2025-06-30T04:50:16.734893Z","iopub.status.idle":"2025-06-30T05:08:35.843945Z","shell.execute_reply.started":"2025-06-30T04:50:16.734875Z","shell.execute_reply":"2025-06-30T05:08:35.843228Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\n# 1) テストデータ読み込み\ntest = pd.read_parquet('/kaggle/input/drw-crypto-market-prediction/test.parquet')\ntest.index = pd.to_datetime(test.index)  # インデックスが既に datetime の場合は不要\n\n# 2) 特徴量作成（訓練時と同じ処理を）\nfor delta in [1,5,15,30,60]:\n    test[f'momentum_{delta}m'] = test['label'].diff(periods=delta)  # テストは label=0 ダミーなので price列があればそちら\n# ※ 実際には price 列がないので、momentum は submission 用に別途渡された “price” を使う想定です。\n#    Kaggle の test.parquet には price 情報がないため、本番予測用には price 列を外部で用意するか、\n#    事前に price 相当のラベルを生成する必要があります。\n\n# ここでは訓練データ同様、momentum と ema_diff を作れる前提で示します。\ntest['ema5']  = test['label'].ewm(span=5).mean()\ntest['ema10'] = test['label'].ewm(span=10).mean()\ntest['ema_diff'] = test['ema5'] - test['ema10']\n\n# 3) 特徴量リスト\nfeatures = ['momentum_1m','momentum_5m','momentum_15m','momentum_30m','momentum_60m','ema_diff']\n\n# 4) 予測\npreds = model.predict(test[features])  # model はチューニング後の LightGBM\n\n# 5) 提出用 DataFrame 作成\nsubmission = pd.read_csv('/kaggle/input/drw-crypto-market-prediction/sample_submission.csv')\nsubmission['prediction'] = preds\n\n# 6) CSV 出力\nsubmission.to_csv('submission.csv', index=False)\nprint(\"submission.csv を作成しました。\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-30T05:15:15.493055Z","iopub.execute_input":"2025-06-30T05:15:15.493332Z","iopub.status.idle":"2025-06-30T05:15:39.225502Z","shell.execute_reply.started":"2025-06-30T05:15:15.493313Z","shell.execute_reply":"2025-06-30T05:15:39.224635Z"}},"outputs":[],"execution_count":null}]}