{"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":"none","dataSources":[{"sourceId":96164,"databundleVersionId":12993472,"sourceType":"competition"}],"dockerImageVersionId":31041,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false},"papermill":{"default_parameters":{},"duration":88.072356,"end_time":"2025-06-14T11:26:22.99696","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":"markdown","source":"# 📈 DRW Crypto Market Prediction | Time Series + Ensemble\n\nWelcome to my solution for the **DRW Crypto Market Prediction** Kaggle competition!\n\n---\n\n> _If this helps you, consider giving it an upvote ❤️_\n","metadata":{}},{"cell_type":"markdown","source":"<a id='Imports'></a>\n# Imports","metadata":{}},{"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\nfrom sklearn.cross_decomposition import PLSRegression as PLS","metadata":{"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":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":" def transform_df(df, n=6):\n    x=[i for i in df.columns if i not in [\"label\"]]#\"bid_qty\",\"ask_qty\", \"buy_qty\",\"sell_qty\", \"volume\", \n    temp=df[x[:696]+x[717:]]\n    pls=PLS(n_components=n)\n    xpls=pls.fit_transform(temp, df[\"label\"])\n    df1=pd.DataFrame()\n    df1[[f\"c{i}\" for i in range(1, n+1)]]=xpls[0]\n    df1=pd.concat([df1, df.reset_index()[[\"bid_qty\",\"ask_qty\", \"buy_qty\",\"sell_qty\", \"volume\",\"label\"]]], axis=1)\n    return df1, pls\n    \ndef transform_test(df, pls, n=6):\n    x=[i for i in df.columns if i not in [\"label\"]]\n    temp=df[x[:696]+x[717:]]\n    xpls=pls.transform(temp)\n    print(xpls)\n    df1=pd.DataFrame()\n    df1[[f\"c{i}\" for i in range(1, n+1)]]=xpls\n    df1=pd.concat([df1, df.reset_index()[[\"bid_qty\",\"ask_qty\", \"sell_qty\", \"buy_qty\",\"volume\"]]], axis=1)\n    return df1","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"TRAIN_PATH = \"/kaggle/input/drw-crypto-market-prediction/train.parquet\"\ntrain_dft = pd.read_parquet(TRAIN_PATH)\nx=[i for i in train_dft.columns if i not in [\"bid_qty\",\"ask_qty\", \"buy_qty\",\"sell_qty\", \"volume\", \"label\"]]\ntrain_dft[x[699]].unique()","metadata":{"execution":{"iopub.status.busy":"2025-06-30T04:33:25.147260Z","iopub.execute_input":"2025-06-30T04:33:25.147557Z","iopub.status.idle":"2025-06-30T04:33:44.602306Z","shell.execute_reply.started":"2025-06-30T04:33:25.147536Z","shell.execute_reply":"2025-06-30T04:33:44.601097Z"}}},{"cell_type":"markdown","source":"<a id='feature'></a>\n# Feature Engineering","metadata":{}},{"cell_type":"code","source":"def feature_engineering(df):\n\n    df['volume_weighted_sell'] = df['sell_qty'] * df['volume']\n    df['buy_sell_ratio'] = df['buy_qty'] / (df['sell_qty'] + 1e-8)\n    df['selling_pressure'] = df['sell_qty'] / (df['volume'] + 1e-8)\n    df['effective_spread_proxy'] = np.abs(df['buy_qty'] - df['sell_qty']) / (df['volume'] + 1e-8)\n\n\n    df = df.replace([np.inf, -np.inf], np.nan)\n    df = df.fillna(0)\n    return df ","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id='Config'></a>\n# Configuration","metadata":{}},{"cell_type":"code","source":"class 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 = [f\"c{i}\" for i in range(1, 7)]+[\"buy_qty\", \"sell_qty\", \"volume\"]\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\": 50,\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":{"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":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id='load'></a>\n# Loading Data","metadata":{}},{"cell_type":"code","source":"def 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)\n    test_df = pd.read_parquet(Config.TEST_PATH)\n    submission_df = pd.read_csv(Config.SUBMISSION_PATH)\n    train_df, pls =transform_df(train_df)\n    test_df=transform_test(test_df, pls)\n    print(test_df)\n    train_df = feature_engineering(train_df)\n    test_df = feature_engineering(test_df)\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","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"Config.FEATURES += [\"bid_qty\", \"ask_qty\", \"buy_qty\", \"sell_qty\", \"volume\"]\nConfig.FEATURES = list(set(Config.FEATURES))  # remove duplicates\nprint(Config.FEATURES)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id='Train'></a>\n# Training and Evaluation","metadata":{}},{"cell_type":"code","source":"def 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\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":{"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":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id='Subm'></a>\n# Submission","metadata":{}},{"cell_type":"code","source":"def 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        }\n\n    final_oof = np.mean([le[\"oof_simple\"] for le in learner_ensembles.values()], axis=0)\n    final_test = np.mean([le[\"test_simple\"] 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":{"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":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id='Main'></a>\n# Main ","metadata":{}},{"cell_type":"code","source":"train_df, test_df, submission_df = load_data()\ntrain_df","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if __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)\n","metadata":{"papermill":{"duration":75.760202,"end_time":"2025-06-14T11:26:22.175852","exception":false,"start_time":"2025-06-14T11:25:06.41565","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null}]}