{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.11.11"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":96164,"databundleVersionId":11418275,"sourceType":"competition"}],"dockerImageVersionId":31041,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true},"papermill":{"default_parameters":{},"duration":88.072356,"end_time":"2025-06-14T11:26:22.996960","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2025-06-14T11:24:54.924604","version":"2.6.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.execute_input":"2025-06-14T11:24:59.130004Z","iopub.status.busy":"2025-06-14T11:24:59.129733Z","iopub.status.idle":"2025-06-14T11:24:59.141132Z","shell.execute_reply":"2025-06-14T11:24:59.140408Z"},"papermill":{"duration":0.015772,"end_time":"2025-06-14T11:24:59.142415","exception":false,"start_time":"2025-06-14T11:24:59.126643","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import sys\nimport pandas as pd\nimport numpy as np\nfrom sklearn.model_selection import KFold\nfrom xgboost import XGBRegressor\nfrom lightgbm import LGBMRegressor\nfrom scipy.stats import pearsonr","metadata":{"execution":{"iopub.execute_input":"2025-06-14T11:24:59.147224Z","iopub.status.busy":"2025-06-14T11:24:59.146730Z","iopub.status.idle":"2025-06-14T11:25:06.373809Z","shell.execute_reply":"2025-06-14T11:25:06.373201Z"},"papermill":{"duration":7.230706,"end_time":"2025-06-14T11:25:06.375184","exception":false,"start_time":"2025-06-14T11:24:59.144478","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================\n# Configuration\n# =========================\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    FEATURES = [\n        \"X863\", \"X856\", \"X344\", \"X598\", \"X862\", \"X385\", \"X852\", \"X603\", \"X860\", \"X674\",\n        \"X415\", \"X345\", \"X137\", \"X855\", \"X174\", \"X302\", \"X178\", \"X532\", \"X168\", \"X612\",\n        \"bid_qty\", \"ask_qty\", \"buy_qty\", \"sell_qty\", \"volume\", \"X888\", \"X421\", \"X333\"\n    ]\n\n    LABEL_COLUMN = \"label\"\n    N_FOLDS = 3\n    RANDOM_STATE = 42\n\nXGB_PARAMS = {\n    \"tree_method\": \"hist\",\n    \"device\": \"gpu\",\n    \"colsample_bylevel\": 0.4778,\n    \"colsample_bynode\": 0.3628,\n    \"colsample_bytree\": 0.7107,\n    \"gamma\": 1.7095,\n    \"learning_rate\": 0.02213,\n    \"max_depth\": 20,\n    \"max_leaves\": 12,\n    \"min_child_weight\": 16,\n    \"n_estimators\": 1667,\n    \"subsample\": 0.06567,\n    \"reg_alpha\": 39.3524,\n    \"reg_lambda\": 75.4484,\n    \"verbosity\": 0,\n    \"random_state\": Config.RANDOM_STATE,\n    \"n_jobs\": -1\n}\n\nLEARNERS = [\n    {\"name\": \"xgb\", \"Estimator\": XGBRegressor, \"params\": XGB_PARAMS}\n]","metadata":{"execution":{"iopub.execute_input":"2025-06-14T11:25:06.379993Z","iopub.status.busy":"2025-06-14T11:25:06.379533Z","iopub.status.idle":"2025-06-14T11:25:06.385224Z","shell.execute_reply":"2025-06-14T11:25:06.384573Z"},"papermill":{"duration":0.009344,"end_time":"2025-06-14T11:25:06.386541","exception":false,"start_time":"2025-06-14T11:25:06.377197","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================\n# Utility Functions\n# =========================\ndef create_time_decay_weights(n: int, decay: float = 0.9) -> np.ndarray:\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_data():\n    train_df = pd.read_parquet(Config.TRAIN_PATH, columns=Config.FEATURES + [Config.LABEL_COLUMN])\n    test_df = pd.read_parquet(Config.TEST_PATH, columns=Config.FEATURES)\n    submission_df = pd.read_csv(Config.SUBMISSION_PATH)\n    print(f\"Loaded data - Train: {train_df.shape}, Test: {test_df.shape}, Submission: {submission_df.shape}\")\n    return train_df.reset_index(drop=True), test_df.reset_index(drop=True), submission_df\n\ndef get_model_slices(n_samples: int):\n    return [\n        {\"name\": \"full_data\", \"cutoff\": 0},\n        {\"name\": \"last_75pct\", \"cutoff\": int(0.25 * n_samples)},\n        {\"name\": \"last_50pct\", \"cutoff\": int(0.50 * n_samples)}\n    ]\n\n# =========================\n# Training and Evaluation\n# =========================\ndef train_and_evaluate(train_df, test_df):\n    n_samples = len(train_df)\n    model_slices = get_model_slices(n_samples)\n\n    oof_preds = {\n        learner[\"name\"]: {s[\"name\"]: np.zeros(n_samples) for s in model_slices}\n        for learner in LEARNERS\n    }\n    test_preds = {\n        learner[\"name\"]: {s[\"name\"]: np.zeros(len(test_df)) for s in model_slices}\n        for learner in LEARNERS\n    }\n\n    full_weights = create_time_decay_weights(n_samples)\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\n        for s in model_slices:\n            cutoff = s[\"cutoff\"]\n            slice_name = s[\"name\"]\n            subset = train_df.iloc[cutoff:].reset_index(drop=True)\n            rel_idx = train_idx[train_idx >= cutoff] - cutoff\n\n            X_train = subset.iloc[rel_idx][Config.FEATURES]\n            y_train = subset.iloc[rel_idx][Config.LABEL_COLUMN]\n            sw = create_time_decay_weights(len(subset))[rel_idx] if cutoff > 0 else full_weights[train_idx]\n\n            print(f\"  Training slice: {slice_name}, samples: {len(X_train)}\")\n\n            for learner in LEARNERS:\n                model = learner[\"Estimator\"](**learner[\"params\"])\n                model.fit(X_train, y_train, sample_weight=sw, eval_set=[(X_valid, y_valid)], verbose=False)\n\n                mask = valid_idx >= cutoff\n                if mask.any():\n                    idxs = valid_idx[mask]\n                    oof_preds[learner[\"name\"]][slice_name][idxs] = model.predict(train_df.iloc[idxs][Config.FEATURES])\n                if cutoff > 0 and (~mask).any():\n                    oof_preds[learner[\"name\"]][slice_name][valid_idx[~mask]] = oof_preds[learner[\"name\"]][\"full_data\"][valid_idx[~mask]]\n\n                test_preds[learner[\"name\"]][slice_name] += model.predict(test_df[Config.FEATURES])\n\n    # Normalize test predictions\n    for learner_name in test_preds:\n        for slice_name in test_preds[learner_name]:\n            test_preds[learner_name][slice_name] /= Config.N_FOLDS\n\n    return oof_preds, test_preds, model_slices","metadata":{"execution":{"iopub.execute_input":"2025-06-14T11:25:06.390801Z","iopub.status.busy":"2025-06-14T11:25:06.390616Z","iopub.status.idle":"2025-06-14T11:25:06.401095Z","shell.execute_reply":"2025-06-14T11:25:06.400617Z"},"papermill":{"duration":0.013922,"end_time":"2025-06-14T11:25:06.402128","exception":false,"start_time":"2025-06-14T11:25:06.388206","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================\n# Ensemble & Submission\n# =========================\ndef ensemble_and_submit(train_df, oof_preds, test_preds, submission_df):\n    learner_ensembles = {}\n    for learner_name in oof_preds:\n        scores = {s: pearsonr(train_df[Config.LABEL_COLUMN], oof_preds[learner_name][s])[0]\n                  for s in oof_preds[learner_name]}\n        total_score = sum(scores.values())\n\n        oof_simple = np.mean(list(oof_preds[learner_name].values()), axis=0)\n        test_simple = np.mean(list(test_preds[learner_name].values()), axis=0)\n        score_simple = pearsonr(train_df[Config.LABEL_COLUMN], oof_simple)[0]\n\n        oof_weighted = sum(scores[s] / total_score * oof_preds[learner_name][s] for s in scores)\n        test_weighted = sum(scores[s] / total_score * test_preds[learner_name][s] for s in scores)\n        score_weighted = pearsonr(train_df[Config.LABEL_COLUMN], oof_weighted)[0]\n\n        print(f\"\\n{learner_name.upper()} Simple Ensemble Pearson:   {score_simple:.4f}\")\n        print(f\"{learner_name.upper()} Weighted Ensemble Pearson: {score_weighted:.4f}\")\n\n        learner_ensembles[learner_name] = {\n            \"oof_simple\": oof_simple,\n            \"test_simple\": test_simple,\n            \"oof_weighted\": oof_weighted,\n            \"test_weighted\": test_weighted\n        }\n\n    final_oof = np.mean([le[\"oof_weighted\"] for le in learner_ensembles.values()], axis=0)\n    final_test = np.mean([le[\"test_weighted\"] for le in learner_ensembles.values()], axis=0)\n    final_score = pearsonr(train_df[Config.LABEL_COLUMN], final_oof)[0]\n\n    print(f\"\\nFINAL ensemble across learners Pearson: {final_score:.4f}\")\n\n    submission_df[\"prediction\"] = final_test\n    submission_df.to_csv(\"submission.csv\", index=False)\n    print(\"Saved: submission.csv\")","metadata":{"execution":{"iopub.execute_input":"2025-06-14T11:25:06.406164Z","iopub.status.busy":"2025-06-14T11:25:06.405978Z","iopub.status.idle":"2025-06-14T11:25:06.412838Z","shell.execute_reply":"2025-06-14T11:25:06.412138Z"},"papermill":{"duration":0.010179,"end_time":"2025-06-14T11:25:06.413978","exception":false,"start_time":"2025-06-14T11:25:06.403799","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================\n# Main Execution\n# =========================\nif __name__ == \"__main__\":\n    train_df, test_df, submission_df = load_data()\n    oof_preds, test_preds, model_slices = train_and_evaluate(train_df, test_df)\n    ensemble_and_submit(train_df, oof_preds, test_preds, submission_df)","metadata":{"execution":{"iopub.execute_input":"2025-06-14T11:25:06.417814Z","iopub.status.busy":"2025-06-14T11:25:06.417639Z","iopub.status.idle":"2025-06-14T11:26:22.174485Z","shell.execute_reply":"2025-06-14T11:26:22.173523Z"},"papermill":{"duration":75.760202,"end_time":"2025-06-14T11:26:22.175852","exception":false,"start_time":"2025-06-14T11:25:06.415650","status":"completed"},"tags":[]},"outputs":[],"execution_count":null}]}