{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":96164,"databundleVersionId":11418275,"sourceType":"competition"}],"dockerImageVersionId":31041,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"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,"execution":{"iopub.status.busy":"2025-06-09T16:58:11.436279Z","iopub.execute_input":"2025-06-09T16:58:11.436526Z","iopub.status.idle":"2025-06-09T16:58:11.700902Z","shell.execute_reply.started":"2025-06-09T16:58:11.436504Z","shell.execute_reply":"2025-06-09T16:58:11.700291Z"}},"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\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    FEATURES         = [\n        \"X863\",\"X856\",\"X344\",\"X598\",\"X862\",\"X385\",\"X852\",\"X603\",\n        \"X860\",\"X674\",\"X415\",\"X345\",\"X137\",\"X855\",\"X174\",\"X302\",\n        \"X178\",\"X532\",\"X168\",\"X612\",\n        \"bid_qty\",\"ask_qty\",\"buy_qty\",\"sell_qty\",\"volume\"\n    ]\n    LABEL_COLUMN     = \"label\"\n    N_FOLDS          = 3\n    RANDOM_STATE     = 42\n\n# Hyperparameters for XGBoost and LightGBM\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    \"verbose\": False,\n}\n\nLGBM_PARAMS = {\n    \"boosting_type\": \"gbdt\",\n    \"device\": \"gpu\",\n    \"n_jobs\": -1,\n    \"verbose\": -1,\n    \"random_state\": Config.RANDOM_STATE,\n    \"colsample_bytree\": 0.5039,\n    \"learning_rate\": 0.01260,\n    \"min_child_samples\": 20,\n    \"min_child_weight\": 0.1146,\n    \"n_estimators\": 915,\n    \"num_leaves\": 145,\n    \"reg_alpha\": 19.2447,\n    \"reg_lambda\": 55.5046,\n    \"subsample\": 0.9709,\n    \"max_depth\": 9\n}\n\nLEARNERS = [\n    {\"name\": \"xgb\",  \"Estimator\": XGBRegressor,  \"params\": XGB_PARAMS},\n    {\"name\": \"lgbm\", \"Estimator\": LGBMRegressor, \"params\": LGBM_PARAMS}\n]\n\nMODEL_SLICES = [\n    {\"name\": \"full_data\",   \"cutoff\": 0},\n    {\"name\": \"last_75pct\",  \"cutoff\": 0},  # to be set after loading\n    {\"name\": \"last_50pct\",  \"cutoff\": 0}\n]\n\n\ndef create_time_decay_weights(n: int, decay: float = 0.95) -> np.ndarray:\n    positions = np.arange(n)\n    normalized = positions / float(n - 1)\n    weights = decay ** (1.0 - normalized)\n    return weights * n / weights.sum()\n\n\ndef load_data():\n    train_df = pd.read_parquet(\n        Config.TRAIN_PATH,\n        columns=Config.FEATURES + [Config.LABEL_COLUMN]\n    ).reset_index(drop=True)\n    test_df = pd.read_parquet(\n        Config.TEST_PATH,\n        columns=Config.FEATURES\n    ).reset_index(drop=True)\n    submission_df = pd.read_csv(Config.SUBMISSION_PATH)\n    print(f\"Loaded train: {train_df.shape}, test: {test_df.shape}, submission: {submission_df.shape}\")\n    return train_df, test_df, submission_df\n\n\n\n############################\n# MAIN\n############################\n\ntrain_df, test_df, submission_df = load_data()\nn_samples = len(train_df)\n# set slice cutoffs\nMODEL_SLICES[1][\"cutoff\"] = int(0.25 * n_samples)\nMODEL_SLICES[2][\"cutoff\"] = int(0.50 * n_samples)\n\n# prepare storage for OOF and test preds\noof_preds = {\n    learner[\"name\"]: {sl[\"name\"]: np.zeros(n_samples) for sl in MODEL_SLICES}\n    for learner in LEARNERS\n}\ntest_preds = {\n    learner[\"name\"]: {sl[\"name\"]: np.zeros(len(test_df)) for sl in MODEL_SLICES}\n    for learner in LEARNERS\n}\n\nfull_weights = create_time_decay_weights(n_samples)\nkf = KFold(n_splits=Config.N_FOLDS, shuffle=False)\n\n# cross-validation\nfor 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 sl in MODEL_SLICES:\n        slice_name = sl[\"name\"]\n        cutoff     = sl[\"cutoff\"]\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\n        # sample weights\n        if cutoff == 0:\n            sw = full_weights[train_idx]\n        else:\n            sw_total = create_time_decay_weights(len(subset))\n            sw = sw_total[rel_idx]\n\n        for learner in LEARNERS:\n            name      = learner[\"name\"]\n            Estimator = learner[\"Estimator\"]\n            params    = learner[\"params\"]\n\n            model = Estimator(**params)\n            model.fit(X_train, y_train, sample_weight=sw,\n                      eval_set=[(X_valid, y_valid)])\n\n            # OOF predictions\n            mask = valid_idx >= cutoff\n            if mask.any():\n                idxs = valid_idx[mask]\n                oof_preds[name][slice_name][idxs] = model.predict(\n                    train_df.iloc[idxs][Config.FEATURES])\n            if cutoff > 0 and (~mask).any():\n                oof_preds[name][slice_name][valid_idx[~mask]] = (\n                    oof_preds[name][\"full_data\"][valid_idx[~mask]])\n\n            # test predictions\n            test_preds[name][slice_name] += model.predict(test_df[Config.FEATURES])\n\n# average test preds\nfor name in test_preds:\n    for slice_name in test_preds[name]:\n        test_preds[name][slice_name] /= Config.N_FOLDS\n\n# compute Pearson scores per learner and slice\npearson_scores = {\n    name: {slice_name: pearsonr(train_df[Config.LABEL_COLUMN], preds)[0]\n           for slice_name, preds in slices.items()}\n    for name, slices in oof_preds.items()\n}\nprint(\"\\nPearson scores by learner and slice:\")\nprint(pearson_scores)\n\n# -- Ensemble per learner across slices --\nlearner_ensembles = {}\nfor learner_name, slice_scores in pearson_scores.items():\n    # simple ensemble\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    # weighted ensemble\n    total_score = sum(slice_scores.values())\n    slice_weights = {sn: sc/total_score for sn, sc in slice_scores.items()}\n    oof_weighted = sum(slice_weights[sn] * oof_preds[learner_name][sn]\n                       for sn in slice_weights)\n    test_weighted = sum(slice_weights[sn] * test_preds[learner_name][sn]\n                        for sn in slice_weights)\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    }\n\n# -- Final ensemble across learners (simple) --\nfinal_oof = np.mean([le[\"oof_simple\"] for le in learner_ensembles.values()], axis=0)\nfinal_test = np.mean([le[\"test_simple\"] for le in learner_ensembles.values()], axis=0)\nfinal_score = pearsonr(train_df[Config.LABEL_COLUMN], final_oof)[0]\nprint(f\"\\nFINAL ensemble across learners Pearson: {final_score:.4f}\")\n\n# save submission\nsubmission_df[\"prediction\"] = final_test\nsubmission_df.to_csv(\"submission.csv\", index=False)\nprint(\"Wrote submission.csv\")\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-09T16:59:22.565698Z","iopub.execute_input":"2025-06-09T16:59:22.566378Z"}},"outputs":[],"execution_count":null}]}