{"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":"gpu","dataSources":[{"sourceId":96164,"databundleVersionId":11418275,"sourceType":"competition"},{"sourceId":12405352,"sourceType":"datasetVersion","datasetId":7823194}],"dockerImageVersionId":31040,"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},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom sklearn.model_selection import KFold\nfrom sklearn.linear_model import Ridge\nimport warnings\n\nwarnings.filterwarnings(\"ignore\")\n\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    # use the submission file with reset order\n    SUBMISSION_PATH = \"/kaggle/input/drw-submission-order-reset/sample_submission_idx.csv\"\n\n    FEATURES = list(set([\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    lags = [1,3,5,10,20,60,120,180,240,60*24,60*24*2,60*24*3,60*24*4,60*24*5,60*24*6,60*24*7]\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\n\ndef add_features(df):\n    # Add small constant to avoid division by zero\n    eps = 1e-8\n    \n    df['bid_ask_interaction'] = df['bid_qty'] * df['ask_qty']\n    df['bid_buy_interaction'] = df['bid_qty'] * df['buy_qty']\n    df['bid_sell_interaction'] = df['bid_qty'] * df['sell_qty']\n    df['ask_buy_interaction'] = df['ask_qty'] * df['buy_qty']\n    df['ask_sell_interaction'] = df['ask_qty'] * df['sell_qty']\n\n    df['volume_weighted_sell'] = df['sell_qty'] * df['volume']\n    df['buy_sell_ratio'] = df['buy_qty'] / (df['sell_qty'] + eps)\n    df['selling_pressure'] = df['sell_qty'] / (df['volume'] + eps)\n    df['log_volume'] = np.log1p(df['volume'])\n\n    df['effective_spread_proxy'] = np.abs(df['buy_qty'] - df['sell_qty']) / (df['volume'] + eps)\n    df['bid_ask_imbalance'] = (df['bid_qty'] - df['ask_qty']) / (df['bid_qty'] + df['ask_qty'] + eps)\n    df['order_flow_imbalance'] = (df['buy_qty'] - df['sell_qty']) / (df['buy_qty'] + df['sell_qty'] + eps)\n    df['liquidity_ratio'] = (df['bid_qty'] + df['ask_qty']) / (df['volume'] + eps)\n    \n    # Add a few simple but effective features\n    df['total_qty'] = df['bid_qty'] + df['ask_qty'] + df['buy_qty'] + df['sell_qty']\n    df['net_order_flow'] = df['buy_qty'] - df['sell_qty']\n    df['relative_spread'] = (df['ask_qty'] - df['bid_qty']) / (df['ask_qty'] + df['bid_qty'] + eps)\n\n    FEATURES = [\n        \"X863\", \"X856\", \"X598\", \"X862\", \"X385\", \"X603\", \"X860\", \"X674\", \"X415\", \"X345\", \"X855\", \"X174\", \"X302\",\n        \"X178\", \"X168\", \"X612\", \"X333\", \"bid_qty\", \"ask_qty\", \"buy_qty\", \"sell_qty\", \"volume\", 'log_volume',\n        'bid_ask_interaction', 'bid_buy_interaction', 'bid_sell_interaction', 'ask_buy_interaction',\n        'ask_sell_interaction', 'total_qty', 'net_order_flow', 'relative_spread']\n\n    for col in FEATURES:\n        for lag in Config.lags:\n            df[f'{col}_lag_{lag}'] = df[col].shift(lag)\n            df[f'{col}_lead_{lag}'] = df[col].shift(-lag)\n\n    # Use forward fill then backward fill then mean for better time series handling\n    df = df.fillna(method='ffill').fillna(method='bfill').fillna(df.mean())\n\n    return df\n\n\ndef sort_test_df_by_time(df):\n    assert len(df.shape) == 2\n    assert df.shape[0] == 538150\n\n    n = df.shape[0]\n    t = pd.Series(np.arange(n))\n    t = t.sample(n=n, random_state=700)\n\n    t = pd.Series(np.arange(n), index=t.to_numpy()).sort_index()\n    return df.iloc[t.to_numpy()]\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\n    # restore the test time order\n    test_df = sort_test_df_by_time(test_df).reset_index(drop=True)\n\n    train_df = add_features(train_df)\n    test_df = add_features(test_df)\n\n    return train_df.reset_index(drop=True), test_df.reset_index(drop=True), submission_df\n\n\n# =========================\n# Training and Evaluation\n# =========================\ndef train_and_evaluate(train_df, test_df):\n    FEATURES = [\n        \"X863\", \"X856\", \"X598\", \"X862\", \"X385\", \"X603\", \"X860\", \"X674\", \"X415\", \"X345\", \"X855\", \"X174\", \"X302\",\n        \"X178\", \"X168\", \"X612\", \"X333\", \"bid_qty\", \"ask_qty\", \"buy_qty\", \"sell_qty\", \"volume\", 'log_volume',\n        'bid_ask_interaction', 'bid_buy_interaction', 'bid_sell_interaction', 'ask_buy_interaction',\n        'ask_sell_interaction', 'total_qty', 'net_order_flow', 'relative_spread']\n\n    lag_features = [f'{f}_lag_{l}' for f in FEATURES for l in Config.lags]\n    lead_features = [f'{f}_lead_{l}' for f in FEATURES for l in Config.lags]\n\n    FEATURES += lag_features\n    FEATURES += lead_features\n\n    n_samples = len(train_df)\n    test_preds = np.zeros(len(test_df))\n\n    full_weights = create_time_decay_weights(n_samples)\n    kf = KFold(n_splits=Config.N_FOLDS, shuffle=True, random_state=Config.RANDOM_STATE)\n\n    for fold, (train_idx, valid_idx) in enumerate(kf.split(train_df), start=1):\n        print(f\"Training fold {fold}/{Config.N_FOLDS}...\")\n\n        X_train = train_df.iloc[train_idx][FEATURES]\n        y_train = train_df.iloc[train_idx][Config.LABEL_COLUMN]\n        sw = full_weights[train_idx]\n\n        # Try different alpha values\n        model = Ridge(alpha=0.1, random_state=Config.RANDOM_STATE)\n        model.fit(\n            X_train, y_train,\n            sample_weight=sw\n        )\n\n        test_preds += model.predict(test_df[FEATURES])\n    \n    # Average predictions across folds\n    test_preds = test_preds / Config.N_FOLDS\n    \n    return test_preds\n\n\n\ndef ensemble_simple(test_preds, submission_df):\n    submission_df[\"prediction\"] = test_preds\n    submission_df = submission_df.sort_values(by='idx', ascending=True)\n    submission_df = submission_df.drop('idx', axis=1)\n    submission_df['ID'] = range(1, len(submission_df) + 1)\n    submission_df = submission_df.reset_index(drop=True)\n    submission_df.to_csv(f\"submission.csv\", index=False)\n\nif __name__ == \"__main__\":\n    train_df, test_df, submission_df = load_data()\n    test_preds = train_and_evaluate(train_df, test_df)\n    ensemble_simple(test_preds, submission_df)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-09T03:22:42.005178Z","iopub.execute_input":"2025-07-09T03:22:42.005401Z","iopub.status.idle":"2025-07-09T03:24:24.258250Z","shell.execute_reply.started":"2025-07-09T03:22:42.005377Z","shell.execute_reply":"2025-07-09T03:24:24.257288Z"}},"outputs":[],"execution_count":null}]}