{"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":114135,"databundleVersionId":14604229,"sourceType":"competition"}],"dockerImageVersionId":31234,"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\nimport lightgbm as lgb\nfrom sklearn.model_selection import TimeSeriesSplit\nimport warnings\n\nwarnings.filterwarnings('ignore')\n\nprint(\"🚀 STARTING PHASE 10: DYNAMIC SNIPER (Top 1% Version)\")\nprint(\"=\"*60)\n\n# ============================================================================\n# 1. CONFIGURATION\n# ============================================================================\nTRAIN_PATH = '/kaggle/input/detecting-reversal-points-in-us-equities/new_comptetition_data/train.csv'\nTEST_PATH = '/kaggle/input/detecting-reversal-points-in-us-equities/new_comptetition_data/test.csv'\n\n# ADJUSTMENT: Target Top 1% (approx 10-12 trades) instead of Top 0.3%\n# This ensures we don't miss obvious opportunities while staying safe.\nTARGET_PERCENTILE = 99.0 \n\nGOLDEN_FEATURES = [\n    'ratio', 'sm_momentum', 'sm_ratio', 'momentum', \n    'occurs_within_zone_100.0_zone_98.5_10', \n    'trending_down_and_above_100.0', 'zone_99.0'\n]\n\n# ============================================================================\n# 2. DATA LOADING & ENGINEERING\n# ============================================================================\ndef load_and_engineer(path):\n    print(f\"⚙️ Processing {path.split('/')[-1]}...\")\n    header = pd.read_csv(path, nrows=0)\n    cols = [c for c in GOLDEN_FEATURES if c in header.columns]\n    cols += [c for c in ['id', 'ticker_id', 'class_label'] if c in header.columns]\n    \n    df = pd.read_csv(path, usecols=cols, low_memory=False)\n    \n    # Context Engineering\n    groups = df.groupby('ticker_id')\n    for col in ['momentum', 'ratio', 'sm_momentum']:\n        if col in df.columns:\n            roll_mean = groups[col].transform(lambda x: x.rolling(20, min_periods=1).mean())\n            roll_std  = groups[col].transform(lambda x: x.rolling(20, min_periods=1).std())\n            df[f'{col}_ZSCORE'] = (df[col] - roll_mean) / (roll_std + 1e-6)\n\n    # DIVERGENCE (The \"Search\" Feature)\n    if 'momentum' in df.columns and 'ratio' in df.columns:\n        df['MOM_RATIO_DIV'] = df['momentum'] / (df['ratio'] + 1e-6)\n\n    return df.fillna(0)\n\ntrain = load_and_engineer(TRAIN_PATH)\ntest = load_and_engineer(TEST_PATH)\n\n# ============================================================================\n# 3. TRAINING\n# ============================================================================\nprint(\"\\n🏋️ Training Ensemble...\")\nexclude = ['id', 'ticker_id', 'class_label', 'target']\nfeatures = [c for c in train.columns if c not in exclude and pd.api.types.is_numeric_dtype(train[c])]\n\nX = train[features]\ny = train['class_label'].map({'None': 0, 'H': 1, 'L': 2}).fillna(0).astype(int)\nX_test = test[features]\n\nmodels = []\ntscv = TimeSeriesSplit(n_splits=5)\n\nfor fold, (train_idx, val_idx) in enumerate(tscv.split(X, y)):\n    X_tr, y_tr = X.iloc[train_idx], y.iloc[train_idx]\n    \n    # Increased num_leaves slightly as per search suggestion to capture complexity\n    model = lgb.LGBMClassifier(\n        n_estimators=1000, learning_rate=0.02, num_leaves=31, max_depth=6,\n        class_weight='balanced', reg_alpha=1.0, reg_lambda=1.0,\n        random_state=42 + fold, verbose=-1, n_jobs=-1\n    )\n    model.fit(X_tr, y_tr)\n    models.append(model)\n\n# ============================================================================\n# 4. DYNAMIC SNIPER LOGIC\n# ============================================================================\nprint(\"\\n🔮 Calculating Dynamic Thresholds (Top 1.0%)...\")\n\ntest_probs = np.zeros((len(X_test), 3))\nfor model in models:\n    test_probs += model.predict_proba(X_test)\ntest_probs /= len(models)\n\n# Determine Thresholds based on Test Set Distribution\nthresh_h_dynamic = np.percentile(test_probs[:, 1], TARGET_PERCENTILE)\nthresh_l_dynamic = np.percentile(test_probs[:, 2], TARGET_PERCENTILE)\n\n# Safety Floor (Keep Intelligence High) - Maintained 0.55\nthresh_h = max(thresh_h_dynamic, 0.55)\nthresh_l = max(thresh_l_dynamic, 0.55)\n\nprint(f\"   H Threshold: {thresh_h:.4f}\")\nprint(f\"   L Threshold: {thresh_l:.4f}\")\n\nfinal_preds = []\nstats = {'None': 0, 'H': 0, 'L': 0}\n\nfor probs in test_probs:\n    p_none, p_h, p_l = probs\n    \n    if p_h > thresh_h and p_h > p_l:\n        final_preds.append('H')\n        stats['H'] += 1\n    elif p_l > thresh_l and p_l > p_h:\n        final_preds.append('L')\n        stats['L'] += 1\n    else:\n        final_preds.append('None')\n        stats['None'] += 1\n\n# ============================================================================\n# 5. SAVE\n# ============================================================================\nsub = pd.DataFrame({'id': test['id'], 'class_label': final_preds})\nsub.to_csv('submission.csv', index=False)\n\nprint(\"\\n\" + \"=\"*60)\nprint(f\"✅ SUBMISSION READY\")\nprint(f\"📊 Signal Distribution: {stats}\")\nprint(\"=\"*60)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-12-30T14:20:03.341565Z","iopub.execute_input":"2025-12-30T14:20:03.341887Z","iopub.status.idle":"2025-12-30T14:21:35.282862Z","shell.execute_reply.started":"2025-12-30T14:20:03.341867Z","shell.execute_reply":"2025-12-30T14:21:35.282151Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}