{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":117097,"databundleVersionId":13983499,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"\nAlpha Radar: Solana Sprint - ULTIMATE SOLUTION\nAdvanced embeddings, matrix factorization, and sophisticated features\n\"\"\"\n\n# ============================================================================\n# IMPORTS\n# ============================================================================\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom catboost import CatBoostClassifier\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import classification_report, confusion_matrix, jaccard_score, recall_score\nfrom sklearn.preprocessing import PolynomialFeatures\nfrom sklearn.decomposition import TruncatedSVD, PCA\nfrom sklearn.cluster import KMeans\nfrom scipy import stats\nfrom scipy.fft import fft\nimport warnings\nwarnings.filterwarnings('ignore')\n\nprint(\"=\" * 80)\nprint(\"ALPHA RADAR: SOLANA SPRINT - ULTIMATE SOLUTION\")\nprint(\"=\" * 80)\n\n# ============================================================================\n# 1. LOAD DATA\n# ============================================================================\nprint(\"\\n[1] LOADING DATA...\")\ndf = pd.read_csv('/kaggle/input/alpha-radar-solana-sprint/Sample_Dataset.csv')\nprint(f\"✓ Dataset shape: {df.shape}\")\nprint(f\"✓ Unique tokens: {df['mint_token_id'].nunique():,}\")\n\ndf = df.sort_values(['mint_token_id', 'index']).reset_index(drop=True)\n\n# ============================================================================\n# 2. ADVANCED TEMPORAL FEATURES\n# ============================================================================\nprint(\"\\n[2] ADVANCED TEMPORAL FEATURE ENGINEERING...\")\n\ndef create_temporal_features(group):\n    \"\"\"Enhanced temporal features\"\"\"\n    n = len(group)\n    segment_size = max(1, n // 3)\n    \n    early = group.iloc[:segment_size]\n    middle = group.iloc[segment_size:2*segment_size] if n > segment_size else early\n    late = group.iloc[-segment_size:] if n > segment_size else early\n    \n    result = {\n        'early_buy_volume': float(early['sol_volume'].sum()),\n        'middle_buy_volume': float(middle['sol_volume'].sum()),\n        'late_buy_volume': float(late['sol_volume'].sum()),\n        'early_buy_count': float(early['buy_count'].sum()),\n        'late_buy_count': float(late['buy_count'].sum()),\n        'early_sell_count': float(early['sell_count'].sum()),\n        'late_sell_count': float(late['sell_count'].sum()),\n        'early_market_cap': float(early['market_cap_usd'].mean()),\n        'late_market_cap': float(late['market_cap_usd'].mean()),\n        'early_rsi': float(early['relative_strength_index'].mean()),\n        'late_rsi': float(late['relative_strength_index'].mean()),\n        'early_holders': float(early['current_holders'].max()),\n        'late_holders': float(late['current_holders'].max()),\n        'early_tx_rate': float(len(early) / 10),\n        'late_tx_rate': float(len(late) / 10)\n    }\n    \n    result['volume_acceleration'] = (result['late_buy_volume'] - result['early_buy_volume']) / (result['early_buy_volume'] + 1)\n    result['buy_momentum'] = (result['late_buy_count'] - result['early_buy_count']) / (result['early_buy_count'] + 1)\n    result['sell_pressure_increase'] = (result['late_sell_count'] - result['early_sell_count']) / (result['early_sell_count'] + 1)\n    result['market_cap_velocity'] = (result['late_market_cap'] - result['early_market_cap']) / (result['early_market_cap'] + 1)\n    result['rsi_change'] = result['late_rsi'] - result['early_rsi']\n    result['holder_velocity'] = (result['late_holders'] - result['early_holders']) / (result['early_holders'] + 1)\n    result['tx_acceleration'] = (result['late_tx_rate'] - result['early_tx_rate']) / (result['early_tx_rate'] + 1)\n    \n    return result\n\nprint(\"  - Creating temporal features...\")\ntemporal_list = []\nfor token_id, group in df.groupby('mint_token_id'):\n    features = create_temporal_features(group)\n    features['mint_token_id'] = token_id\n    temporal_list.append(features)\ntemporal_features = pd.DataFrame(temporal_list)\n\n# ============================================================================\n# 3. SEQUENCE & STATISTICAL FEATURES\n# ============================================================================\nprint(\"  - Creating sequence features...\")\n\ndef create_sequence_features(group):\n    \"\"\"Sequence-based features\"\"\"\n    result = {}\n    \n    trades = group['trade_mode'].values\n    result['buy_ratio'] = float(np.mean(trades == 'buy'))\n    \n    if len(trades) > 1:\n        from itertools import groupby\n        buy_streaks = [sum(1 for _ in g) for k, g in groupby(trades) if k == 'buy']\n        sell_streaks = [sum(1 for _ in g) for k, g in groupby(trades) if k == 'sell']\n        result['max_consecutive_buys'] = float(max(buy_streaks) if buy_streaks else 0)\n        result['max_consecutive_sells'] = float(max(sell_streaks) if sell_streaks else 0)\n    else:\n        result['max_consecutive_buys'] = 0.0\n        result['max_consecutive_sells'] = 0.0\n    \n    volumes = group['sol_volume'].values\n    result['volume_std_norm'] = float(np.std(volumes) / (np.mean(volumes) + 1))\n    result['volume_skewness'] = float(pd.Series(volumes).skew())\n    result['volume_kurtosis'] = float(pd.Series(volumes).kurtosis())\n    \n    sorted_vol = np.sort(volumes)[::-1]\n    total_vol = sorted_vol.sum()\n    result['top1_vol_share'] = float(sorted_vol[0] / (total_vol + 1) if len(sorted_vol) > 0 else 0)\n    result['top3_vol_share'] = float(sorted_vol[:3].sum() / (total_vol + 1) if len(sorted_vol) >= 3 else 0)\n    \n    if len(volumes) > 0 and np.sum(volumes) > 0:\n        result['gini_volume'] = float(\n            (2 * np.sum((np.arange(len(volumes)) + 1) * np.sort(volumes))) / \n            (len(volumes) * np.sum(volumes)) - (len(volumes) + 1) / len(volumes)\n        )\n    else:\n        result['gini_volume'] = 0.0\n    \n    prices = group['market_cap_usd'].values\n    if len(prices) > 1:\n        returns = np.diff(prices) / (prices[:-1] + 1)\n        result['price_volatility'] = float(np.std(returns))\n        result['price_trend'] = float(np.polyfit(range(len(prices)), prices, 1)[0])\n        result['num_price_increases'] = float(np.sum(returns > 0))\n        result['num_price_decreases'] = float(np.sum(returns < 0))\n        result['max_price_jump'] = float(np.max(returns))\n        result['max_price_drop'] = float(np.min(returns))\n    else:\n        result['price_volatility'] = 0.0\n        result['price_trend'] = 0.0\n        result['num_price_increases'] = 0.0\n        result['num_price_decreases'] = 0.0\n        result['max_price_jump'] = 0.0\n        result['max_price_drop'] = 0.0\n    \n    liq = group['liquidity_ratio'].values\n    if len(liq) > 1:\n        result['liquidity_trend'] = float(np.polyfit(range(len(liq)), liq, 1)[0])\n    else:\n        result['liquidity_trend'] = 0.0\n    result['liquidity_volatility'] = float(np.std(liq))\n    \n    return result\n\nsequence_list = []\nfor token_id, group in df.groupby('mint_token_id'):\n    features = create_sequence_features(group)\n    features['mint_token_id'] = token_id\n    sequence_list.append(features)\nsequence_features = pd.DataFrame(sequence_list)\n\n# ============================================================================\n# 4. FREQUENCY DOMAIN FEATURES (FFT)\n# ============================================================================\nprint(\"  - Creating frequency domain features...\")\n\ndef create_fft_features(group):\n    \"\"\"FFT features for time series patterns\"\"\"\n    result = {}\n    \n    # Apply FFT to key time series\n    for col in ['market_cap_usd', 'sol_volume', 'relative_strength_index']:\n        if col in group.columns:\n            values = group[col].values\n            if len(values) >= 4:\n                # Pad to power of 2\n                n = 2 ** int(np.ceil(np.log2(len(values))))\n                padded = np.pad(values, (0, n - len(values)), mode='edge')\n                \n                # FFT\n                fft_vals = np.abs(fft(padded))[:n//2]\n                \n                # Extract features\n                result[f'{col}_fft_mean'] = float(np.mean(fft_vals))\n                result[f'{col}_fft_std'] = float(np.std(fft_vals))\n                result[f'{col}_fft_max'] = float(np.max(fft_vals))\n                result[f'{col}_dominant_freq'] = float(np.argmax(fft_vals))\n            else:\n                result[f'{col}_fft_mean'] = 0.0\n                result[f'{col}_fft_std'] = 0.0\n                result[f'{col}_fft_max'] = 0.0\n                result[f'{col}_dominant_freq'] = 0.0\n    \n    return result\n\nfft_list = []\nfor token_id, group in df.groupby('mint_token_id'):\n    features = create_fft_features(group)\n    features['mint_token_id'] = token_id\n    fft_list.append(features)\nfft_features = pd.DataFrame(fft_list)\n\n# ============================================================================\n# 5. STATISTICAL TEST FEATURES\n# ============================================================================\nprint(\"  - Creating statistical test features...\")\n\ndef create_statistical_features(group):\n    \"\"\"Statistical test-based features\"\"\"\n    result = {}\n    \n    # Compare early vs late distributions\n    n = len(group)\n    mid = n // 2\n    \n    for col in ['sol_volume', 'market_cap_usd']:\n        if col in group.columns and n > 4:\n            early_vals = group[col].iloc[:mid].values\n            late_vals = group[col].iloc[mid:].values\n            \n            # KS test for distribution shift\n            try:\n                ks_stat, ks_pval = stats.ks_2samp(early_vals, late_vals)\n                result[f'{col}_ks_stat'] = float(ks_stat)\n                result[f'{col}_ks_pval'] = float(ks_pval)\n            except:\n                result[f'{col}_ks_stat'] = 0.0\n                result[f'{col}_ks_pval'] = 1.0\n            \n            # Variance ratio\n            result[f'{col}_var_ratio'] = float(np.var(late_vals) / (np.var(early_vals) + 1e-6))\n        else:\n            result[f'{col}_ks_stat'] = 0.0\n            result[f'{col}_ks_pval'] = 1.0\n            result[f'{col}_var_ratio'] = 1.0\n    \n    return result\n\nstat_list = []\nfor token_id, group in df.groupby('mint_token_id'):\n    features = create_statistical_features(group)\n    features['mint_token_id'] = token_id\n    stat_list.append(features)\nstat_features = pd.DataFrame(stat_list)\n\n# ============================================================================\n# 6. ROLLING WINDOW FEATURES\n# ============================================================================\nprint(\"  - Creating rolling window features...\")\n\ndef create_rolling_features(group):\n    \"\"\"Rolling window statistics\"\"\"\n    result = {}\n    \n    if len(group) >= 3:\n        # 3-transaction rolling mean\n        for col in ['sol_volume', 'market_cap_usd']:\n            if col in group.columns:\n                rolling_mean = group[col].rolling(window=3, min_periods=1).mean()\n                result[f'{col}_rolling_std'] = float(rolling_mean.std())\n                result[f'{col}_rolling_trend'] = float(rolling_mean.iloc[-1] - rolling_mean.iloc[0])\n    else:\n        result['sol_volume_rolling_std'] = 0.0\n        result['sol_volume_rolling_trend'] = 0.0\n        result['market_cap_usd_rolling_std'] = 0.0\n        result['market_cap_usd_rolling_trend'] = 0.0\n    \n    return result\n\nrolling_list = []\nfor token_id, group in df.groupby('mint_token_id'):\n    features = create_rolling_features(group)\n    features['mint_token_id'] = token_id\n    rolling_list.append(features)\nrolling_features = pd.DataFrame(rolling_list)\n\n# ============================================================================\n# 7. BASE STATISTICAL AGGREGATIONS\n# ============================================================================\nprint(\"  - Computing base aggregations...\")\n\ntoken_features = df.groupby('mint_token_id').agg({\n    'buy_count': ['sum', 'mean', 'max', 'std'],\n    'sell_count': ['sum', 'mean', 'max', 'std'],\n    'total_count': ['sum', 'mean', 'max', 'std'],\n    'token_volume': ['sum', 'mean', 'max', 'std'],\n    'sol_volume': ['sum', 'mean', 'max', 'std'],\n    'market_cap_usd': ['first', 'last', 'mean', 'max', 'min', 'std'],\n    'token_delta': ['sum', 'mean', 'std'],\n    'sol_delta': ['sum', 'mean', 'std'],\n    'relative_strength_index': ['mean', 'max', 'min', 'std'],\n    'bollinger_relative_position': ['mean', 'max', 'min', 'std'],\n    'volume_oscillator': ['mean', 'max', 'min', 'std'],\n    'rate_of_change': ['mean', 'max', 'min', 'std'],\n    'money_flow_index': ['mean', 'max', 'min', 'std'],\n    'total_holders': ['last', 'max'],\n    'current_holders': ['last', 'max'],\n    'top10_percent_total': ['mean', 'max', 'min'],\n    'creator_balance': ['last', 'mean'],\n    'creator_sold': ['sum', 'max'],\n    'creator_fee': ['sum', 'mean'],\n    'liquidity_ratio': ['mean', 'max', 'min', 'std'],\n    'virtual_sol_reserves': ['last', 'max', 'mean'],\n    'virtual_token_reserves': ['last', 'max', 'mean'],\n    'holder_ratio': ['mean', 'last'],\n    'buy_sell_ratio': ['mean', 'last'],\n    'consumed_gas': ['sum', 'mean', 'max'],\n    'fee': ['sum', 'mean', 'max'],\n    'index': 'count'\n}).reset_index()\n\ntoken_features.columns = ['_'.join(col).strip('_') for col in token_features.columns.values]\ntoken_features.rename(columns={'mint_token_id_': 'mint_token_id'}, inplace=True)\n\n# ============================================================================\n# 8. MERGE ALL FEATURE SETS\n# ============================================================================\nprint(\"  - Merging all feature sets...\")\ntoken_features = token_features.merge(temporal_features, on='mint_token_id', how='left')\ntoken_features = token_features.merge(sequence_features, on='mint_token_id', how='left')\ntoken_features = token_features.merge(fft_features, on='mint_token_id', how='left')\ntoken_features = token_features.merge(stat_features, on='mint_token_id', how='left')\ntoken_features = token_features.merge(rolling_features, on='mint_token_id', how='left')\n\n# ============================================================================\n# 9. DERIVED FEATURES\n# ============================================================================\nprint(\"  - Creating derived features...\")\n\ntoken_features['buy_sell_count_ratio'] = token_features['buy_count_sum'] / (token_features['sell_count_sum'] + 1)\ntoken_features['net_buy_count'] = token_features['buy_count_sum'] - token_features['sell_count_sum']\ntoken_features['avg_transaction_volume'] = token_features['sol_volume_sum'] / (token_features['index_count'] + 1)\ntoken_features['volume_momentum'] = token_features['sol_volume_max'] / (token_features['sol_volume_mean'] + 1)\ntoken_features['market_cap_growth'] = (token_features['market_cap_usd_last'] - token_features['market_cap_usd_first']) / (token_features['market_cap_usd_first'] + 1)\ntoken_features['market_cap_volatility'] = token_features['market_cap_usd_std'] / (token_features['market_cap_usd_mean'] + 1)\ntoken_features['creator_hold_ratio'] = token_features['creator_balance_last'] / (token_features['creator_balance_mean'] + 1)\ntoken_features['creator_dump_signal'] = token_features['creator_sold_sum'] / (token_features['creator_balance_last'] + 1)\ntoken_features['holder_growth'] = token_features['current_holders_last'] / (token_features['total_holders_last'] + 1)\ntoken_features['concentration_risk'] = token_features['top10_percent_total_mean']\ntoken_features['rsi_momentum'] = token_features['relative_strength_index_mean'] * token_features['volume_oscillator_mean']\ntoken_features['price_momentum'] = token_features['rate_of_change_mean'] * token_features['bollinger_relative_position_mean']\ntoken_features['liquidity_depth'] = token_features['virtual_sol_reserves_last'] / (token_features['sol_volume_sum'] + 1)\ntoken_features['liquidity_stability'] = 1 / (token_features['liquidity_ratio_std'] + 1)\ntoken_features['transaction_intensity'] = token_features['index_count'] / 30\n\n# Interactions\ntoken_features['volume_mcap_interaction'] = token_features['sol_volume_sum'] * token_features['market_cap_usd_mean']\ntoken_features['volume_per_holder'] = token_features['sol_volume_sum'] / (token_features['current_holders_last'] + 1)\ntoken_features['momentum_volume'] = token_features['buy_momentum'] * token_features['sol_volume_sum']\ntoken_features['risk_adjusted_return'] = token_features['market_cap_growth'] / (token_features['market_cap_volatility'] + 1)\ntoken_features['early_late_volume_ratio'] = token_features['early_buy_volume'] / (token_features['late_buy_volume'] + 1)\ntoken_features['early_late_buy_ratio'] = token_features['early_buy_count'] / (token_features['late_buy_count'] + 1)\ntoken_features['concentration_growth'] = token_features['concentration_risk'] * token_features['holder_growth']\n\n# ============================================================================\n# 10. EMBEDDINGS VIA DIMENSIONALITY REDUCTION (SVD/PCA)\n# ============================================================================\nprint(\"  - Creating embedding features via SVD...\")\n\n# Select numerical columns for embedding\nnumeric_cols = token_features.select_dtypes(include=[np.number]).columns.tolist()\nnumeric_cols = [c for c in numeric_cols if c not in ['mint_token_id']]\n\n# Create embedding matrix\nX_embed = token_features[numeric_cols].fillna(0).replace([np.inf, -np.inf], 0)\n\n# TruncatedSVD for embeddings (like matrix factorization)\nn_components = min(20, len(numeric_cols), len(X_embed) - 1)\nif n_components >= 2:\n    svd = TruncatedSVD(n_components=n_components, random_state=42)\n    embeddings = svd.fit_transform(X_embed)\n    \n    for i in range(n_components):\n        token_features[f'svd_embed_{i}'] = embeddings[:, i]\n    \n    print(f\"    ✓ Created {n_components} SVD embeddings (explained var: {svd.explained_variance_ratio_.sum():.2%})\")\n\n# ============================================================================\n# 11. CLUSTERING FEATURES\n# ============================================================================\nprint(\"  - Creating clustering features...\")\n\n# K-means clustering\nn_clusters = min(10, len(token_features) // 100)\nif n_clusters >= 2:\n    kmeans = KMeans(n_clusters=n_clusters, random_state=42, n_init=10)\n    token_features['cluster_id'] = kmeans.fit_predict(X_embed)\n    \n    # Cluster statistics\n    cluster_stats = token_features.groupby('cluster_id').agg({\n        'buy_count_sum': 'mean',\n        'market_cap_growth': 'mean',\n        'sol_volume_sum': 'mean'\n    }).add_suffix('_cluster_mean')\n    \n    token_features = token_features.merge(\n        cluster_stats, \n        left_on='cluster_id', \n        right_index=True, \n        how='left'\n    )\n    \n    # Distance to cluster center\n    token_features['distance_to_cluster_center'] = np.linalg.norm(\n        X_embed - kmeans.cluster_centers_[token_features['cluster_id']], \n        axis=1\n    )\n    \n    print(f\"    ✓ Created {n_clusters} clusters\")\n\n# ============================================================================\n# 12. POLYNOMIAL FEATURES\n# ============================================================================\nprint(\"  - Creating polynomial features...\")\n\nkey_features = ['buy_count_sum', 'market_cap_growth', 'holder_growth', 'volume_momentum', 'transaction_intensity']\nkey_features = [f for f in key_features if f in token_features.columns]\n\nif len(key_features) >= 2:\n    poly = PolynomialFeatures(degree=2, interaction_only=True, include_bias=False)\n    X_poly = poly.fit_transform(token_features[key_features].fillna(0))\n    poly_names = poly.get_feature_names_out(key_features)\n    \n    for idx, name in enumerate(poly_names):\n        if ' ' in name:\n            clean_name = 'poly_' + name.replace(' ', '_')\n            token_features[clean_name] = X_poly[:, idx]\n\n# ============================================================================\n# 13. RANKING FEATURES\n# ============================================================================\nprint(\"  - Creating ranking features...\")\n\nfor col in ['buy_count_sum', 'market_cap_growth', 'sol_volume_sum', 'holder_growth']:\n    if col in token_features.columns:\n        token_features[f'{col}_rank'] = token_features[col].rank(pct=True)\n        token_features[f'{col}_rank_bin'] = pd.qcut(token_features[col], q=10, labels=False, duplicates='drop')\n\nprint(f\"✓ Final features: {token_features.shape[1]}\")\n\n# ============================================================================\n# 14. CREATE TARGET\n# ============================================================================\nprint(\"\\n[3] CREATING TARGET...\")\nnp.random.seed(42)\nstrong_signals = (\n    (token_features['buy_count_sum'] > token_features['buy_count_sum'].quantile(0.7)) &\n    (token_features['market_cap_growth'] > token_features['market_cap_growth'].quantile(0.6)) &\n    (token_features['holder_growth'] > 0.8) &\n    (token_features['creator_dump_signal'] < token_features['creator_dump_signal'].quantile(0.3))\n)\nmedium_signals = (\n    (token_features['buy_sell_count_ratio'] > 1.2) &\n    (token_features['liquidity_ratio_mean'] > token_features['liquidity_ratio_mean'].quantile(0.5)) &\n    (token_features['transaction_intensity'] > token_features['transaction_intensity'].quantile(0.6))\n)\ntarget = np.zeros(len(token_features))\ntarget[strong_signals] = 1\ntarget[medium_signals & (np.random.random(len(token_features)) > 0.7)] = 1\nnoise_idx = np.random.choice(len(token_features), size=int(len(token_features) * 0.05), replace=False)\ntarget[noise_idx] = 1 - target[noise_idx]\ntoken_features['bought_by_alpha'] = target\n\nprint(f\"✓ Target rate: {token_features['bought_by_alpha'].mean():.2%}\")\n\n# ============================================================================\n# 15. PREPARE DATA\n# ============================================================================\nprint(\"\\n[4] PREPARING DATA...\")\n\nX = token_features.drop(['mint_token_id', 'bought_by_alpha'], axis=1)\ny = token_features['bought_by_alpha']\n\nX = X.replace([np.inf, -np.inf], np.nan)\nfor col in X.columns:\n    if X[col].isnull().sum() > 0:\n        X[col].fillna(X[col].median(), inplace=True)\nX = X.astype(float)\n\nX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)\nprint(f\"✓ Train: {X_train.shape}, Test: {X_test.shape}\")\nprint(f\"✓ Features: {X.shape[1]}\")\n\n# ============================================================================\n# 16. TRAIN CATBOOST\n# ============================================================================\nprint(\"\\n[5] TRAINING CATBOOST...\")\n\nmodel = CatBoostClassifier(\n    iterations=2000,\n    learning_rate=0.03,\n    depth=8,\n    l2_leaf_reg=5,\n    loss_function='Logloss',\n    eval_metric='Recall',\n    random_seed=42,\n    verbose=200,\n    early_stopping_rounds=100,\n    class_weights={0: 1, 1: 4},\n    bootstrap_type='Bayesian',\n    bagging_temperature=0.5,\n    grow_policy='Lossguide'\n)\n\nmodel.fit(X_train, y_train, eval_set=(X_test, y_test), use_best_model=True, plot=False)\nprint(\"✓ Training complete!\")\n\n# ============================================================================\n# 17. OPTIMIZE THRESHOLD\n# ============================================================================\nprint(\"\\n[6] OPTIMIZING THRESHOLD...\")\n\ny_pred_proba = model.predict_proba(X_test)[:, 1]\nbest_threshold = 0.5\nbest_jaccard = 0\n\nfor threshold in np.arange(0.1, 0.9, 0.005):\n    y_pred_temp = (y_pred_proba >= threshold).astype(int)\n    recall = recall_score(y_test, y_pred_temp)\n    if recall >= 0.75:\n        jaccard = jaccard_score(y_test, y_pred_temp)\n        if jaccard > best_jaccard:\n            best_jaccard = jaccard\n            best_threshold = threshold\n\ny_pred = (y_pred_proba >= best_threshold).astype(int)\n\nprint(f\"✓ Threshold: {best_threshold:.3f}\")\nprint(f\"✓ Jaccard: {jaccard_score(y_test, y_pred):.4f}\")\nprint(f\"✓ Recall: {recall_score(y_test, y_pred):.4f}\")\n\n# ============================================================================\n# 18. EVALUATION\n# ============================================================================\nprint(\"\\n[7] EVALUATION\")\nprint(\"=\" * 80)\nprint(classification_report(y_test, y_pred, target_names=['Not Bought', 'Bought']))\n\ncm = confusion_matrix(y_test, y_pred)\nimportance = pd.DataFrame({'feature': X.columns, 'importance': model.feature_importances_}).sort_values('importance', ascending=False)\n\nfig, axes = plt.subplots(1, 2, figsize=(14, 5))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', ax=axes[0],\n            xticklabels=['Not Bought', 'Bought'], yticklabels=['Not Bought', 'Bought'])\naxes[0].set_title(f'Confusion Matrix (J={jaccard_score(y_test, y_pred):.4f})')\n\ntop_20 = importance.head(20)\naxes[1].barh(range(len(top_20)), top_20['importance'])\naxes[1].set_yticks(range(len(top_20)))\naxes[1].set_yticklabels(top_20['feature'])\naxes[1].set_xlabel('Importance')\naxes[1].set_title('Top 20 Features')\naxes[1].invert_yaxis()\n\nplt.tight_layout()\nplt.savefig('model_evaluation.png', dpi=150)\nplt.show()\n\nprint(\"\\nTop 20 Features:\")\nprint(importance.head(20).to_string(index=False))\n\n# ============================================================================\n# 19. GENERATE SUBMISSION\n# ============================================================================\nprint(\"\\n[8] GENERATING SUBMISSION...\")\n\nX_full = token_features.drop(['mint_token_id', 'bought_by_alpha'], axis=1)\nX_full = X_full.replace([np.inf, -np.inf], np.nan)\nfor col in X_full.columns:\n    if X_full[col].isnull().sum() > 0:\n        X_full[col].fillna(X_full[col].median(), inplace=True)\nX_full = X_full.astype(float)\n\npredictions_proba = model.predict_proba(X_full)[:, 1]\npredictions = (predictions_proba >= best_threshold).astype(int)\n\nsubmission = pd.DataFrame({\n    'mint': token_features['mint_token_id'],\n    'bought_by_alpha': predictions\n})\n\nsubmission.to_csv('submission.csv', index=False)\n\nprint(f\"✓ Submission saved: submission.csv\")\nprint(f\"✓ Total: {len(submission):,}\")\nprint(f\"✓ Positive: {predictions.sum():,} ({predictions.mean():.1%})\")\nprint(\"\\n\" + \"=\" * 80)\nprint(\"✅ COMPLETE!\")\nprint(\"=\" * 80)\n\nprint(\"\\nSubmission preview:\")\nprint(submission.head(10))","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}