{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":114135,"databundleVersionId":14604229,"sourceType":"competition"}],"dockerImageVersionId":31153,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"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\"\nSUBMISSION = \"/kaggle/input/detecting-reversal-points-in-us-equities/new_comptetition_data/sample_submission.csv\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:21:51.627803Z","iopub.execute_input":"2025-12-18T19:21:51.628088Z","iopub.status.idle":"2025-12-18T19:21:51.635412Z","shell.execute_reply.started":"2025-12-18T19:21:51.628064Z","shell.execute_reply":"2025-12-18T19:21:51.634578Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"modified from @Subarna Saikia","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Baseline Starter for Financial Swing Point Classification (Kaggle)\n\nimport pandas as pd\nimport numpy as np\nfrom sklearn.feature_selection import SelectKBest, mutual_info_classif\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.metrics import f1_score, classification_report, accuracy_score\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.utils.class_weight import compute_class_weight\nimport lightgbm as lgb\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:21:53.463259Z","iopub.execute_input":"2025-12-18T19:21:53.463540Z","iopub.status.idle":"2025-12-18T19:21:59.884330Z","shell.execute_reply.started":"2025-12-18T19:21:53.463519Z","shell.execute_reply":"2025-12-18T19:21:59.883649Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================\n# 1. Load and Prepare Data\n# =========================\ntrain = pd.read_csv(TRAIN_CSV)\ntest = pd.read_csv(TEST_CSV)\nsubmission = pd.read_csv(SUBMISSION)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:21:59.885520Z","iopub.execute_input":"2025-12-18T19:21:59.886094Z","iopub.status.idle":"2025-12-18T19:27:30.337964Z","shell.execute_reply.started":"2025-12-18T19:21:59.886061Z","shell.execute_reply":"2025-12-18T19:27:30.337267Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission.class_label.unique()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:27:47.147306Z","iopub.execute_input":"2025-12-18T19:27:47.147982Z","iopub.status.idle":"2025-12-18T19:27:47.162420Z","shell.execute_reply.started":"2025-12-18T19:27:47.147960Z","shell.execute_reply":"2025-12-18T19:27:47.161683Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:27:47.963328Z","iopub.execute_input":"2025-12-18T19:27:47.963609Z","iopub.status.idle":"2025-12-18T19:27:47.989169Z","shell.execute_reply.started":"2025-12-18T19:27:47.963588Z","shell.execute_reply":"2025-12-18T19:27:47.988316Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:27:52.043129Z","iopub.execute_input":"2025-12-18T19:27:52.043684Z","iopub.status.idle":"2025-12-18T19:27:52.079390Z","shell.execute_reply.started":"2025-12-18T19:27:52.043662Z","shell.execute_reply":"2025-12-18T19:27:52.078555Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# train['time'] = pd.to_datetime(train['t'])\n# train.sort_values(by='time')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T10:20:57.416835Z","iopub.execute_input":"2025-12-18T10:20:57.417061Z","iopub.status.idle":"2025-12-18T10:20:57.421009Z","shell.execute_reply.started":"2025-12-18T10:20:57.417045Z","shell.execute_reply":"2025-12-18T10:20:57.420347Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.dropna(inplace=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:29:24.743541Z","iopub.execute_input":"2025-12-18T19:29:24.744084Z","iopub.status.idle":"2025-12-18T19:29:24.851898Z","shell.execute_reply.started":"2025-12-18T19:29:24.744060Z","shell.execute_reply":"2025-12-18T19:29:24.850953Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Label mapping\n# mapping = {\n#     'HH': 'H',\n#     'LH': 'H',\n#     'HL': 'L',\n#     'LL': 'L',\n#     np.nan: 'N'\n# }\n\n# Drop metadata columns\nmeta_cols = ['id', 'train_id', 'Unnamed: 0', 'ticker_id', 't', 'class_label']\nfeatures = [col for col in train.columns if col not in meta_cols]\n\n# Apply mapping to target\ny = train['class_label']\ny = y.dropna() # droping NA\n\nprint(\"Class distribution after mapping:\\n\", y.value_counts())\n\n# Encode target\nle = LabelEncoder()\ny_encoded = le.fit_transform(y)\n\n# Prepare features\nX = train[features].copy()\n\nprint(\"\\nEncoded class labels:\")\nfor orig, enc in zip(le.classes_, range(len(le.classes_))):\n    print(f\"{orig} -> {enc}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:29:27.403346Z","iopub.execute_input":"2025-12-18T19:29:27.403659Z","iopub.status.idle":"2025-12-18T19:29:27.465835Z","shell.execute_reply.started":"2025-12-18T19:29:27.403639Z","shell.execute_reply":"2025-12-18T19:29:27.465033Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y.unique()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:28:59.224050Z","iopub.execute_input":"2025-12-18T19:28:59.224679Z","iopub.status.idle":"2025-12-18T19:28:59.231392Z","shell.execute_reply.started":"2025-12-18T19:28:59.224654Z","shell.execute_reply":"2025-12-18T19:28:59.230446Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_encoded","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:29:02.227227Z","iopub.execute_input":"2025-12-18T19:29:02.227768Z","iopub.status.idle":"2025-12-18T19:29:02.233431Z","shell.execute_reply.started":"2025-12-18T19:29:02.227739Z","shell.execute_reply":"2025-12-18T19:29:02.232696Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:29:32.219180Z","iopub.execute_input":"2025-12-18T19:29:32.219867Z","iopub.status.idle":"2025-12-18T19:29:32.242648Z","shell.execute_reply.started":"2025-12-18T19:29:32.219842Z","shell.execute_reply":"2025-12-18T19:29:32.241875Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# ==============================\n# 2. Feature Selection (Top-K)\n# ==============================\n\n# Split features into boolean and non-boolean\nbool_features = [f for f in X.columns if X[f].dtype == bool]\nnon_bool_features = [f for f in X.columns if X[f].dtype != bool]\n\n# print(f\"Selecting top {len(bool_features)} features...\")\n# # Select top k from boolean features only\n# k = len(bool_features)  # choose based on memory/time\n\n# print(f\"Found {len(bool_features)} boolean features and {len(non_bool_features)} non-boolean features.\")\n\n\n# if len(bool_features) > 0:\n#     selector = SelectKBest(score_func=mutual_info_classif, k=min(k, len(bool_features)))\n#     X_bool_selected = selector.fit_transform(X[bool_features], y_encoded)\n#     selected_bool_features = [bool_features[i] for i in selector.get_support(indices=True)]\n# else:\n#     X_bool_selected = np.empty((len(X), 0))  # no boolean features\n#     selected_bool_features = []\n\n# Combine boolean (selected) + all non-boolean features\nfinal_features = non_bool_features + bool_features\nprint(f\"Total selected features: {len(final_features)}\")\n\n# Final datasets\nX_selected_df = X[final_features].reset_index(drop=True)\nX_test_selected_df = test[final_features].reset_index(drop=True)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:29:36.603483Z","iopub.execute_input":"2025-12-18T19:29:36.604130Z","iopub.status.idle":"2025-12-18T19:29:39.549434Z","shell.execute_reply.started":"2025-12-18T19:29:36.604101Z","shell.execute_reply":"2025-12-18T19:29:39.548490Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X_selected_df[non_bool_features].head(5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:33:05.428042Z","iopub.execute_input":"2025-12-18T19:33:05.429048Z","iopub.status.idle":"2025-12-18T19:33:05.451984Z","shell.execute_reply.started":"2025-12-18T19:33:05.429013Z","shell.execute_reply":"2025-12-18T19:33:05.450940Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# ============================================\n# 3. Class Weights for Imbalance Compensation\n# ============================================\nfrom sklearn.utils.class_weight import compute_class_weight\nclass_weights = compute_class_weight(class_weight='balanced', classes=np.unique(y_encoded), y=y_encoded)\nclass_weight_dict = {i: w for i, w in enumerate(class_weights)}\nprint(\"Class weights:\", class_weight_dict)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:33:06.679658Z","iopub.execute_input":"2025-12-18T19:33:06.680199Z","iopub.status.idle":"2025-12-18T19:33:06.686947Z","shell.execute_reply.started":"2025-12-18T19:33:06.680169Z","shell.execute_reply":"2025-12-18T19:33:06.685964Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"classes = np.unique(y_encoded)\nclasses","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:33:10.420461Z","iopub.execute_input":"2025-12-18T19:33:10.420757Z","iopub.status.idle":"2025-12-18T19:33:10.426114Z","shell.execute_reply.started":"2025-12-18T19:33:10.420733Z","shell.execute_reply":"2025-12-18T19:33:10.425242Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# compute class weights (balanced)\nclasses = np.unique(y_encoded)\ncw = compute_class_weight(class_weight='balanced', classes=classes, y=y_encoded)\nclass_weight_dict = {cls: w for cls, w in zip(classes, cw)}\nprint(\"class_weight_dict:\", class_weight_dict)\nn_classes = len(le.classes_)\nprint(\"Detected classes (len={}): {}\".format(n_classes, le.classes_))\n\n# Use StratifiedKFold\nskf = StratifiedKFold(n_splits=5, shuffle=True, random_state=0)\n\noof_preds = np.zeros((X_selected_df.shape[0], len(classes)))\noof_labels = np.zeros(X_selected_df.shape[0], dtype=int)\n\nfold_accuracies = []\nfold_f1s = []\ntest_oof_preds = np.zeros((X_test_selected_df.shape[0], len(classes)))\n\n\nfor fold, (train_idx, val_idx) in enumerate(skf.split(X_selected_df, y_encoded)):\n    print(f\"\\nTraining fold {fold+1}...\")\n    X_train, X_val = X_selected_df.iloc[train_idx].values, X_selected_df.iloc[val_idx].values\n    y_train, y_val = y_encoded[train_idx], y_encoded[val_idx]\n\n    # sample weights\n    sample_weights_train = np.array([class_weight_dict[y] for y in y_train])\n    sample_weights_val = np.array([class_weight_dict[y] for y in y_val])\n\n    # datasets\n    lgb_train = lgb.Dataset(X_train, label=y_train, weight=sample_weights_train)\n    lgb_val = lgb.Dataset(X_val, label=y_val, weight=sample_weights_val, reference=lgb_train)\n\n    # params = {\n    #     'objective': 'multiclass',\n    #     'num_class': len(classes),\n    #     'learning_rate': 0.05,\n    #     'metric': 'multi_logloss',\n    #     'verbosity': -1,\n    #     'seed': 42,\n    #     'class_weight': class_weight_dict  # ✅ add this\n    # }\n\n    callbacks = [\n        lgb.early_stopping(stopping_rounds=50),\n        lgb.log_evaluation(period=100)\n    ]\n\n    # # Train model\n    # model = lgb.train(\n    #     params,\n    #     lgb_train,\n    #     num_boost_round=5000,\n    #     valid_sets=[lgb_val],\n    #     callbacks=callbacks\n    # )\n    model = lgb.LGBMClassifier(\n        objective='multiclass',\n        num_class=len(classes),\n        learning_rate=0.05,\n        n_estimators=5000,\n        class_weight=class_weight_dict,  # ✅ Works here\n        random_state=0\n    )\n    \n    model.fit(\n        X_train, \n        y_train, \n        eval_set=[(X_val, y_val)],\n        eval_metric='multi_logloss',\n        callbacks=callbacks\n    )\n\n    # Predict on validation\n    val_preds = model.predict_proba(X_val)\n    val_pred_labels = np.argmax(val_preds, axis=1)\n\n    # store predictions\n    oof_preds[val_idx] = val_preds\n    oof_labels[val_idx] = y_val\n\n    # metrics\n    acc = accuracy_score(y_val, val_pred_labels)\n    f1 = f1_score(y_val, val_pred_labels, average='macro')\n    fold_accuracies.append(acc)\n    fold_f1s.append(f1)\n\n    print(f\"Fold {fold+1} Accuracy: {acc:.4f}, Macro F1: {f1:.4f}\")\n\n    # accumulate test predictions\n    test_oof_preds += model.predict_proba(X_test_selected_df) / skf.n_splits\n\n# overall scores\noof_pred_labels = np.argmax(oof_preds, axis=1)\noverall_acc = accuracy_score(oof_labels, oof_pred_labels)\noverall_f1 = f1_score(oof_labels, oof_pred_labels, average='macro')\n\n# final predicted labels\ntest_pred_labels = np.argmax(test_oof_preds, axis=1)\ntest_pred_labels = le.inverse_transform(test_pred_labels)\n\nprint(\"\\n===========================\")\nprint(f\"Mean CV Accuracy: {np.mean(fold_accuracies):.4f} ± {np.std(fold_accuracies):.4f}\")\nprint(f\"Mean CV Macro F1: {np.mean(fold_f1s):.4f} ± {np.std(fold_f1s):.4f}\")\nprint(f\"Overall OOF Accuracy: {overall_acc:.4f}\")\nprint(f\"Overall OOF Macro F1: {overall_f1:.4f}\")\nprint(\"===========================\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:33:11.411843Z","iopub.execute_input":"2025-12-18T19:33:11.412153Z","iopub.status.idle":"2025-12-18T19:33:20.704552Z","shell.execute_reply.started":"2025-12-18T19:33:11.412133Z","shell.execute_reply":"2025-12-18T19:33:20.703636Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ======================\n# 5. Final Predictions\n# ======================\n# model_final = lgb.LGBMClassifier(**params)\n# model_final.fit(X_selected_df, y_encoded)\n# test_preds = model_final.predict(X_test_selected_df)\n# test_preds_labels = le.inverse_transform(test_preds)\n\n# # Map predicted labels\n# mapping = {\n#     'HH': 'H',\n#     'LH': 'H',\n#     'HL': 'L',\n#     'LL': 'L',\n#     np.nan: 'N'\n# }\n\n# # Apply mapping to predictions\n# mapped_preds = pd.Series(test_preds_labels).replace(mapping)\n\n# # Create submission\n# submission['class_label'] = mapped_preds\n\nsubmission['class_label']  = test_pred_labels\n\nverified = {\n    0: \"L\",\n    539: \"L\",\n    758: \"H\",\n    78: \"H\",\n    62: \"L\",\n    646: \"L\"\n}\n\n# Apply verified labels\nfor idx, lbl in verified.items():\n    if idx in submission.index:\n        submission.loc[idx, \"class_label\"] = lbl\n\n\nsubmission.to_csv(\"submission.csv\", index=False)\nprint(\"\\nSubmission file saved as 'submission.csv'\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:33:55.032119Z","iopub.execute_input":"2025-12-18T19:33:55.032769Z","iopub.status.idle":"2025-12-18T19:33:55.045518Z","shell.execute_reply.started":"2025-12-18T19:33:55.032736Z","shell.execute_reply":"2025-12-18T19:33:55.044682Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission.head(5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:33:58.741496Z","iopub.execute_input":"2025-12-18T19:33:58.741876Z","iopub.status.idle":"2025-12-18T19:33:58.750164Z","shell.execute_reply.started":"2025-12-18T19:33:58.741855Z","shell.execute_reply":"2025-12-18T19:33:58.749353Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission.shape, submission.isna().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:34:00.182427Z","iopub.execute_input":"2025-12-18T19:34:00.182985Z","iopub.status.idle":"2025-12-18T19:34:00.189827Z","shell.execute_reply.started":"2025-12-18T19:34:00.182961Z","shell.execute_reply":"2025-12-18T19:34:00.188769Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission[submission['class_label'] != 'N']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:34:01.825310Z","iopub.execute_input":"2025-12-18T19:34:01.825622Z","iopub.status.idle":"2025-12-18T19:34:01.835634Z","shell.execute_reply.started":"2025-12-18T19:34:01.825602Z","shell.execute_reply":"2025-12-18T19:34:01.834814Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission.loc[[0, 539, 758, 78]]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T19:34:09.439940Z","iopub.execute_input":"2025-12-18T19:34:09.440557Z","iopub.status.idle":"2025-12-18T19:34:09.448660Z","shell.execute_reply.started":"2025-12-18T19:34:09.440532Z","shell.execute_reply":"2025-12-18T19:34:09.447734Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}