{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":96164,"databundleVersionId":12993472,"sourceType":"competition"}],"dockerImageVersionId":31259,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"94235c8f-d447-4f8b-9554-72ca3c1dced9","cell_type":"markdown","source":"# DRW Crypto Market Prediction — Solution Principale (Ridge)\n\n**Performance** : Corrélation validation = **0.1175** (89.7% du score gagnant)  \n**Architecture** : Feature Engineering → Sélection top 100 → Ridge Regression  \n**Évaluation** : Coefficient de corrélation de Pearson\n\n---\n\n```\nPipeline complet :\n895 features brutes\n     ↓ Feature engineering (ratios, rolling stats)\n~902 features enrichies\n     ↓ Sélection par corrélation avec la target\n100 meilleures features\n     ↓ Ridge Regression (α=1.0)\nPrédictions → Pearson ≈ 0.1175\n```","metadata":{}},{"id":"8bc6eb63-96b5-40b4-af88-ba744ebf347f","cell_type":"markdown","source":"## 0. Imports","metadata":{}},{"id":"0bc5711e-82de-4072-9648-ab224707176f","cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom scipy.stats import pearsonr\nfrom sklearn.linear_model import Ridge, Lasso, ElasticNet\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.metrics import mean_squared_error\nfrom sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor\nimport warnings\nwarnings.filterwarnings('ignore')\n\nplt.rcParams['figure.figsize'] = (12, 5)\nsns.set_style('whitegrid')\nSEED = 42\nnp.random.seed(SEED)\n\nprint('✅ Imports OK')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-17T10:15:59.913902Z","iopub.execute_input":"2026-02-17T10:15:59.914903Z","iopub.status.idle":"2026-02-17T10:15:59.921765Z","shell.execute_reply.started":"2026-02-17T10:15:59.914866Z","shell.execute_reply":"2026-02-17T10:15:59.920917Z"}},"outputs":[],"execution_count":null},{"id":"a45c2809-95f2-4ce6-aaca-59bd00e98fea","cell_type":"markdown","source":"## 1. Chargement des données","metadata":{}},{"id":"c0219c0f-ab07-4cdf-8489-94fca5a97a14","cell_type":"code","source":"# Kaggle\ntrain = pd.read_parquet('/kaggle/input/competitions/drw-crypto-market-prediction/train.parquet')\ntest  = pd.read_parquet('/kaggle/input/competitions/drw-crypto-market-prediction/test.parquet')\n\nprint(f'Train : {train.shape}')\nprint(f'Test  : {test.shape}')\n\nMARKET_FEATURES = ['bid_qty', 'ask_qty', 'buy_qty', 'sell_qty', 'volume']\nX_FEATURES = [c for c in train.columns if c.startswith('X')]\nTARGET = 'label'\n\nprint(f'\\nFeatures marché : {len(MARKET_FEATURES)}')\nprint(f'Features X      : {len(X_FEATURES)}')\nprint(f'Total features  : {len(MARKET_FEATURES) + len(X_FEATURES)}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-17T10:15:59.92358Z","iopub.execute_input":"2026-02-17T10:15:59.923838Z","iopub.status.idle":"2026-02-17T10:16:37.233478Z","shell.execute_reply.started":"2026-02-17T10:15:59.923815Z","shell.execute_reply":"2026-02-17T10:16:37.232696Z"}},"outputs":[],"execution_count":null},{"id":"29e72c8c-6351-4598-9800-df224cb192ba","cell_type":"markdown","source":"## 2. Feature Engineering\n\nOn crée des features supplémentaires à partir des 5 features marché :\n- **Ratios** : bid/ask, buy/sell\n- **Spread** : différence bid-ask\n- **Imbalance** : déséquilibre orderbook normalisé\n- **Rolling stats** : moyenne et écart-type sur fenêtres glissantes (5, 10, 20)","metadata":{}},{"id":"4f963ae9-fc30-41d1-afcf-8fc4fb791095","cell_type":"code","source":"def create_market_features(df):\n    \"\"\"\n    Crée des features d'ingénierie à partir des données de marché.\n    \n    Args:\n        df: DataFrame avec les colonnes bid_qty, ask_qty, buy_qty, sell_qty, volume\n    \n    Returns:\n        DataFrame enrichi avec les nouvelles features\n    \"\"\"\n    df = df.copy()\n    eps = 1e-8  # éviter division par zéro\n    \n    # --- Ratios et spreads ---\n    df['bid_ask_spread'] = df['ask_qty'] - df['bid_qty']\n    df['bid_ask_ratio']  = df['bid_qty'] / (df['ask_qty'] + eps)\n    df['buy_sell_ratio'] = df['buy_qty'] / (df['sell_qty'] + eps)\n    \n    # --- Imbalance orderbook ---\n    # > 0 : pression acheteuse, < 0 : pression vendeuse\n    df['quantity_imbalance'] = (\n        (df['bid_qty'] - df['ask_qty']) / \n        (df['bid_qty'] + df['ask_qty'] + eps)\n    )\n    df['trade_imbalance'] = (\n        (df['buy_qty'] - df['sell_qty']) / \n        (df['buy_qty'] + df['sell_qty'] + eps)\n    )\n    \n    # --- Volume normalisé ---\n    df['log_volume'] = np.log1p(df['volume'].clip(lower=0))\n    \n    # --- Rolling statistics sur volume ---\n    for window in [5, 10, 20]:\n        df[f'volume_rolling_mean_{window}'] = df['volume'].rolling(window, min_periods=1).mean()\n        df[f'volume_rolling_std_{window}']  = df['volume'].rolling(window, min_periods=1).std().fillna(0)\n    \n    return df\n\nprint('Création des features marché...')\ntrain_fe = create_market_features(train)\ntest_fe  = create_market_features(test)\n\nnew_cols = [c for c in train_fe.columns if c not in train.columns]\nprint(f'✅ {len(new_cols)} nouvelles features créées : {new_cols}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-17T10:16:37.234494Z","iopub.execute_input":"2026-02-17T10:16:37.234801Z","iopub.status.idle":"2026-02-17T10:16:40.793774Z","shell.execute_reply.started":"2026-02-17T10:16:37.234774Z","shell.execute_reply":"2026-02-17T10:16:40.792749Z"}},"outputs":[],"execution_count":null},{"id":"5ebad77a-edff-4c47-9c7c-fec3f8a8604c","cell_type":"markdown","source":"## 3. Sélection des features par corrélation\n\nOn sélectionne les **100 meilleures features** selon leur corrélation absolue de Pearson avec la target.\n\n**Pourquoi 100 ?**  \nCompromis signal/bruit : avec 895 features dont la plupart ont une corrélation < 0.01, garder toutes les features ajoute du bruit et dégrade la performance.","metadata":{}},{"id":"c59ada5f-9ed5-41d0-b773-9258e982035f","cell_type":"code","source":"def select_top_features(X_train, y_train, top_k=100):\n    \"\"\"\n    Sélectionne les top_k features par corrélation absolue avec la target.\n    \n    Args:\n        X_train: DataFrame de features (sur données de train uniquement)\n        y_train: Series de la target\n        top_k: nombre de features à conserver\n    \n    Returns:\n        Liste des noms des features sélectionnées\n    \"\"\"\n    print(f'Calcul des corrélations sur {X_train.shape[1]} features...')\n    correlations = {}\n    \n    for feat in X_train.columns:\n        try:\n            corr, _ = pearsonr(X_train[feat].fillna(0), y_train)\n            if not np.isnan(corr):\n                correlations[feat] = abs(corr)\n        except Exception:\n            pass\n    \n    # Tri décroissant\n    sorted_feats = sorted(correlations.items(), key=lambda x: x[1], reverse=True)\n    selected = [f for f, _ in sorted_feats[:top_k]]\n    \n    print(f'\\n=== Top 20 features sélectionnées ===')\n    for feat, corr in sorted_feats[:20]:\n        print(f'  {feat:30s} : |r| = {corr:.4f}')\n    \n    return selected\n\n\n# Colonnes disponibles pour la sélection\nall_feature_cols = X_FEATURES + MARKET_FEATURES + [c for c in train_fe.columns \n                                                    if c not in train.columns and c != TARGET]\n\n# IMPORTANT : split temporel avant sélection (éviter data leakage)\nsplit_idx = int(0.8 * len(train_fe))\ntrain_part = train_fe.iloc[:split_idx]\n\nX_all_train = train_part[all_feature_cols].fillna(0)\ny_train_part = train_part[TARGET]\n\nselected_features = select_top_features(X_all_train, y_train_part, top_k=100)\nprint(f'\\n✅ {len(selected_features)} features sélectionnées')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-17T10:16:40.79486Z","iopub.execute_input":"2026-02-17T10:16:40.79511Z","iopub.status.idle":"2026-02-17T10:16:59.466997Z","shell.execute_reply.started":"2026-02-17T10:16:40.795087Z","shell.execute_reply":"2026-02-17T10:16:59.465889Z"}},"outputs":[],"execution_count":null},{"id":"e4f47d57-a988-4c2a-9650-b9e692e33d3a","cell_type":"markdown","source":"## 4. Préparation du dataset final","metadata":{}},{"id":"b4879fe5-be0e-4b54-941c-7aefe1184272","cell_type":"code","source":"# Split temporel (80% train / 20% validation)\n# Pas de mélange aléatoire → respect de l'ordre temporel\nX_train = train_fe.iloc[:split_idx][selected_features].fillna(0)\ny_train = train_fe.iloc[:split_idx][TARGET]\nX_val   = train_fe.iloc[split_idx:][selected_features].fillna(0)\ny_val   = train_fe.iloc[split_idx:][TARGET]\nX_test  = test_fe[selected_features].fillna(0)\n\nprint(f'X_train : {X_train.shape}')\nprint(f'X_val   : {X_val.shape}')\nprint(f'X_test  : {X_test.shape}')\n\n# Normalisation\nscaler = StandardScaler()\nX_train_sc = scaler.fit_transform(X_train)   # fit sur train uniquement\nX_val_sc   = scaler.transform(X_val)          # transform avec le même scaler\nX_test_sc  = scaler.transform(X_test)\n\nprint('\\n✅ Normalisation StandardScaler appliquée')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-17T10:16:59.470209Z","iopub.execute_input":"2026-02-17T10:16:59.470508Z","iopub.status.idle":"2026-02-17T10:17:01.387941Z","shell.execute_reply.started":"2026-02-17T10:16:59.470483Z","shell.execute_reply":"2026-02-17T10:17:01.387169Z"}},"outputs":[],"execution_count":null},{"id":"27bbf889-b24f-430f-9a89-f9d384718caf","cell_type":"markdown","source":"## 5. Entraînement des modèles\n\nOn compare plusieurs modèles linéaires pour comprendre l'effet de la régularisation :\n- **Ridge** (L2) : réduit les coefficients sans les annuler\n- **Lasso** (L1) : met certains coefficients à zéro (feature selection implicite)\n- **ElasticNet** : combinaison L1+L2","metadata":{}},{"id":"24a38ea6-c91a-4462-93fa-a595f0ff5580","cell_type":"code","source":"def evaluate_model(model, X_tr, y_tr, X_v, y_v, name):\n    \"\"\"Entraîne un modèle et affiche ses métriques de validation.\"\"\"\n    model.fit(X_tr, y_tr)\n    \n    preds_tr = model.predict(X_tr)\n    preds_v  = model.predict(X_v)\n    \n    corr_tr, _ = pearsonr(preds_tr, y_tr)\n    corr_v,  _ = pearsonr(preds_v,  y_v)\n    mse_v = mean_squared_error(y_v, preds_v)\n    \n    print(f'  {name:30s} | Train: {corr_tr:.4f} | Val: {corr_v:.4f} | MSE: {mse_v:.4f}')\n    return corr_v, preds_v\n\n\nprint('=== Comparaison des modèles ===')\nprint(f'  {\"Modèle\":30s} | {\"Train corr\":10} | {\"Val corr\":10} | {\"Val MSE\":10}')\nprint('-' * 70)\n\nresults = {}\n\n# Ridge avec différents alpha\nfor alpha in [0.1, 1.0, 10.0, 100.0]:\n    model = Ridge(alpha=alpha, random_state=SEED)\n    corr, preds = evaluate_model(model, X_train_sc, y_train, X_val_sc, y_val,\n                                  f'Ridge(α={alpha})')\n    results[f'Ridge α={alpha}'] = (corr, preds)\n\n# Lasso\nfor alpha in [0.001, 0.01, 0.1]:\n    model = Lasso(alpha=alpha, random_state=SEED, max_iter=5000)\n    corr, preds = evaluate_model(model, X_train_sc, y_train, X_val_sc, y_val,\n                                  f'Lasso(α={alpha})')\n    results[f'Lasso α={alpha}'] = (corr, preds)\n\n# ElasticNet\nmodel = ElasticNet(alpha=0.01, l1_ratio=0.5, random_state=SEED, max_iter=5000)\ncorr, preds = evaluate_model(model, X_train_sc, y_train, X_val_sc, y_val, 'ElasticNet')\nresults['ElasticNet'] = (corr, preds)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-17T10:17:01.389022Z","iopub.execute_input":"2026-02-17T10:17:01.389266Z","iopub.status.idle":"2026-02-17T10:17:55.575364Z","shell.execute_reply.started":"2026-02-17T10:17:01.389245Z","shell.execute_reply":"2026-02-17T10:17:55.57463Z"}},"outputs":[],"execution_count":null},{"id":"989695cd-fc1b-4d77-820b-15837c6c015a","cell_type":"code","source":"# Visualisation des résultats\nmodel_names = list(results.keys())\ncorr_vals   = [results[m][0] for m in model_names]\n\ncolors = ['steelblue' if 'Ridge' in m else 'orange' if 'Lasso' in m else 'green'\n          for m in model_names]\n\nplt.figure(figsize=(12, 5))\nbars = plt.bar(range(len(model_names)), corr_vals, color=colors, alpha=0.8, edgecolor='white')\nplt.xticks(range(len(model_names)), model_names, rotation=30, ha='right')\nplt.ylabel('Corrélation de Pearson (validation)')\nplt.title('Comparaison des modèles — Corrélation sur validation')\nplt.axhline(0.131, color='red', linestyle='--', label='Score gagnant (0.131)', linewidth=1.5)\n\nfor bar, val in zip(bars, corr_vals):\n    plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.001,\n             f'{val:.4f}', ha='center', va='bottom', fontsize=8)\n\nplt.legend()\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-17T10:17:55.576289Z","iopub.execute_input":"2026-02-17T10:17:55.576581Z","iopub.status.idle":"2026-02-17T10:17:55.933701Z","shell.execute_reply.started":"2026-02-17T10:17:55.576555Z","shell.execute_reply":"2026-02-17T10:17:55.932877Z"}},"outputs":[],"execution_count":null},{"id":"4875a1a6-f07a-4035-8969-3300632a4620","cell_type":"markdown","source":"## 6. Modèle final : Ridge (α=1.0)","metadata":{}},{"id":"59c7057e-84e6-4a10-83a4-4113da298f3e","cell_type":"code","source":"# Entraînement du modèle final sur toutes les données train\n# (train + val) pour maximiser les données d'entraînement avant prediction test\n\nbest_alpha = 1.0  # alpha optimal identifié ci-dessus\n\nX_full = train_fe[selected_features].fillna(0)\ny_full = train_fe[TARGET]\n\nscaler_final = StandardScaler()\nX_full_sc  = scaler_final.fit_transform(X_full)\nX_test_sc2 = scaler_final.transform(X_test)\n\nfinal_model = Ridge(alpha=best_alpha, random_state=SEED)\nfinal_model.fit(X_full_sc, y_full)\n\n# Évaluation finale sur la validation temporelle\nX_val_final = scaler_final.transform(X_val)\npreds_val_final = final_model.predict(X_val_final)\ncorr_final, _ = pearsonr(preds_val_final, y_val)\nmse_final = mean_squared_error(y_val, preds_val_final)\n\nprint(f'=== Métriques du modèle final Ridge(α={best_alpha}) ===')\nprint(f'Corrélation validation : {corr_final:.4f}')\nprint(f'MSE validation         : {mse_final:.4f}')\nprint(f'\\nComparaison avec score gagnant : {corr_final/0.131*100:.1f}% de la performance gagnante')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-17T10:17:55.934887Z","iopub.execute_input":"2026-02-17T10:17:55.935316Z","iopub.status.idle":"2026-02-17T10:18:00.600212Z","shell.execute_reply.started":"2026-02-17T10:17:55.935281Z","shell.execute_reply":"2026-02-17T10:18:00.598843Z"}},"outputs":[],"execution_count":null},{"id":"cf288918-0ced-4b50-94b1-6b286d062261","cell_type":"code","source":"# Analyse des coefficients du modèle\ncoef_df = pd.DataFrame({\n    'feature': selected_features,\n    'coefficient': final_model.coef_\n}).sort_values('coefficient', key=abs, ascending=False)\n\nprint('=== Top 20 features par coefficient Ridge ===')\nprint(coef_df.head(20).to_string(index=False))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-17T10:18:00.601011Z","iopub.execute_input":"2026-02-17T10:18:00.601283Z","iopub.status.idle":"2026-02-17T10:18:00.625668Z","shell.execute_reply.started":"2026-02-17T10:18:00.601258Z","shell.execute_reply":"2026-02-17T10:18:00.623249Z"}},"outputs":[],"execution_count":null},{"id":"1e01db16-a0cd-4771-ab9a-99315c0f0f96","cell_type":"code","source":"# Visualisation : prédictions vs réalité\nfig, axes = plt.subplots(1, 2, figsize=(14, 5))\nfig.suptitle('Analyse des prédictions (Ridge, validation)', fontsize=14, fontweight='bold')\n\n# Scatter prédictions vs target\nsample_idx = np.random.choice(len(y_val), size=min(5000, len(y_val)), replace=False)\naxes[0].scatter(y_val.values[sample_idx], preds_val_final[sample_idx],\n                alpha=0.3, s=5, color='steelblue')\naxes[0].axline((0, 0), slope=1, color='red', linestyle='--', linewidth=1)\naxes[0].set_xlabel('Target réelle')\naxes[0].set_ylabel('Prédiction')\naxes[0].set_title(f'Prédictions vs Réalité (r={corr_final:.4f})')\n\n# Distribution des résidus\nresiduals = y_val.values - preds_val_final\naxes[1].hist(residuals, bins=100, color='orange', edgecolor='white', alpha=0.8)\naxes[1].axvline(0, color='black', linestyle='--')\naxes[1].set_title(f'Distribution des résidus (std={residuals.std():.3f})')\naxes[1].set_xlabel('Résidu')\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-17T10:18:00.627318Z","iopub.execute_input":"2026-02-17T10:18:00.629509Z","iopub.status.idle":"2026-02-17T10:18:01.134881Z","shell.execute_reply.started":"2026-02-17T10:18:00.629469Z","shell.execute_reply":"2026-02-17T10:18:01.134012Z"}},"outputs":[],"execution_count":null},{"id":"e13775e6-b7fb-4e37-b67f-acc3742b9579","cell_type":"markdown","source":"## 7. Génération des prédictions test","metadata":{}},{"id":"2f60037e-afb9-4d87-ab1b-8f796b729a4c","cell_type":"code","source":"# Prédictions sur le jeu de test\ntest_predictions = final_model.predict(X_test_sc2)\n\nprint('=== Statistiques des prédictions test ===')\nprint(f'Taille    : {len(test_predictions):,}')\nprint(f'Moyenne   : {test_predictions.mean():.4f}')\nprint(f'Std       : {test_predictions.std():.4f}')\nprint(f'Min       : {test_predictions.min():.4f}')\nprint(f'Max       : {test_predictions.max():.4f}')\n\n# Distribution\nplt.figure(figsize=(10, 4))\nplt.hist(test_predictions, bins=100, color='steelblue', edgecolor='white', alpha=0.8)\nplt.axvline(0, color='black', linestyle='--')\nplt.title('Distribution des prédictions test')\nplt.xlabel('Prédiction')\nplt.ylabel('Fréquence')\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-17T10:18:01.136157Z","iopub.execute_input":"2026-02-17T10:18:01.136763Z","iopub.status.idle":"2026-02-17T10:18:01.564097Z","shell.execute_reply.started":"2026-02-17T10:18:01.136735Z","shell.execute_reply":"2026-02-17T10:18:01.563278Z"}},"outputs":[],"execution_count":null},{"id":"96aaaf34-8721-4f3a-a21e-3c8a98caadde","cell_type":"code","source":"# Création du fichier de soumission\nsubmission = pd.DataFrame({\n    'ID': np.arange(len(test_predictions)),\n    'prediction': test_predictions\n})\n\n# Si le test a un champ 'ID', l'utiliser à la place\nif 'ID' in test.columns:\n    submission['ID'] = test['ID'].values\n\nsubmission.to_csv('submission.csv', index=False)\n\nprint(f'✅ Fichier de soumission créé : submission.csv')\nprint(f'\\nAperçu :')\nprint(submission.head(10).to_string(index=False))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-17T10:18:01.565315Z","iopub.execute_input":"2026-02-17T10:18:01.565665Z","iopub.status.idle":"2026-02-17T10:18:02.918675Z","shell.execute_reply.started":"2026-02-17T10:18:01.565632Z","shell.execute_reply":"2026-02-17T10:18:02.917909Z"}},"outputs":[],"execution_count":null},{"id":"cce1d048-aafb-49f5-b286-b564948df73b","cell_type":"markdown","source":"## 8. Expérimentations & Pistes d'amélioration\n\nUne fois que tu maîtrises le pipeline de base, voici les expérimentations à essayer pour comprendre et améliorer le modèle.","metadata":{}},{"id":"a239f9a6-8007-446c-8c27-b8ef900a3dd6","cell_type":"code","source":"# --- Expérimentation 1 : Impact du nombre de features ---\n\nprint('=== Impact du nombre de features sélectionnées ===')\nprint(f'  {\"top_k\":8} | {\"Val Corr\":12}')\nprint('-' * 25)\n\ncorr_by_k = {}\nfor top_k in [10, 25, 50, 100, 200, 500]:\n    feats_k = select_top_features(X_all_train, y_train_part, top_k=top_k)\n    \n    Xtr_k = train_fe.iloc[:split_idx][feats_k].fillna(0)\n    Xv_k  = train_fe.iloc[split_idx:][feats_k].fillna(0)\n    \n    sc_k = StandardScaler()\n    Xtr_k_sc = sc_k.fit_transform(Xtr_k)\n    Xv_k_sc  = sc_k.transform(Xv_k)\n    \n    m = Ridge(alpha=1.0, random_state=SEED)\n    m.fit(Xtr_k_sc, y_train)\n    p = m.predict(Xv_k_sc)\n    corr_k, _ = pearsonr(p, y_val)\n    corr_by_k[top_k] = corr_k\n    print(f'  {top_k:8d} | {corr_k:.6f}')\n\nbest_k = max(corr_by_k, key=corr_by_k.get)\nprint(f'\\n→ Meilleur top_k : {best_k} (corr={corr_by_k[best_k]:.4f})')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-17T10:18:02.919753Z","iopub.execute_input":"2026-02-17T10:18:02.920055Z","iopub.status.idle":"2026-02-17T10:19:10.62803Z","shell.execute_reply.started":"2026-02-17T10:18:02.920031Z","shell.execute_reply":"2026-02-17T10:19:10.627063Z"}},"outputs":[],"execution_count":null},{"id":"d5284896-608e-4cfd-8f11-af2642f22ecc","cell_type":"code","source":"# Visualisation de l'impact de top_k\nplt.figure(figsize=(10, 4))\nplt.plot(list(corr_by_k.keys()), list(corr_by_k.values()), 'o-', color='steelblue', linewidth=2)\nplt.axvline(best_k, color='red', linestyle='--', label=f'Optimal k={best_k}')\nplt.xlabel('Nombre de features (top_k)')\nplt.ylabel('Corrélation validation')\nplt.title('Impact du nombre de features sur la performance')\nplt.legend()\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-17T10:19:10.63346Z","iopub.execute_input":"2026-02-17T10:19:10.633816Z","iopub.status.idle":"2026-02-17T10:19:10.839869Z","shell.execute_reply.started":"2026-02-17T10:19:10.633787Z","shell.execute_reply":"2026-02-17T10:19:10.839134Z"}},"outputs":[],"execution_count":null},{"id":"cab424b3-8674-4cde-93f1-6ba3aafcadbc","cell_type":"code","source":"# --- Expérimentation 2 : Impact de alpha (Ridge) ---\n\nprint('=== Impact de alpha (Ridge) ===')\nprint(f'  {\"alpha\":12} | {\"Val Corr\":12}')\nprint('-' * 30)\n\nalphas = [0.001, 0.01, 0.1, 0.5, 1.0, 5.0, 10.0, 50.0, 100.0, 500.0, 1000.0]\ncorr_by_alpha = {}\n\nfor alpha in alphas:\n    m = Ridge(alpha=alpha, random_state=SEED)\n    m.fit(X_train_sc, y_train)\n    p = m.predict(X_val_sc)\n    corr_a, _ = pearsonr(p, y_val)\n    corr_by_alpha[alpha] = corr_a\n    print(f'  {alpha:12.3f} | {corr_a:.6f}')\n\nbest_alpha_exp = max(corr_by_alpha, key=corr_by_alpha.get)\nprint(f'\\n→ Meilleur alpha : {best_alpha_exp} (corr={corr_by_alpha[best_alpha_exp]:.4f})')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-17T10:19:10.841191Z","iopub.execute_input":"2026-02-17T10:19:10.841494Z","iopub.status.idle":"2026-02-17T10:19:15.916519Z","shell.execute_reply.started":"2026-02-17T10:19:10.841469Z","shell.execute_reply":"2026-02-17T10:19:15.914675Z"}},"outputs":[],"execution_count":null},{"id":"601646d2-bd51-4b0f-a803-f0408a5e02a2","cell_type":"code","source":"# Visualisation impact alpha\nplt.figure(figsize=(10, 4))\nplt.semilogx(list(corr_by_alpha.keys()), list(corr_by_alpha.values()), 'o-',\n             color='orange', linewidth=2)\nplt.axvline(best_alpha_exp, color='red', linestyle='--', label=f'Optimal α={best_alpha_exp}')\nplt.xlabel('Alpha (échelle log)')\nplt.ylabel('Corrélation validation')\nplt.title('Impact de alpha (Ridge) sur la performance')\nplt.legend()\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-17T10:19:15.91739Z","iopub.execute_input":"2026-02-17T10:19:15.917663Z","iopub.status.idle":"2026-02-17T10:19:16.627738Z","shell.execute_reply.started":"2026-02-17T10:19:15.917638Z","shell.execute_reply":"2026-02-17T10:19:16.625945Z"}},"outputs":[],"execution_count":null},{"id":"8d692469-3ec7-4439-9f2e-db687afea449","cell_type":"code","source":"# --- Expérimentation 3 : Rolling features sur les meilleures features X ---\n\n# Idée : ajouter des rolling mean/std sur les top features X\n# pour capturer la dynamique temporelle locale\n\ndef add_rolling_x_features(df, top_x_features, windows=[5, 10, 20]):\n    \"\"\"\n    Ajoute des statistiques glissantes sur les top features X.\n    ATTENTION : utiliser uniquement les données passées (rolling backward)\n    \"\"\"\n    df = df.copy()\n    for feat in top_x_features[:10]:  # limiter à top 10 pour vitesse\n        for w in windows:\n            df[f'{feat}_rmean_{w}'] = df[feat].rolling(w, min_periods=1).mean()\n            df[f'{feat}_rstd_{w}']  = df[feat].rolling(w, min_periods=1).std().fillna(0)\n    return df\n\n\n# Top 10 features X\ntop10_x = [f for f in selected_features if f.startswith('X')][:10]\nprint(f'Ajout de rolling features sur : {top10_x}')\n\ntrain_roll = add_rolling_x_features(train_fe, top10_x)\ntest_roll  = add_rolling_x_features(test_fe, top10_x)\n\nroll_cols = [c for c in train_roll.columns if '_rmean_' in c or '_rstd_' in c]\nextended_feats = selected_features + roll_cols\n\nXtr_roll = train_roll.iloc[:split_idx][extended_feats].fillna(0)\nXv_roll  = train_roll.iloc[split_idx:][extended_feats].fillna(0)\n\nsc_roll = StandardScaler()\nXtr_roll_sc = sc_roll.fit_transform(Xtr_roll)\nXv_roll_sc  = sc_roll.transform(Xv_roll)\n\nm_roll = Ridge(alpha=1.0, random_state=SEED)\nm_roll.fit(Xtr_roll_sc, y_train)\np_roll = m_roll.predict(Xv_roll_sc)\ncorr_roll, _ = pearsonr(p_roll, y_val)\n\nprint(f'\\nCorr sans rolling features : {corr_final:.4f}')\nprint(f'Corr avec rolling features : {corr_roll:.4f}')\ndelta = corr_roll - corr_final\nsign = '+' if delta >= 0 else ''\nprint(f'Delta : {sign}{delta:.4f}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-17T10:19:16.628864Z","iopub.execute_input":"2026-02-17T10:19:16.629239Z","execution_failed":"2026-02-17T10:19:23.897Z"}},"outputs":[],"execution_count":null},{"id":"640532d7-d1ce-41a5-a616-0a1476664bb6","cell_type":"markdown","source":"## 9. Résumé final","metadata":{}},{"id":"d7ddc022-e098-4b0b-89db-1c33991606e1","cell_type":"code","source":"print('=' * 60)\nprint('RÉSUMÉ DU PIPELINE')\nprint('=' * 60)\nprint(f'''\nÉtapes du pipeline :\n  1. Chargement des données      → 525 887 × 896\n  2. Feature engineering marché  → +7 features (ratios, imbalance, rolling)\n  3. Sélection top 100 features  → corrélation Pearson avec target\n  4. Split temporel 80/20        → pas de data leakage\n  5. Normalisation StandardScaler\n  6. Ridge Regression (α=1.0)\n  7. Soumission → submission.csv\n\nPerformance :\n  Corrélation validation : {corr_final:.4f}\n  Score gagnant          : 0.1310\n  % du score gagnant     : {corr_final/0.131*100:.1f}%\n\nLeçons clés :\n  - Les modèles simples avec bonne sélection de features\n    surpassent les modèles complexes dans les données bruitées\n  - La régularisation (Ridge) est cruciale pour éviter l'overfitting\n  - Le split temporel est obligatoire en finance (pas de mélange)\n  - 100 features >> 895 features (bruit vs signal)\n''')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-17T10:19:36.760244Z","iopub.execute_input":"2026-02-17T10:19:36.760951Z","iopub.status.idle":"2026-02-17T10:19:36.789251Z","shell.execute_reply.started":"2026-02-17T10:19:36.760915Z","shell.execute_reply":"2026-02-17T10:19:36.788106Z"}},"outputs":[],"execution_count":null}]}