{"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,"isSourceIdPinned":false,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# **Comprehensive Crypto Market Prediction Solution**","metadata":{}},{"cell_type":"markdown","source":"**I'll implement a comprehensive solution incorporating crypto-specific features, advanced architectures, and robust validation strategies. Here's the complete implementation:**","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\ndef reduce_mem_usage(dataframe, dataset):    \n    print('Reducing memory usage for:', dataset)\n    initial_mem_usage = dataframe.memory_usage().sum() / 1024**2\n    \n    for col in dataframe.columns:\n        col_type = dataframe[col].dtype\n\n        c_min = dataframe[col].min()\n        c_max = dataframe[col].max()\n        if str(col_type)[:3] == 'int':\n            if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:\n                dataframe[col] = dataframe[col].astype(np.int8)\n            elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:\n                dataframe[col] = dataframe[col].astype(np.int16)\n            elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:\n                dataframe[col] = dataframe[col].astype(np.int32)\n            elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:\n                dataframe[col] = dataframe[col].astype(np.int64)\n        else:\n            if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:\n                dataframe[col] = dataframe[col].astype(np.float16)\n            elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:\n                dataframe[col] = dataframe[col].astype(np.float32)\n            else:\n                dataframe[col] = dataframe[col].astype(np.float64)\n\n    final_mem_usage = dataframe.memory_usage().sum() / 1024**2\n    print('--- Memory usage before: {:.2f} MB'.format(initial_mem_usage))\n    print('--- Memory usage after: {:.2f} MB'.format(final_mem_usage))\n    print('--- Decreased memory usage by {:.1f}%\\n'.format(100 * (initial_mem_usage - final_mem_usage) / initial_mem_usage))\n\n    return dataframe\n\nimport sys\nimport pandas as pd\nimport numpy as np\nfrom sklearn.model_selection import KFold\nfrom xgboost import XGBRegressor\nfrom sklearn.feature_selection import SelectKBest, f_regression, mutual_info_regression\nfrom sklearn.linear_model import LassoCV\nfrom sklearn.preprocessing import StandardScaler, RobustScaler\nfrom scipy.stats import pearsonr, spearmanr\nfrom sklearn.ensemble import RandomForestRegressor\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# ===== Feature Engineering =====\ndef feature_engineering(df):\n    \"\"\"Enhanced feature engineering with all the proven features\"\"\"\n    # Original interaction features (proven to work)\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    df['volume_weighted_sell'] = df['sell_qty'] * df['volume']\n    df['buy_sell_ratio'] = df['buy_qty'] / (df['sell_qty'] + 1e-10)\n    df['selling_pressure'] = df['sell_qty'] / (df['volume'] + 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'] + 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'] + 1e-10)\n    \n    # === MICROSTRUCTURE FEATURES (Proven effective) ===\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'] + 1e-10)\n    df['buying_pressure'] = df['buy_qty'] / (df['volume'] + 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'] + 1e-10)\n    df['relative_spread'] = np.abs(df['bid_qty'] - df['ask_qty']) / (df['total_depth'] + 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'] + 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'] + 1e-10)\n    \n    # Market Activity Indicators\n    df['volume_depth_ratio'] = df['volume'] / (df['total_depth'] + 1e-10)\n    df['activity_intensity'] = (df['buy_qty'] + df['sell_qty']) / (df['volume'] + 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    # 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'] + 1e-10)\n    df['adverse_selection_proxy'] = df['net_order_flow'] / (df['total_depth'] + 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'] + 1e-10)\n    df['market_efficiency'] = df['volume'] / (df['bid_ask_spread'] + 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'] + 1e-10)\n    df['ask_ratio'] = df['ask_qty'] / (df['total_depth'] + 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'] + 1e-10)\n    df['market_stress'] = df['volume'] / (df['total_depth'] + 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'] + 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    # Handle infinities and NaN\n    df = df.replace([np.inf, -np.inf], np.nan)\n    \n    # For each column, replace NaN with median for robustness\n    for col in df.columns:\n        if df[col].isna().any():\n            median_val = df[col].median()\n            df[col] = df[col].fillna(median_val if not pd.isna(median_val) else 0)\n    \n    return df\n\n# ===== Smart Feature Selection =====\ndef smart_feature_selection(df, label_col, sample_size=500000, top_k=150):\n    \"\"\"\n    Efficient feature selection using recent data samples\n    Uses multiple methods and focuses on recent crypto patterns\n    \"\"\"\n    print(f\"Starting smart feature selection with {len(df)} samples...\")\n    \n    # Use the most recent data for feature selection (crypto patterns change)\n    recent_sample_size = min(sample_size, len(df))\n    recent_df = df.tail(recent_sample_size).copy()\n    print(f\"Using {len(recent_df)} recent samples for feature selection\")\n    \n    # Get all feature columns (excluding label)\n    feature_cols = [col for col in recent_df.columns if col != label_col]\n    print(f\"Total features before selection: {len(feature_cols)}\")\n    \n    X_sample = recent_df[feature_cols]\n    y_sample = recent_df[label_col]\n    \n    # Remove features with zero variance or too many missing values\n    print(\"Removing low-variance and high-missing features...\")\n    valid_features = []\n    for col in feature_cols:\n        if X_sample[col].var() > 1e-8 and X_sample[col].isna().sum() / len(X_sample) < 0.95:\n            valid_features.append(col)\n    \n    X_sample = X_sample[valid_features]\n    print(f\"Features after variance/missing filter: {len(valid_features)}\")\n    \n    # Method 1: Correlation with target (fast)\n    print(\"Computing correlations...\")\n    correlations = {}\n    for col in valid_features:\n        try:\n            corr = abs(pearsonr(X_sample[col], y_sample)[0])\n            if not np.isnan(corr):\n                correlations[col] = corr\n        except:\n            continue\n    \n    # Method 2: Mutual Information (sample for speed)\n    print(\"Computing mutual information...\")\n    mi_sample_size = min(100000, len(X_sample))\n    sample_idx = np.random.choice(len(X_sample), mi_sample_size, replace=False)\n    X_mi = X_sample.iloc[sample_idx]\n    y_mi = y_sample.iloc[sample_idx]\n    \n    try:\n        mi_scores = mutual_info_regression(X_mi, y_mi, random_state=42)\n        mi_dict = dict(zip(X_mi.columns, mi_scores))\n    except:\n        mi_dict = {}\n    \n    # Method 3: L1 regularization feature importance (fast)\n    print(\"Computing L1 regularization scores...\")\n    try:\n        scaler = StandardScaler()\n        X_scaled = scaler.fit_transform(X_mi)\n        lasso = LassoCV(cv=3, random_state=42, max_iter=1000)\n        lasso.fit(X_scaled, y_mi)\n        l1_scores = abs(lasso.coef_)\n        l1_dict = dict(zip(X_mi.columns, l1_scores))\n    except:\n        l1_dict = {}\n    \n    # Method 4: Tree-based importance (sample for speed)\n    print(\"Computing tree-based importance...\")\n    try:\n        rf_sample_size = min(50000, len(X_sample))\n        rf_idx = np.random.choice(len(X_sample), rf_sample_size, replace=False)\n        rf = RandomForestRegressor(n_estimators=50, random_state=42, n_jobs=-1)\n        rf.fit(X_sample.iloc[rf_idx], y_sample.iloc[rf_idx])\n        tree_scores = rf.feature_importances_\n        tree_dict = dict(zip(X_sample.columns, tree_scores))\n    except:\n        tree_dict = {}\n    \n    # Combine scores with weights\n    print(\"Combining feature scores...\")\n    combined_scores = {}\n    for col in valid_features:\n        score = 0\n        score += correlations.get(col, 0) * 0.3  # Correlation weight\n        score += mi_dict.get(col, 0) * 0.25      # MI weight\n        score += l1_dict.get(col, 0) * 0.25      # L1 weight  \n        score += tree_dict.get(col, 0) * 0.2     # Tree weight\n        combined_scores[col] = score\n    \n    # Select top features\n    selected_features = sorted(combined_scores.items(), key=lambda x: x[1], reverse=True)\n    final_features = [feat[0] for feat in selected_features[:top_k]]\n    \n    print(f\"Selected {len(final_features)} features\")\n    print(\"Top 10 selected features:\")\n    for i, (feat, score) in enumerate(selected_features[:10]):\n        print(f\"  {i+1:2d}. {feat:30s} - Score: {score:.4f}\")\n    \n    return final_features\n\n# ===== Configuration =====\nclass Config:\n    TRAIN_PATH = \"/kaggle/input/drw-crypto-market-prediction/train.parquet\"\n    TEST_PATH = \"/kaggle/input/drw-crypto-market-prediction/test.parquet\"\n    SUBMISSION_PATH = \"/kaggle/input/drw-crypto-market-prediction/sample_submission.csv\"\n    \n    # Core original features (keep these as baseline)\n    CORE_FEATURES = ['X363', 'X321', 'X405', 'X730', 'X523', 'X756', 'X589', 'X462', 'X779',\n                'X25', 'X532', 'X520', 'X329', 'X383', 'X752', 'X287', 'X298', 'X759', 'X302',\n                'X55', 'X56', 'X52', 'X303', 'X51', 'X598', 'X385', 'X603', 'X674',\n                'X415', 'X345', 'X174', 'X178', 'X168', 'X612', \n                'bid_qty', 'ask_qty', 'buy_qty', 'sell_qty', 'volume']\n    \n    LABEL_COLUMN = \"label\"\n    N_FOLDS = 3\n    RANDOM_STATE = 42\n    \n    # Feature selection parameters\n    FEATURE_SELECTION_SAMPLE_SIZE = 750000  # Use more recent data\n    TARGET_FEATURES = 120  # Optimal balance between performance and speed\n\n# ===== Model Parameters =====\n# Optimized XGBoost parameters for faster training\nXGB_PARAMS = {\n    \"tree_method\": \"hist\",\n    \"device\": \"cpu\",\n    \"colsample_bylevel\": 0.5,\n    \"colsample_bynode\": 0.4,\n    \"colsample_bytree\": 0.7,\n    \"gamma\": 1.5,\n    \"learning_rate\": 0.025,  # Slightly higher for faster training\n    \"max_depth\": 18,         # Reduced depth\n    \"max_leaves\": 10,        # Fewer leaves\n    \"min_child_weight\": 15,\n    \"n_estimators\": 1200,    # Fewer estimators but higher LR\n    \"subsample\": 0.08,\n    \"reg_alpha\": 35.0,\n    \"reg_lambda\": 65.0,\n    \"verbosity\": 0,\n    \"random_state\": Config.RANDOM_STATE,\n    \"n_jobs\": -1\n}\n\n# ===== Data Loading and Processing =====\ndef create_time_decay_weights(n: int, decay: float = 0.92) -> np.ndarray:\n    \"\"\"Create time decay weights emphasizing recent data\"\"\"\n    positions = np.arange(n)\n    normalized = positions / (n - 1)\n    weights = decay ** (1.0 - normalized)\n    return weights * n / weights.sum()\n\ndef load_and_process_data():\n    \"\"\"Load, engineer features, and select best features efficiently\"\"\"\n    print(\"Loading data...\")\n    \n    # Load only necessary columns initially for memory efficiency\n    initial_cols = Config.CORE_FEATURES + [Config.LABEL_COLUMN]\n    train_df = pd.read_parquet(Config.TRAIN_PATH, columns=initial_cols)\n    test_df = pd.read_parquet(Config.TEST_PATH, columns=Config.CORE_FEATURES)\n    submission_df = pd.read_csv(Config.SUBMISSION_PATH)\n    \n    print(f\"Initial data loaded - Train: {train_df.shape}, Test: {test_df.shape}\")\n    \n    # Apply feature engineering\n    print(\"Engineering features...\")\n    train_df = feature_engineering(train_df)\n    test_df = feature_engineering(test_df)\n    \n    # Remove original base features (keep engineered ones)\n    to_remove = [\"bid_qty\", \"ask_qty\", \"buy_qty\", \"sell_qty\", \"volume\"]\n    train_df = train_df.drop(columns=to_remove)\n    test_df = test_df.drop(columns=to_remove)\n    \n    print(f\"After feature engineering - Train: {train_df.shape}, Test: {test_df.shape}\")\n    \n    # Smart feature selection on recent data\n    print(\"Performing smart feature selection...\")\n    selected_features = smart_feature_selection(\n        train_df, \n        Config.LABEL_COLUMN, \n        sample_size=Config.FEATURE_SELECTION_SAMPLE_SIZE,\n        top_k=Config.TARGET_FEATURES\n    )\n    \n    # Keep only selected features\n    train_df = train_df[selected_features + [Config.LABEL_COLUMN]]\n    test_df = test_df[selected_features]\n    \n    # Memory optimization\n    print(\"Optimizing memory usage...\")\n    train_df = reduce_mem_usage(train_df, \"train\")\n    test_df = reduce_mem_usage(test_df, \"test\")\n    \n    # Update config with selected features\n    Config.FEATURES = selected_features\n    \n    print(f\"Final data - Train: {train_df.shape}, Test: {test_df.shape}\")\n    print(f\"Selected features: {len(Config.FEATURES)}\")\n    \n    return train_df.reset_index(drop=True), test_df.reset_index(drop=True), submission_df\n\n# ===== Model Training with Recent Data Focus =====\ndef get_model_slices(n_samples: int):\n    \"\"\"Define data slices focusing on recent crypto patterns\"\"\"\n    return [\n        {\"name\": \"recent_95pct\", \"cutoff\": int(0.05 * n_samples)},  # Most recent 95%\n        {\"name\": \"recent_90pct\", \"cutoff\": int(0.10 * n_samples)},  # Most recent 90%\n        {\"name\": \"recent_85pct\", \"cutoff\": int(0.15 * n_samples)},  # Most recent 85%\n        {\"name\": \"recent_80pct\", \"cutoff\": int(0.20 * n_samples)},  # Most recent 80%\n    ]\n\ndef train_xgb_model(X_train, y_train, X_valid, y_valid, X_test, sample_weights=None):\n    \"\"\"Train optimized XGBoost model\"\"\"\n    model = XGBRegressor(**XGB_PARAMS)\n    \n    # Fit with early stopping for efficiency\n    model.fit(\n        X_train, y_train, \n        sample_weight=sample_weights,\n        eval_set=[(X_valid, y_valid)],\n        early_stopping_rounds=100,\n        verbose=False\n    )\n    \n    valid_pred = model.predict(X_valid)\n    test_pred = model.predict(X_test)\n    \n    return valid_pred, test_pred, model\n\ndef train_and_evaluate(train_df, test_df):\n    \"\"\"Train models with focus on recent data patterns\"\"\"\n    n_samples = len(train_df)\n    model_slices = get_model_slices(n_samples)\n    \n    # Initialize predictions\n    oof_preds = {s[\"name\"]: np.zeros(n_samples) for s in model_slices}\n    test_preds = {s[\"name\"]: np.zeros(len(test_df)) for s in model_slices}\n    feature_importance = {s[\"name\"]: np.zeros(len(Config.FEATURES)) for s in model_slices}\n    \n    kf = KFold(n_splits=Config.N_FOLDS, shuffle=False)\n    \n    for fold, (train_idx, valid_idx) in enumerate(kf.split(train_df), start=1):\n        print(f\"\\n--- Fold {fold}/{Config.N_FOLDS} ---\")\n        X_valid = train_df.iloc[valid_idx][Config.FEATURES]\n        y_valid = train_df.iloc[valid_idx][Config.LABEL_COLUMN]\n        X_test = test_df[Config.FEATURES]\n        \n        for s in model_slices:\n            cutoff = s[\"cutoff\"]\n            slice_name = s[\"name\"]\n            \n            # Use recent data slice\n            recent_df = train_df.iloc[cutoff:].reset_index(drop=True)\n            rel_idx = train_idx[train_idx >= cutoff] - cutoff\n            \n            if len(rel_idx) == 0:\n                continue\n                \n            X_train = recent_df.iloc[rel_idx][Config.FEATURES]\n            y_train = recent_df.iloc[rel_idx][Config.LABEL_COLUMN]\n            \n            # Create time decay weights for recent emphasis\n            sample_weights = create_time_decay_weights(len(recent_df))[rel_idx]\n            \n            print(f\"  Training {slice_name}: {len(X_train)} samples\")\n            \n            try:\n                valid_pred, test_pred, model = train_xgb_model(\n                    X_train, y_train, X_valid, y_valid, X_test, sample_weights\n                )\n                \n                # Store predictions for validation samples in this slice\n                mask = valid_idx >= cutoff\n                if mask.any():\n                    oof_preds[slice_name][valid_idx[mask]] = valid_pred[mask]\n                \n                # For samples outside the slice, use the most comprehensive slice\n                if cutoff > 0 and (~mask).any():\n                    oof_preds[slice_name][valid_idx[~mask]] = \\\n                        oof_preds[\"recent_95pct\"][valid_idx[~mask]]\n                \n                test_preds[slice_name] += test_pred\n                feature_importance[slice_name] += model.feature_importances_\n                \n                # Compute validation score\n                valid_corr = pearsonr(y_valid, valid_pred)[0]\n                print(f\"    {slice_name} validation correlation: {valid_corr:.4f}\")\n                \n            except Exception as e:\n                print(f\"    Error in {slice_name}: {str(e)}\")\n                continue\n    \n    # Average test predictions across folds\n    for slice_name in test_preds:\n        test_preds[slice_name] /= Config.N_FOLDS\n        feature_importance[slice_name] /= Config.N_FOLDS\n    \n    return oof_preds, test_preds, feature_importance\n\n# ===== Ensemble and Submission =====\ndef create_smart_ensemble(train_df, oof_preds, test_preds):\n    \"\"\"Create weighted ensemble based on recent performance\"\"\"\n    print(\"\\nEvaluating slice performance...\")\n    \n    slice_scores = {}\n    ensemble_weights = {}\n    \n    for slice_name in oof_preds:\n        # Evaluate on recent data (more relevant for crypto)\n        recent_idx = int(0.8 * len(train_df))  # Last 20% for evaluation\n        recent_true = train_df.iloc[recent_idx:][Config.LABEL_COLUMN]\n        recent_pred = oof_preds[slice_name][recent_idx:]\n        \n        # Remove zeros (unvalidated samples)\n        valid_mask = recent_pred != 0\n        if valid_mask.sum() > 0:\n            score = pearsonr(recent_true[valid_mask], recent_pred[valid_mask])[0]\n            slice_scores[slice_name] = score\n            print(f\"  {slice_name}: {score:.4f} (recent data correlation)\")\n        else:\n            slice_scores[slice_name] = 0\n    \n    # Compute ensemble weights (higher weight for better recent performance)\n    total_score = sum(max(0, score) for score in slice_scores.values())\n    if total_score > 0:\n        ensemble_weights = {k: max(0, v) / total_score for k, v in slice_scores.items()}\n    else:\n        # Equal weights if all scores are poor\n        ensemble_weights = {k: 1.0 / len(slice_scores) for k in slice_scores}\n    \n    print(\"\\nEnsemble weights:\")\n    for slice_name, weight in ensemble_weights.items():\n        print(f\"  {slice_name}: {weight:.3f}\")\n    \n    # Create weighted ensemble\n    ensemble_test = np.zeros(len(test_preds[list(test_preds.keys())[0]]))\n    for slice_name, weight in ensemble_weights.items():\n        ensemble_test += weight * test_preds[slice_name]\n    \n    return ensemble_test, slice_scores, ensemble_weights\n\ndef create_submission(train_df, oof_preds, test_preds, submission_df):\n    \"\"\"Create optimized submission\"\"\"\n    \n    # Create smart ensemble\n    ensemble_pred, slice_scores, weights = create_smart_ensemble(train_df, oof_preds, test_preds)\n    \n    # Evaluate ensemble performance\n    best_slice = max(slice_scores.items(), key=lambda x: x[1])\n    print(f\"\\nBest individual slice: {best_slice[0]} ({best_slice[1]:.4f})\")\n    \n    # Create submission\n    submission = submission_df.copy()\n    submission[\"prediction\"] = ensemble_pred\n    submission.to_csv(\"submission.csv\", index=False)\n    \n    print(f\"\\nSubmission created with ensemble prediction\")\n    print(f\"Ensemble uses {len([w for w in weights.values() if w > 0.01])} slices\")\n    \n    return ensemble_pred\n\n# ===== Main Execution =====\nif __name__ == \"__main__\":\n    print(\"=== Enhanced Crypto Prediction with Smart Feature Selection ===\\n\")\n    \n    # Load and process data\n    print(\"Step 1: Loading and processing data...\")\n    train_df, test_df, submission_df = load_and_process_data()\n    \n    # Train models\n    print(\"\\nStep 2: Training models on recent data slices...\")\n    oof_preds, test_preds, feature_importance = train_and_evaluate(train_df, test_df)\n    \n    # Create submission\n    print(\"\\nStep 3: Creating optimized submission...\")\n    final_pred = create_submission(train_df, oof_preds, test_preds, submission_df)\n    \n    # Print feature importance\n    print(\"\\nTop 15 most important features:\")\n    avg_importance = np.mean(list(feature_importance.values()), axis=0)\n    feature_importance_pairs = list(zip(Config.FEATURES, avg_importance))\n    feature_importance_pairs.sort(key=lambda x: x[1], reverse=True)\n    \n    for i, (feat, imp) in enumerate(feature_importance_pairs[:15]):\n        print(f\"  {i+1:2d}. {feat:35s} - Importance: {imp:.4f}\")\n    \n    print(\"\\n=== Processing Complete! ===\")\n    print(\"Files created:\")\n    print(\"- submission.csv (optimized ensemble)\")\n    print(f\"- Used {len(Config.FEATURES)} carefully selected features\")\n    print(f\"- Focused on recent crypto market patterns\")","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}