{"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.10.14"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":84493,"databundleVersionId":9871156,"sourceType":"competition"},{"sourceId":10445023,"sourceType":"datasetVersion","datasetId":6406397},{"sourceId":10445263,"sourceType":"datasetVersion","datasetId":6465466},{"sourceId":203900450,"sourceType":"kernelVersion"}],"dockerImageVersionId":30787,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true},"papermill":{"default_parameters":{},"duration":7.594014,"end_time":"2024-10-10T11:58:36.355301","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2024-10-10T11:58:28.761287","version":"2.6.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Model is based on Xiang Sheng's notebooks:\n\n- Preprocessing : https://www.kaggle.com/code/motono0223/js24-preprocessing-create-lags\n- Training (NN): https://www.kaggle.com/code/voix97/jane-street-rmf-training-nn\n  - trained NN model : https://www.kaggle.com/datasets/voix97/js-xs-nn-trained-model\n- Inference of NN : **this notebook** https://www.kaggle.com/code/voix97/jane-street-rmf-nn-with-pytorch-lightning\n","metadata":{}},{"cell_type":"markdown","source":"# Pytorch Lightning Neural Networks Inference ","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport polars as pl\nimport numpy as np\nimport os, gc\nfrom tqdm.auto import tqdm\nfrom matplotlib import pyplot as plt\nimport pickle\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom pytorch_lightning import (LightningDataModule, LightningModule, Trainer)\nfrom pytorch_lightning.callbacks import EarlyStopping, ModelCheckpoint, Timer\n\nimport pandas as pd\nimport numpy as np\nfrom sklearn.metrics import r2_score\nfrom sklearn.model_selection import train_test_split\nfrom torch.utils.data import Dataset, DataLoader\n\n\nfrom sklearn.metrics import r2_score\nfrom lightgbm import LGBMRegressor\nimport lightgbm as lgb\nfrom xgboost import XGBRegressor\nfrom catboost import CatBoostRegressor\nfrom sklearn.ensemble import VotingRegressor\n\nimport warnings\nwarnings.filterwarnings('ignore')\npd.options.display.max_columns = None\n\nimport kaggle_evaluation.jane_street_inference_server","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-13T03:15:34.694655Z","iopub.execute_input":"2025-01-13T03:15:34.694964Z","iopub.status.idle":"2025-01-13T03:15:46.794158Z","shell.execute_reply.started":"2025-01-13T03:15:34.694935Z","shell.execute_reply":"2025-01-13T03:15:46.793463Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Configurations","metadata":{}},{"cell_type":"code","source":"class CONFIG:\n    seed = 42\n    target_col = \"responder_6\"\n    \n    # Column definitions\n    all_cols = [\"date_id\", \"symbol_id\", \"time_id\", \"weight\"] + \\\n               [f\"feature_{idx:02d}\" for idx in range(79)] + \\\n               [f\"responder_{idx}_lag_1\" for idx in range(9)] + [target_col]\n               \n    test_cols = [\"row_id\", \"date_id\", \"symbol_id\", \"time_id\"] + \\\n                [f\"feature_{idx:02d}\" for idx in range(79)] + \\\n                [f\"responder_{idx}_lag_1\" for idx in range(9)] + [target_col]\n                \n    feature_cols = [f\"feature_{idx:02d}\" for idx in range(79)] + [f\"responder_{idx}_lag_1\" for idx in range(9)]\n                   \n    lag_cols_rename = {f\"responder_{idx}\": f\"responder_{idx}_lag_1\" \n                      for idx in range(9)}\n                      \n    only_features = [\"row_id\", \"date_id\", \"symbol_id\", \"time_id\"] + \\\n                    [f\"feature_{idx:02d}\" for idx in range(79)]\n                    \n    only_lags = [\"row_id\", \"date_id\", \"symbol_id\", \"time_id\"] + \\\n                [f\"responder_{idx}_lag_1\" for idx in range(9)]\n    \n    # Model paths\n    model_paths = [\"/kaggle/input/trained-aemlp\"]\n    scaler = \"/kaggle/input/scaler/scaler.pt\"\n    # Training settings\n    BATCH_SIZE = 16384\n    TRAIN_EVERY_N_DAYS = 5\n    \n    # Flags\n    retrain = True\n    EVAL = False  # Set to True for evaluation mode","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-13T03:15:46.798880Z","iopub.execute_input":"2025-01-13T03:15:46.799114Z","iopub.status.idle":"2025-01-13T03:15:46.806176Z","shell.execute_reply.started":"2025-01-13T03:15:46.799090Z","shell.execute_reply":"2025-01-13T03:15:46.805319Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Load preprocessed data (to calculate CV)","metadata":{}},{"cell_type":"code","source":"valid = pl.scan_parquet(\n    f\"/kaggle/input/js24-preprocessing-create-lags/validation.parquet/\"\n).collect().to_pandas()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-13T03:15:46.807792Z","iopub.execute_input":"2025-01-13T03:15:46.808037Z","iopub.status.idle":"2025-01-13T03:15:49.374737Z","shell.execute_reply.started":"2025-01-13T03:15:46.808013Z","shell.execute_reply":"2025-01-13T03:15:49.374030Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Load model","metadata":{}},{"cell_type":"code","source":"class SupervisedAE(LightningModule):\n    def __init__(self, input_dim, hidden_dims, dropouts, lr, weight_decay, recon_weight, noise_std=0.1):\n        super().__init__()\n        self.save_hyperparameters()\n        self.noise_std = noise_std\n        self.lr = lr\n        self.weight_decay = weight_decay\n        self.recon_weight = recon_weight\n\n        # Encoder with residual connections\n        self.encoder_layers = nn.ModuleList()\n        in_dim = input_dim\n        for hd in hidden_dims:\n            self.encoder_layers.append(nn.Sequential(\n                nn.Linear(in_dim, hd),\n                nn.SiLU(),\n                nn.Dropout(dropouts[1])\n            ))\n            if in_dim == hd:  # Add residual connection if dimensions match\n                self.encoder_layers[-1].residual = True\n            else:\n                self.encoder_layers[-1].residual = False\n            in_dim = hd\n\n        # Decoder with similar architecture\n        self.decoder_layers = nn.ModuleList()\n        rev_dims = list(reversed(hidden_dims))\n        in_dim = rev_dims[0]\n        for hd in rev_dims[1:]:\n            self.decoder_layers.append(nn.Sequential(\n                nn.Linear(in_dim, hd),\n                nn.SiLU(),\n                nn.Dropout(dropouts[0])\n            ))\n            in_dim = hd\n        self.decoder_layers.append(nn.Linear(in_dim, input_dim))\n\n        # Single prediction head for responder_6\n        self.head = nn.Sequential(\n            nn.Linear(hidden_dims[-1], hidden_dims[-1] // 2),\n            nn.SiLU(),\n            nn.Dropout(dropouts[0]),\n            nn.Linear(hidden_dims[-1] // 2, 1)\n        )\n\n        self.validation_step_outputs = []\n\n    def forward(self, x):\n        # Add noise during training\n        if self.training:\n            x = x + torch.randn_like(x) * self.noise_std\n\n        # Encoder with residual connections\n        z = x\n        for layer in self.encoder_layers:\n            if layer.residual:\n                z = z + layer(z)\n            else:\n                z = layer(z)\n\n        # Decoder\n        x_hat = z\n        for layer in self.decoder_layers:\n            x_hat = layer(x_hat)\n\n        # Prediction for responder_6\n        y_lin = self.head(z)\n        y_hat = 5.0 * torch.tanh(y_lin).squeeze(-1)  # Bound predictions like original\n\n        return x_hat, y_hat\n\n    def training_step(self, batch, batch_idx):\n        x, y, w = batch\n        x_hat, y_hat = self(x)\n\n        # Reconstruction loss\n        recon_loss = F.mse_loss(x_hat, x, reduction=\"none\").mean(dim=1)\n\n        # Supervised loss for responder_6\n        sup_loss = F.mse_loss(y_hat, y, reduction=\"none\")\n\n        # Combine losses with weights\n        total_loss = (self.recon_weight * recon_loss + sup_loss) * w\n        loss = total_loss.mean()\n\n        # Log metrics\n        self.log(\"train_loss\", loss)\n        self.log(\"train_recon_loss\", recon_loss.mean())\n        self.log(\"train_sup_loss\", sup_loss.mean())\n\n        return loss\n\n    def validation_step(self, batch, batch_idx):\n        x, y, w = batch\n        x_hat, y_hat = self(x)\n\n        # Reconstruction loss\n        recon_loss = F.mse_loss(x_hat, x, reduction=\"none\").mean(dim=1)\n\n        # Supervised loss for responder_6\n        sup_loss = F.mse_loss(y_hat, y, reduction=\"none\")\n\n        # Combine losses with weights\n        total_loss = (self.recon_weight * recon_loss + sup_loss) * w\n        val_loss = total_loss.mean()\n\n        # Log validation metrics\n        self.log(\"val_loss\", val_loss, prog_bar=True, on_step=False, on_epoch=True)\n        self.log(\"val_recon_loss\", recon_loss.mean(), on_step=False, on_epoch=True)\n        self.log(\"val_sup_loss\", sup_loss.mean(), on_step=False, on_epoch=True)\n\n        # Store predictions for R^2 calculation\n        self.validation_step_outputs.append({\n            'y_hat': y_hat.detach(),\n            'y_true': y.detach(),\n            'weights': w.detach()\n        })\n\n        return val_loss\n\n    def on_validation_epoch_end(self):\n        \"\"\"Calculate validation weighted R^2 at the end of the epoch.\"\"\"\n        # Concatenate all batches\n        y_hat = torch.cat([x['y_hat'] for x in self.validation_step_outputs]).cpu().numpy()\n        y_true = torch.cat([x['y_true'] for x in self.validation_step_outputs]).cpu().numpy()\n        weights = torch.cat([x['weights'] for x in self.validation_step_outputs]).cpu().numpy()\n\n        # Calculate weighted R^2\n        numerator = np.average((y_hat - y_true)**2, weights=weights)\n        denominator = np.average(y_true**2, weights=weights) + 1e-38\n        val_r2 = 1.0 - numerator / denominator\n\n        # Log metrics\n        self.log(\"val_r_square\", val_r2, prog_bar=True)\n\n        # Clear stored outputs\n        self.validation_step_outputs.clear()\n\n    def configure_optimizers(self):\n        optimizer = torch.optim.Adam(self.parameters(), lr=self.lr, weight_decay=self.weight_decay)\n        scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(\n            optimizer,\n            mode='min',\n            factor=0.5,\n            patience=5,\n            verbose=True\n        )\n        return {\n            'optimizer': optimizer,\n            'lr_scheduler': {\n                'scheduler': scheduler,\n                'monitor': 'val_loss',\n            }\n        }\n\n    def on_train_epoch_end(self):\n        if self.trainer.sanity_checking:\n            return\n        epoch = self.trainer.current_epoch\n        metrics = {k: v.item() if isinstance(v, torch.Tensor) else v\n                  for k, v in self.trainer.logged_metrics.items()}\n        formatted_metrics = {k: f\"{v:.5f}\" for k, v in metrics.items()}\n        print(f\"Epoch {epoch}: {formatted_metrics}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-13T03:15:56.777303Z","iopub.execute_input":"2025-01-13T03:15:56.777610Z","iopub.status.idle":"2025-01-13T03:15:56.796038Z","shell.execute_reply.started":"2025-01-13T03:15:56.777584Z","shell.execute_reply":"2025-01-13T03:15:56.795188Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"N_folds = 5\nmodels = []\nfor fold in range(N_folds):\n    checkpoint_path = f\"{CONFIG.model_paths[0]}/nn_{fold}.model.ckpt\"\n    model = SupervisedAE.load_from_checkpoint(checkpoint_path)\n    models.append(model.to(\"cuda:0\"))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-13T03:16:02.327660Z","iopub.execute_input":"2025-01-13T03:16:02.328606Z","iopub.status.idle":"2025-01-13T03:16:04.591665Z","shell.execute_reply.started":"2025-01-13T03:16:02.328558Z","shell.execute_reply":"2025-01-13T03:16:04.590737Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"models[0]","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# CV Score","metadata":{}},{"cell_type":"code","source":"# Extract features, target, and weights from the validation set\nX_valid = valid[CONFIG.feature_cols]\ny_valid = valid[CONFIG.target_col]\nw_valid = valid[\"weight\"]\n\n# Handle missing values\nX_valid = X_valid.fillna(method='ffill').fillna(0)\n\n# Print shapes for debugging\nprint(f\"X_valid.shape: {X_valid.shape}\")\nprint(f\"y_valid.shape: {y_valid.shape}\")\nprint(f\"w_valid.shape: {w_valid.shape}\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_pred_valid = np.zeros_like(y_valid.to_numpy())\nprint(f\"Initialized y_pred_valid with shape: {y_pred_valid.shape}\")\n\nwith torch.no_grad():\n    for model in models:\n        model.eval()\n        # Forward pass\n        x_hat, y_hat = model(torch.FloatTensor(X_valid.values).to(\"cuda:0\"))\n        # Accumulate the predictions\n        y_pred_valid += y_hat.cpu().numpy() / len(models)\n\nprint(f\"Final y_pred_valid shape: {y_pred_valid.shape}\")\nprint(f\"First 10 predictions: {y_pred_valid[:10]}\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"del valid, X_valid, y_valid, w_valid\ngc.collect()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-13T03:16:08.412948Z","iopub.execute_input":"2025-01-13T03:16:08.413891Z","iopub.status.idle":"2025-01-13T03:16:08.573223Z","shell.execute_reply.started":"2025-01-13T03:16:08.413841Z","shell.execute_reply":"2025-01-13T03:16:08.572012Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict(test: pl.DataFrame, lags: pl.DataFrame | None) -> pl.DataFrame | pd.DataFrame:\n    \"\"\"Make predictions and do a single training step when new labels arrive\"\"\"\n    global models\n\n    # Define paths for saving and loading models\n    MODEL_PATHS = [f\"model_{i}.pt\" for i in range(len(models))]  # Paths for model checkpoints\n    LAST_DAY_PATH = \"last_day.txt\"  # Path to the file storing the last day's date ID\n\n    # Load scaler if not exists\n    if not hasattr(predict, \"scaler\"):\n        predict.scaler = torch.load(CONFIG.scaler)\n\n    # Load saved models from previous day\n    date_id = test['date_id'].unique()[0]\n    if os.path.exists(LAST_DAY_PATH):\n        with open(LAST_DAY_PATH, 'r') as f:\n            last_day = int(f.read())\n        if last_day < date_id:\n            if DEBUG:\n                print(f\"Loading models from day {last_day}\")\n            for model, path in zip(models, MODEL_PATHS):\n                if os.path.exists(path):\n                    model.load_state_dict(torch.load(path))\n\n    if DEBUG and lags is not None and 0 in test['time_id'].to_numpy():\n        print(f\"Processing debug day {date_id}\")\n\n    test = test.fill_null(-1)\n\n    test_with_features = test\n    for col in set(CONFIG.feature_cols) - set(test.columns):\n        test_with_features = test_with_features.with_columns(pl.lit(-1).alias(col))\n\n    time_ids = test_with_features['time_id'].unique().sort()\n    preds = np.zeros((test.shape[0],))\n\n    for time_id in time_ids:\n        time_mask = test_with_features['time_id'] == time_id\n        step_data = test_with_features.filter(time_mask)\n\n        # Normalize features\n        X = (step_data.select(CONFIG.feature_cols).fill_null(-1).to_numpy() -\n             predict.scaler['mean'].to_numpy()) / predict.scaler['std'].to_numpy()\n\n        with torch.no_grad():\n            test_tensor = torch.FloatTensor(X).to(\"cuda:0\")\n            step_preds = np.zeros((len(step_data),))\n\n            for model in models:\n                model.eval()\n                _, y_hat = model(test_tensor)\n                step_preds += y_hat.cpu().numpy() / len(models)\n\n            step_indices = test_with_features['time_id'] == time_id\n            preds[step_indices] = step_preds\n\n        if time_id == 0 and lags is not None:\n            train_data = step_data.join(\n                lags,\n                on=[\"date_id\", \"time_id\", \"symbol_id\"],\n                how=\"inner\"\n            )\n\n            if len(train_data) > 0:\n                # Normalize features and get labels\n                X_train = (train_data.select(CONFIG.feature_cols).fill_null(-1).to_numpy() -\n                           predict.scaler['mean'].to_numpy()) / predict.scaler['std'].to_numpy()\n                y_train = train_data.select(['responder_6_lag_1']).fill_null(-1).to_numpy().flatten()\n\n                X_train = torch.FloatTensor(X_train).to(\"cuda:0\")\n                y_train = torch.FloatTensor(y_train).to(\"cuda:0\")\n\n                for i, model in enumerate(models):\n                    model.train()\n                    model.recon_weight = 0.01\n                    optimizer = torch.optim.Adam(model.parameters(), lr= 0.000001)\n\n                    # Single forward/backward pass without noise injection\n                    model.noise_std = 0.1\n                    x_recon, y_pred = model(X_train)\n                    loss = model.training_step((X_train, y_train, torch.ones_like(y_train)), 0)\n\n                    if torch.isnan(loss):\n                        if DEBUG:\n                            print(\"Warning: NaN loss, skipping update\")\n                        continue\n\n                    loss.backward()\n                    torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.5)\n                    optimizer.step()\n                    optimizer.zero_grad()\n                    model.eval()\n\n                    # Save the model after training\n                    torch.save(model.state_dict(), MODEL_PATHS[i])\n\n    # Save the current date ID\n    with open(LAST_DAY_PATH, 'w') as f:\n        f.write(str(date_id))\n\n    predictions = (\n        test.select('row_id')\n        .with_columns(\n            pl.Series(\n                name='responder_6',\n                values=np.clip(preds, a_min=-5, a_max=5),\n                dtype=pl.Float64,\n            )\n        )\n    )\n    return predictions","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-13T04:39:19.445646Z","iopub.execute_input":"2025-01-13T04:39:19.446351Z","iopub.status.idle":"2025-01-13T04:39:19.460184Z","shell.execute_reply.started":"2025-01-13T04:39:19.446320Z","shell.execute_reply":"2025-01-13T04:39:19.459335Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### There seems to be bug in official code, can only submit polars dataframe","metadata":{}},{"cell_type":"code","source":"def makelag(date_id):\n    \"\"\"\n    Making lag at the previout day\n\n    Args:\n    date_id (int): date_id at the previout day\n    \n    Returns:\n    pl.dataframe\n    \"\"\"\n    responder_cols = [s for s in train.columns if \"responder\" in s]\n    lag = alltraindata.filter(pl.col(\"date_id\")==date_id).select([\"date_id\",\"time_id\",\"symbol_id\"] + responder_cols).collect()\n    lag.columns = lag_sample.columns\n    \n    return lag\n\ndef weighted_zero_mean_r2(y_true, y_pred, weights):\n    \"\"\"\n    Calculate the sample weighted zero-mean R-squared score.\n    \"\"\"\n    numerator = np.sum(weights * (y_true - y_pred)**2)\n    denominator = np.sum(weights * y_true**2)\n    \n    r2_score = 1 - numerator / denominator\n    return r2_score","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-13T03:23:10.300059Z","iopub.execute_input":"2025-01-13T03:23:10.300427Z","iopub.status.idle":"2025-01-13T03:23:10.306330Z","shell.execute_reply.started":"2025-01-13T03:23:10.300395Z","shell.execute_reply":"2025-01-13T03:23:10.305463Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DEBUG = False\nif DEBUG:\n    # Use competition paths\n    lag_sample = pl.read_parquet(\"/kaggle/input/jane-street-realtime-marketdata-forecasting/lags.parquet/date_id=0/part-0.parquet\")\n    alltraindata = pl.scan_parquet(\"/kaggle/input/jane-street-realtime-marketdata-forecasting/train.parquet\")\n    \n    # pick 50 days\n    nb_days = 100\n    train = alltraindata.filter(pl.col(\"date_id\")>1698-nb_days).collect()\n    train = train.with_columns(pl.Series(range(len(train))).alias(\"row_id\"))\n\n    all_submission_dataframe = []\n    \n    # Process day by day\n    for num_days, df_per_day in train.group_by(\"date_id\", maintain_order=True):\n        print(f\"\\nProcessing debug day {num_days[0]}\")\n        \n        for time_id, test in df_per_day.group_by(\"time_id\", maintain_order=True):\n            # when time_id == 0, make lags\n            if time_id[0] == 0:\n                lag = makelag(num_days[0] - 1)\n            else:\n                lag = None\n            \n            submission_dataframe = predict(test, lag)\n            all_submission_dataframe.append(submission_dataframe)\n            \n    all_submission_dataframe = pl.concat(all_submission_dataframe)\n    \n    # Calculate score\n    print(\"\\nCalculating final score...\")\n    score = weighted_zero_mean_r2(\n        train.select(\"responder_6\").to_numpy().reshape(-1),\n        all_submission_dataframe.select(\"responder_6\").to_numpy().reshape(-1),\n        train.select(\"weight\").to_numpy().reshape(-1)\n    )\n    print(f\"Final Debug Score: {score:.6f}\")\n\n","metadata":{"trusted":true,"_kg_hide-output":false,"execution":{"iopub.status.busy":"2025-01-13T04:39:23.757290Z","iopub.execute_input":"2025-01-13T04:39:23.757620Z","iopub.status.idle":"2025-01-13T04:51:26.117174Z","shell.execute_reply.started":"2025-01-13T04:39:23.757591Z","shell.execute_reply":"2025-01-13T04:51:26.116240Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"When your notebook is run on the hidden test set, inference_server.serve must be called within 15 minutes of the notebook starting or the gateway will throw an error. If you need more than 15 minutes to load your model you can do so during the very first `predict` call, which does not have the usual 10 minute response deadline.","metadata":{"papermill":{"duration":0.002521,"end_time":"2024-10-10T11:58:33.6023","exception":false,"start_time":"2024-10-10T11:58:33.599779","status":"completed"},"tags":[]}},{"cell_type":"code","source":"inference_server = kaggle_evaluation.jane_street_inference_server.JSInferenceServer(predict)\n\nif os.getenv('KAGGLE_IS_COMPETITION_RERUN'):\n    inference_server.serve()\nelse:\n    inference_server.run_local_gateway(\n        (\n            '/kaggle/input/jane-street-realtime-marketdata-forecasting/test.parquet',\n            '/kaggle/input/jane-street-realtime-marketdata-forecasting/lags.parquet',\n        )\n    )","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","papermill":{"duration":2.225871,"end_time":"2024-10-10T11:58:35.830964","exception":false,"start_time":"2024-10-10T11:58:33.605093","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null}]}