{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","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":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd \nimport numpy as np","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-13T06:41:54.975353Z","iopub.execute_input":"2025-07-13T06:41:54.975621Z","iopub.status.idle":"2025-07-13T06:41:55.676991Z","shell.execute_reply.started":"2025-07-13T06:41:54.975591Z","shell.execute_reply":"2025-07-13T06:41:55.676136Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport warnings\nfrom catboost import CatBoostRegressor\nfrom sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error\nfrom scipy.stats import pearsonr\nfrom sklearn.preprocessing import StandardScaler # Changed from MinMaxScaler\nwarnings.filterwarnings('ignore')\n\n# Load the datasets\ntrain = pd.read_parquet(\"/kaggle/input/drw-crypto-market-prediction/train.parquet\")\ntest = pd.read_parquet(\"/kaggle/input/drw-crypto-market-prediction/test.parquet\")\n\n# Define base features - REVERTED TO ORIGINAL SMALLER SET\n# Keeping your original base features as they are likely chosen for good reasons\nbase_features = [\n    \"X752\", \"X287\", \"X298\", \"X759\", \"X302\", \"X55\", \"X56\", \"X52\", \"X303\", \"X51\",\"X21\",\"X20\",\"X28\",\n    \"X598\", \"X385\", \"X603\", \"X674\", \"X415\", \"X345\", \"X174\", \"X178\", \"X168\", \"X612\",\"X29\",\"X19\",\"X27\",\n    \"bid_qty\", \"ask_qty\", \"buy_qty\", \"sell_qty\", \"volume\", \"label\"\n]\n\n# Select base features for train and test sets\ntrain = train[base_features]\ntest = test[base_features]\nselected_features = [\"X752\", \"X21\",\"X20\",\"X28\",\"X759\",\"X29\",\"X19\",\"X27\"]\n\ndef add_features(df):\n    # Original features\n    df['bid_ask_interaction'] = df['bid_qty'] * df['ask_qty']\n    df['bid_buy_interaction'] = df['bid_qty'] * df['buy_qty']\n    df['bid_sell_interaction'] = df['bid_qty'] * df['sell_qty']\n    df['ask_buy_interaction'] = df['ask_qty'] * df['buy_qty']\n    df['ask_sell_interaction'] = df['ask_qty'] * df['sell_qty']\n\n    # Handle division by zero more robustly, e.g., using a small epsilon or fillna(0)\n    df['volume_weighted_sell'] = df['sell_qty'] * df['volume']\n    df['buy_sell_ratio'] = df['buy_qty'] / (df['sell_qty'].replace(0, np.nan) + 1e-10)\n    df['selling_pressure'] = df['sell_qty'] / (df['volume'].replace(0, np.nan) + 1e-10)\n    df['log_volume'] = np.log1p(df['volume'])\n\n    df['effective_spread_proxy'] = np.abs(df['buy_qty'] - df['sell_qty']) / (df['volume'].replace(0, np.nan) + 1e-10)\n    df['bid_ask_imbalance'] = (df['bid_qty'] - df['ask_qty']) / (df['bid_qty'] + df['ask_qty'] + 1e-10)\n    df['order_flow_imbalance'] = (df['buy_qty'] - df['sell_qty']) / (df['buy_qty'] + df['sell_qty'] + 1e-10)\n    df['liquidity_ratio'] = (df['bid_qty'] + df['ask_qty']) / (df['volume'].replace(0, np.nan) + 1e-10)\n    \n    # === NEW MICROSTRUCTURE FEATURES ===\n    \n    # Price Pressure Indicators\n    df['net_order_flow'] = df['buy_qty'] - df['sell_qty']\n    df['normalized_net_flow'] = df['net_order_flow'] / (df['volume'].replace(0, np.nan) + 1e-10)\n    df['buying_pressure'] = df['buy_qty'] / (df['volume'].replace(0, np.nan) + 1e-10)\n    df['volume_weighted_buy'] = df['buy_qty'] * df['volume']\n    \n    # Liquidity Depth Measures\n    df['total_depth'] = df['bid_qty'] + df['ask_qty']\n    df['depth_imbalance'] = (df['bid_qty'] - df['ask_qty']) / (df['total_depth'].replace(0, np.nan) + 1e-10)\n    df['relative_spread'] = np.abs(df['bid_qty'] - df['ask_qty']) / (df['total_depth'].replace(0, np.nan) + 1e-10)\n    df['log_depth'] = np.log1p(df['total_depth'])\n    \n    # Order Flow Toxicity Proxies\n    df['kyle_lambda'] = np.abs(df['net_order_flow']) / (df['volume'].replace(0, np.nan) + 1e-10)\n    df['flow_toxicity'] = np.abs(df['order_flow_imbalance']) * df['volume']\n    df['aggressive_flow_ratio'] = (df['buy_qty'] + df['sell_qty']) / (df['total_depth'].replace(0, np.nan) + 1e-10)\n    \n    # Market Activity Indicators\n    df['volume_depth_ratio'] = df['volume'] / (df['total_depth'].replace(0, np.nan) + 1e-10)\n    df['activity_intensity'] = (df['buy_qty'] + df['sell_qty']) / (df['volume'].replace(0, np.nan) + 1e-10)\n    df['log_buy_qty'] = np.log1p(df['buy_qty'])\n    df['log_sell_qty'] = np.log1p(df['sell_qty'])\n    df['log_bid_qty'] = np.log1p(df['bid_qty'])\n    df['log_ask_qty'] = np.log1p(df['ask_qty'])\n    \n    # Microstructure Volatility Proxies\n    df['realized_spread_proxy'] = 2 * np.abs(df['net_order_flow']) / (df['volume'].replace(0, np.nan) + 1e-10)\n    df['price_impact_proxy'] = df['net_order_flow'] / (df['total_depth'].replace(0, np.nan) + 1e-10)\n    df['quote_volatility_proxy'] = np.abs(df['depth_imbalance'])\n    \n    # Complex Interaction Terms\n    df['flow_depth_interaction'] = df['net_order_flow'] * df['total_depth']\n    df['imbalance_volume_interaction'] = df['order_flow_imbalance'] * df['volume']\n    df['depth_volume_interaction'] = df['total_depth'] * df['volume']\n    df['buy_sell_spread'] = np.abs(df['buy_qty'] - df['sell_qty'])\n    df['bid_ask_spread'] = np.abs(df['bid_qty'] - df['ask_qty'])\n    \n    # Information Asymmetry Measures\n    df['trade_informativeness'] = df['net_order_flow'] / (df['bid_qty'] + df['ask_qty'] + 1e-10)\n    df['execution_shortfall_proxy'] = df['buy_sell_spread'] / (df['volume'].replace(0, np.nan) + 1e-10)\n    df['adverse_selection_proxy'] = df['net_order_flow'] / (df['total_depth'].replace(0, np.nan) + 1e-10) * df['volume']\n    \n    # Market Efficiency Indicators\n    df['fill_probability'] = df['volume'] / (df['buy_qty'] + df['sell_qty'] + 1e-10)\n    df['execution_rate'] = (df['buy_qty'] + df['sell_qty']) / (df['total_depth'].replace(0, np.nan) + 1e-10)\n    df['market_efficiency'] = df['volume'] / (df['bid_ask_spread'].replace(0, np.nan) + 1e-10)\n    \n    # Non-linear Transformations\n    df['sqrt_volume'] = np.sqrt(df['volume'])\n    df['sqrt_depth'] = np.sqrt(df['total_depth'])\n    df['volume_squared'] = df['volume'] ** 2\n    df['imbalance_squared'] = df['order_flow_imbalance'] ** 2\n    \n    # Relative Measures\n    df['bid_ratio'] = df['bid_qty'] / (df['total_depth'].replace(0, np.nan) + 1e-10)\n    df['ask_ratio'] = df['ask_qty'] / (df['total_depth'].replace(0, np.nan) + 1e-10)\n    df['buy_ratio'] = df['buy_qty'] / (df['buy_qty'] + df['sell_qty'] + 1e-10)\n    df['sell_ratio'] = df['sell_qty'] / (df['buy_qty'] + df['sell_qty'] + 1e-10)\n    \n    # Market Stress Indicators\n    df['liquidity_consumption'] = (df['buy_qty'] + df['sell_qty']) / (df['total_depth'].replace(0, np.nan) + 1e-10)\n    df['market_stress'] = df['volume'] / (df['total_depth'].replace(0, np.nan) + 1e-10) * np.abs(df['order_flow_imbalance'])\n    df['depth_depletion'] = df['volume'] / (df['bid_qty'] + df['ask_qty'] + 1e-10)\n    \n    # Directional Indicators\n    df['net_buying_ratio'] = df['net_order_flow'] / (df['volume'].replace(0, np.nan) + 1e-10)\n    df['directional_volume'] = df['net_order_flow'] * np.log1p(df['volume'])\n    df['signed_volume'] = np.sign(df['net_order_flow']) * df['volume']\n    \n    # Replace infinities and NaNs globally after all calculations\n    df = df.replace([np.inf, -np.inf], 0).fillna(0)\n    \n    return df\n\n# Add features to train and test datasets\nx_train = add_features(train)\nx_test = add_features(test)\n\n# Ensure the index is a datetime type (though not strictly necessary for this model, good practice)\nx_train.index = pd.to_datetime(x_train.index)\n# Sort the dataset by timestamp (index)\nx_train = x_train.sort_index()\n\n# Identify numerical columns for scaling\n# Exclude 'label' from these transformations as it's the target variable\nnumerical_cols_to_process = x_train.drop(columns=['label']).select_dtypes(include=np.number).columns.tolist()\n\n## Data Normalization (StandardScaler) 📏\nprint(\"--- Starting Data Normalization ---\")\n# Use StandardScaler instead of MinMaxScaler. It's less sensitive to outliers and preserves their information better.\nscaler = StandardScaler()\n# Fit scaler only on training data features, then transform train and test\nx_train[numerical_cols_to_process] = scaler.fit_transform(x_train[numerical_cols_to_process])\nx_test[numerical_cols_to_process] = scaler.transform(x_test[numerical_cols_to_process]) # Use same scaler fitted on train\nprint(\"--- Data Normalization Complete ---\")\n\n# Define the split point (80% train, 20% validation)\nsplit_index = int(len(x_train) * 0.8)\n\n# Slice by index — NO shuffling to maintain time series integrity\ntrain_split = x_train.iloc[:split_index]\nval_split = x_train.iloc[split_index:]\n\n# Training features and labels\nX_train = train_split.drop(columns=['label'])\ny_train = train_split['label']\n\n# Validation features and labels\nX_val = val_split.drop(columns=['label'])\ny_val = val_split['label']\n\n# CatBoost model parameters\n# Increased iterations, slightly reduced learning rate, and increased depth.\n# Also added a higher `l2_leaf_reg` for regularization.\nCAT_PARAMS = {\n    'iterations': 1000, # Increased iterations\n    'learning_rate': 0.001, # Slightly reduced learning rate\n    'depth': 7, # Increased depth to allow for more complex interactions\n    'l2_leaf_reg': 50, # Increased L2 regularization\n    'loss_function': 'RMSE',\n    'eval_metric': 'RMSE',\n    'random_seed': 42,\n    'verbose': 100, # Set verbose to see training progress\n    'early_stopping_rounds': 75, # Adjusted early stopping rounds\n    'thread_count': -1\n}\n\n# Initialize and train the CatBoost model\nmodel = CatBoostRegressor(**CAT_PARAMS)\nmodel.fit(X_train, y_train,\n          eval_set=(X_val, y_val),\n          early_stopping_rounds=CAT_PARAMS['early_stopping_rounds'])\n\n# Make predictions on training and validation sets\ny_train_pred = model.predict(X_train)\ny_val_pred = model.predict(X_val)\n\n# --- Calculate and Print Metrics for Training Set ---\nprint(\"\\n📊 **Training Set Metrics:**\")\n# Pearson correlation\npearson_corr_train, _ = pearsonr(y_train, y_train_pred)\nprint(f\"✅ Pearson Correlation: {pearson_corr_train:.4f}\")\n# MSE\nmse_train = mean_squared_error(y_train, y_train_pred)\nprint(f\"📉 Mean Squared Error (MSE): {mse_train:.4f}\")\n# RMSE\nrmse_train = np.sqrt(mse_train)\nprint(f\"📏 Root Mean Squared Error (RMSE): {rmse_train:.4f}\")\n# MAE\nmae_train = mean_absolute_error(y_train, y_train_pred)\nprint(f\"🎯 Mean Absolute Error (MAE): {mae_train:.4f}\")\n# R2 Score\nr2_train = r2_score(y_train, y_train_pred)\nprint(f\"📈 R-squared (R2) Score: {r2_train:.4f}\")\n\n# --- Calculate and Print Metrics for Validation Set ---\nprint(\"\\n📊 **Validation Set Metrics:**\")\n# Pearson correlation\npearson_corr_val, _ = pearsonr(y_val, y_val_pred)\nprint(f\"✅ Pearson Correlation: {pearson_corr_val:.4f}\")\n# MSE\nmse_val = mean_squared_error(y_val, y_val_pred)\nprint(f\"📉 Mean Squared Error (MSE): {mse_val:.4f}\")\n# RMSE\nrmse_val = np.sqrt(mse_val)\nprint(f\"📏 Root Mean Squared Error (RMSE): {rmse_val:.4f}\")\n# MAE\nmae_val = mean_absolute_error(y_val, y_val_pred)\nprint(f\"🎯 Mean Absolute Error (MAE): {mae_val:.4f}\")\n# R2 Score\nr2_val = r2_score(y_val, y_val_pred)\nprint(f\"📈 R-squared (R2) Score: {r2_val:.4f}\")\n\n# --- Generate Submission File ---\n# Retrain the model on the full training data (X_train and X_val combined) for final prediction\nprint(\"\\n--- Retraining model on full training data for final submission ---\")\nX_full_train = pd.concat([X_train, X_val])\ny_full_train = pd.concat([y_train, y_val])\n\nfinal_model = CatBoostRegressor(**CAT_PARAMS)\nfinal_model.fit(X_full_train, y_full_train, verbose=0) # No early stopping or validation set for final training\n\nfeature_names = X_full_train.columns.tolist()\nx_test = x_test[feature_names] # Ensure test set features match training set features\n\ny_pred = final_model.predict(x_test)\nsubmission = pd.read_csv(\"/kaggle/input/drw-crypto-market-prediction/sample_submission.csv\")\nsubmission[\"prediction\"] = y_pred\nsubmission.to_csv(\"submission.csv\", index=False)\n\nprint(\"\\n📁 Submission file saved as 'submission.csv'\")\nprint(submission.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-13T06:56:33.316888Z","iopub.execute_input":"2025-07-13T06:56:33.317287Z","iopub.status.idle":"2025-07-13T06:59:38.989996Z","shell.execute_reply.started":"2025-07-13T06:56:33.317256Z","shell.execute_reply":"2025-07-13T06:59:38.989040Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}