{"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":31153,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\nfrom sklearn.model_selection import GroupKFold, StratifiedKFold\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.metrics import f1_score, classification_report\nfrom sklearn.utils.class_weight import compute_class_weight\nfrom sklearn.feature_selection import VarianceThreshold, SelectKBest, mutual_info_classif\n\nimport lightgbm as lgb\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-30T14:32:05.696633Z","iopub.execute_input":"2025-12-30T14:32:05.697186Z","iopub.status.idle":"2025-12-30T14:32:05.703745Z","shell.execute_reply.started":"2025-12-30T14:32:05.697147Z","shell.execute_reply":"2025-12-30T14:32:05.702695Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TRAIN_CSV = \"/kaggle/input/detecting-reversal-points-in-us-equities/new_comptetition_data/train.csv\"\nTEST_CSV  = \"/kaggle/input/detecting-reversal-points-in-us-equities/new_comptetition_data/test.csv\"\nSAMPLE_SUB = \"/kaggle/input/detecting-reversal-points-in-us-equities/new_comptetition_data/sample_submission.csv\"\n\ntrain = pd.read_csv(TRAIN_CSV, low_memory=False)\ntest  = pd.read_csv(TEST_CSV,  low_memory=False)\nsub   = pd.read_csv(SAMPLE_SUB, low_memory=False)\n\nprint(train.shape, test.shape)\n\ny_raw = train[\"class_label\"].fillna(\"None\").astype(str)\n\nmapping = {\n    \"HH\": \"H\", \"LH\": \"H\",\n    \"HL\": \"L\", \"LL\": \"L\",\n    \"H\": \"H\", \"L\": \"L\",\n    \"None\": \"None\", \"nan\": \"None\", \"N\": \"None\"\n}\n\ny = y_raw.replace(mapping)\nprint(y.value_counts())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-30T14:32:05.705561Z","iopub.execute_input":"2025-12-30T14:32:05.706018Z","iopub.status.idle":"2025-12-30T14:32:52.600263Z","shell.execute_reply.started":"2025-12-30T14:32:05.705984Z","shell.execute_reply":"2025-12-30T14:32:52.599352Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"meta_cols = [\"id\", \"train_id\", \"ticker_id\", \"t\", \"class_label\", \"Unnamed: 0\"]\nfeatures = [c for c in train.columns if c not in meta_cols]\n\nX_train_raw = train[features].apply(pd.to_numeric, errors=\"coerce\")\nX_test_raw  = test[features].apply(pd.to_numeric, errors=\"coerce\")\n\nmed = X_train_raw.median()\nX_train_raw = X_train_raw.fillna(med)\nX_test_raw  = X_test_raw.fillna(med)\n\n# 1️⃣ 去掉常数特征\nvt = VarianceThreshold(1e-6)\nX_train_vt = vt.fit_transform(X_train_raw)\nX_test_vt  = vt.transform(X_test_raw)\n\n# 2️⃣ 互信息选前 1000 个\nk = min(1000, X_train_vt.shape[1])\nselector = SelectKBest(mutual_info_classif, k=k)\nselector.fit(X_train_vt, LabelEncoder().fit_transform(y))\n\nX_train = selector.transform(X_train_vt)\nX_test  = selector.transform(X_test_vt)\n\nprint(\"Final feature dim:\", X_train.shape)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-30T14:32:52.601354Z","iopub.execute_input":"2025-12-30T14:32:52.602407Z","iopub.status.idle":"2025-12-30T14:44:32.007777Z","shell.execute_reply.started":"2025-12-30T14:32:52.602375Z","shell.execute_reply":"2025-12-30T14:44:32.006693Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_stage1 = np.where(y == \"None\", \"None\", \"Swing\")\n\ngroups = train[\"ticker_id\"].values\ngkf = GroupKFold(n_splits=5)\n\noof_p_none = np.zeros(len(X_train))\ntest_p_none = np.zeros(len(X_test))\n\nfor fold, (tr, va) in enumerate(gkf.split(X_train, y_stage1, groups)):\n    print(f\"\\nStage1 Fold {fold+1}\")\n\n    le = LabelEncoder()\n    y_tr = le.fit_transform(y_stage1[tr])\n    y_va = le.transform(y_stage1[va])\n\n    cw = compute_class_weight(\"balanced\", classes=np.unique(y_tr), y=y_tr)\n    cw = dict(zip(np.unique(y_tr), cw))\n\n    model = lgb.LGBMClassifier(\n        objective=\"binary\",\n        n_estimators=2000,\n        learning_rate=0.05,\n        class_weight=cw,\n        random_state=42+fold\n    )\n\n    model.fit(\n        X_train[tr], y_tr,\n        eval_set=[(X_train[va], y_va)],\n        eval_metric=\"binary_logloss\",\n        callbacks=[lgb.early_stopping(100)]\n    )\n\n    idx_none = list(le.classes_).index(\"None\")\n    oof_p_none[va] = model.predict_proba(X_train[va])[:, idx_none]\n    test_p_none += model.predict_proba(X_test)[:, idx_none] / 5\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-30T14:44:32.008774Z","iopub.execute_input":"2025-12-30T14:44:32.009127Z","iopub.status.idle":"2025-12-30T14:44:34.495196Z","shell.execute_reply.started":"2025-12-30T14:44:32.009106Z","shell.execute_reply":"2025-12-30T14:44:34.494365Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"swing_idx = np.where(y != \"None\")[0]\nX_swing = X_train[swing_idx]\ny_swing = y.iloc[swing_idx].values\n\noof_stage2 = np.zeros((len(swing_idx), 2))\ntest_stage2 = np.zeros((len(X_test), 2))\n\ngroups_swing = train.loc[swing_idx, \"ticker_id\"].values\ngkf2 = GroupKFold(n_splits=5)\n\nfor fold, (tr, va) in enumerate(gkf2.split(X_swing, y_swing, groups_swing)):\n    print(f\"\\nStage2 Fold {fold+1}\")\n\n    le = LabelEncoder()\n    y_tr = le.fit_transform(y_swing[tr])\n    y_va = le.transform(y_swing[va])\n\n    model = lgb.LGBMClassifier(\n        objective=\"binary\",\n        n_estimators=1500,\n        learning_rate=0.05,\n        random_state=100+fold\n    )\n\n    model.fit(\n        X_swing[tr], y_tr,\n        eval_set=[(X_swing[va], y_va)],\n        eval_metric=\"binary_logloss\",\n        callbacks=[lgb.early_stopping(100)]\n    )\n\n    proba_va = model.predict_proba(X_swing[va])\n    if le.classes_[0] == \"H\":\n        oof_stage2[va] = proba_va\n        test_stage2 += model.predict_proba(X_test) / 5\n    else:\n        oof_stage2[va] = proba_va[:, ::-1]\n        test_stage2 += model.predict_proba(X_test)[:, ::-1] / 5\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-30T14:44:34.497572Z","iopub.execute_input":"2025-12-30T14:44:34.497827Z","iopub.status.idle":"2025-12-30T14:44:34.712151Z","shell.execute_reply.started":"2025-12-30T14:44:34.497806Z","shell.execute_reply":"2025-12-30T14:44:34.711232Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"best_f1, best_thr = -1, None\nbest_pred = None\n\nfor thr in np.linspace(0.2, 0.6, 41):\n    pred = np.array([\"None\"] * len(y), dtype=object)\n\n    for i, idx in enumerate(swing_idx):\n        if oof_p_none[idx] < thr:\n            pred[idx] = \"H\" if oof_stage2[i,0] >= oof_stage2[i,1] else \"L\"\n\n    f1 = f1_score(y, pred, average=\"macro\")\n    if f1 > best_f1:\n        best_f1, best_thr, best_pred = f1, thr, pred\n\nprint(\"Best thr:\", best_thr)\nprint(\"OOF Macro F1:\", best_f1)\nprint(pd.Series(best_pred).value_counts())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-30T14:44:34.713113Z","iopub.execute_input":"2025-12-30T14:44:34.713480Z","iopub.status.idle":"2025-12-30T14:44:35.321276Z","shell.execute_reply.started":"2025-12-30T14:44:34.713449Z","shell.execute_reply":"2025-12-30T14:44:35.320348Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"final_pred = np.array([\"None\"] * len(X_test), dtype=object)\n\nfor i in range(len(X_test)):\n    if test_p_none[i] < best_thr:\n        final_pred[i] = \"H\" if test_stage2[i,0] >= test_stage2[i,1] else \"L\"\n\nout = pd.DataFrame({\n    \"id\": test[\"id\"],\n    \"class_label\": final_pred\n})\n\nout.to_csv(\"submission.csv\", index=False)\nprint(out[\"class_label\"].value_counts())\nout.head(10)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-30T14:44:35.322303Z","iopub.execute_input":"2025-12-30T14:44:35.322707Z","iopub.status.idle":"2025-12-30T14:44:35.339796Z","shell.execute_reply.started":"2025-12-30T14:44:35.322685Z","shell.execute_reply":"2025-12-30T14:44:35.338881Z"}},"outputs":[],"execution_count":null}]}