From af139ffbab1a99927fd62cb970c862516566a59f Mon Sep 17 00:00:00 2001 From: robcaulk Date: Mon, 1 May 2023 13:18:03 +0000 Subject: [PATCH 01/21] add transformer with positional encoding, fix some odds and ends in pytorch, upgrade to PyTorch 2.0 --- docs/freqai-parameter-table.md | 2 +- .../base_models/BasePyTorchClassifier.py | 1 + .../freqai/base_models/BasePyTorchModel.py | 1 + .../base_models/BasePyTorchRegressor.py | 1 + freqtrade/freqai/freqai_interface.py | 1 + .../PyTorchTransformerRegressor.py | 139 ++++++++++++++++++ .../freqai/torch/PyTorchDataConvertor.py | 14 +- freqtrade/freqai/torch/PyTorchMLPModel.py | 5 +- freqtrade/freqai/torch/PyTorchModelTrainer.py | 53 +++++-- .../freqai/torch/PyTorchTransformerModel.py | 91 ++++++++++++ freqtrade/freqai/torch/datasets.py | 19 +++ requirements-freqai-rl.txt | 2 +- tests/freqai/test_freqai_interface.py | 14 +- 13 files changed, 317 insertions(+), 26 deletions(-) create mode 100644 freqtrade/freqai/prediction_models/PyTorchTransformerRegressor.py create mode 100644 freqtrade/freqai/torch/PyTorchTransformerModel.py create mode 100644 freqtrade/freqai/torch/datasets.py diff --git a/docs/freqai-parameter-table.md b/docs/freqai-parameter-table.md index 1487b92c2..76c175304 100644 --- a/docs/freqai-parameter-table.md +++ b/docs/freqai-parameter-table.md @@ -114,5 +114,5 @@ Mandatory parameters are marked as **Required** and have to be set in one of the |------------|-------------| | | **Extraneous parameters** | `freqai.keras` | If the selected model makes use of Keras (typical for TensorFlow-based prediction models), this flag needs to be activated so that the model save/loading follows Keras standards.
**Datatype:** Boolean.
Default: `False`. -| `freqai.conv_width` | The width of a convolutional neural network input tensor. This replaces the need for shifting candles (`include_shifted_candles`) by feeding in historical data points as the second dimension of the tensor. Technically, this parameter can also be used for regressors, but it only adds computational overhead and does not change the model training/prediction.
**Datatype:** Integer.
Default: `2`. +| `freqai.conv_width` | The width of a neural network input tensor. This replaces the need for shifting candles (`include_shifted_candles`) by feeding in historical data points as the second dimension of the tensor. Technically, this parameter can also be used for regressors, but it only adds computational overhead and does not change the model training/prediction.
**Datatype:** Integer.
Default: `2`. | `freqai.reduce_df_footprint` | Recast all numeric columns to float32/int32, with the objective of reducing ram/disk usage and decreasing train/inference timing. This parameter is set in the main level of the Freqtrade configuration file (not inside FreqAI).
**Datatype:** Boolean.
Default: `False`. diff --git a/freqtrade/freqai/base_models/BasePyTorchClassifier.py b/freqtrade/freqai/base_models/BasePyTorchClassifier.py index 977152cc5..1f54e7609 100644 --- a/freqtrade/freqai/base_models/BasePyTorchClassifier.py +++ b/freqtrade/freqai/base_models/BasePyTorchClassifier.py @@ -74,6 +74,7 @@ class BasePyTorchClassifier(BasePyTorchModel): dk.data_dictionary["prediction_features"], device=self.device ) + self.model.model.eval() logits = self.model.model(x) probs = F.softmax(logits, dim=-1) predicted_classes = torch.argmax(probs, dim=-1) diff --git a/freqtrade/freqai/base_models/BasePyTorchModel.py b/freqtrade/freqai/base_models/BasePyTorchModel.py index 8177b8eb8..82042d24c 100644 --- a/freqtrade/freqai/base_models/BasePyTorchModel.py +++ b/freqtrade/freqai/base_models/BasePyTorchModel.py @@ -27,6 +27,7 @@ class BasePyTorchModel(IFreqaiModel, ABC): self.device = "cuda" if torch.cuda.is_available() else "cpu" test_size = self.freqai_info.get('data_split_parameters', {}).get('test_size') self.splits = ["train", "test"] if test_size != 0 else ["train"] + self.window_size = self.freqai_info.get("conv_width", 1) def train( self, unfiltered_df: DataFrame, pair: str, dk: FreqaiDataKitchen, **kwargs diff --git a/freqtrade/freqai/base_models/BasePyTorchRegressor.py b/freqtrade/freqai/base_models/BasePyTorchRegressor.py index ea6fabe49..d5a550f58 100644 --- a/freqtrade/freqai/base_models/BasePyTorchRegressor.py +++ b/freqtrade/freqai/base_models/BasePyTorchRegressor.py @@ -44,6 +44,7 @@ class BasePyTorchRegressor(BasePyTorchModel): dk.data_dictionary["prediction_features"], device=self.device ) + self.model.model.eval() y = self.model.model(x) y = y.cpu() pred_df = DataFrame(y.detach().numpy(), columns=[dk.label_list[0]]) diff --git a/freqtrade/freqai/freqai_interface.py b/freqtrade/freqai/freqai_interface.py index 3580963d4..6815e421c 100644 --- a/freqtrade/freqai/freqai_interface.py +++ b/freqtrade/freqai/freqai_interface.py @@ -80,6 +80,7 @@ class IFreqaiModel(ABC): if self.keras and self.ft_params.get("DI_threshold", 0): self.ft_params["DI_threshold"] = 0 logger.warning("DI threshold is not configured for Keras models yet. Deactivating.") + self.CONV_WIDTH = self.freqai_info.get('conv_width', 1) if self.ft_params.get("inlier_metric_window", 0): self.CONV_WIDTH = self.ft_params.get("inlier_metric_window", 0) * 2 diff --git a/freqtrade/freqai/prediction_models/PyTorchTransformerRegressor.py b/freqtrade/freqai/prediction_models/PyTorchTransformerRegressor.py new file mode 100644 index 000000000..e760f6e68 --- /dev/null +++ b/freqtrade/freqai/prediction_models/PyTorchTransformerRegressor.py @@ -0,0 +1,139 @@ +from typing import Any, Dict, Tuple + +import numpy as np +import numpy.typing as npt +import pandas as pd +import torch + +from freqtrade.freqai.base_models.BasePyTorchRegressor import BasePyTorchRegressor +from freqtrade.freqai.data_kitchen import FreqaiDataKitchen +from freqtrade.freqai.torch.PyTorchDataConvertor import (DefaultPyTorchDataConvertor, + PyTorchDataConvertor) +from freqtrade.freqai.torch.PyTorchModelTrainer import PyTorchTransformerTrainer +from freqtrade.freqai.torch.PyTorchTransformerModel import PyTorchTransformerModel + + +class PyTorchTransformerRegressor(BasePyTorchRegressor): + """ + This class implements the fit method of IFreqaiModel. + in the fit method we initialize the model and trainer objects. + the only requirement from the model is to be aligned to PyTorchRegressor + predict method that expects the model to predict tensor of type float. + the trainer defines the training loop. + + parameters are passed via `model_training_parameters` under the freqai + section in the config file. e.g: + { + ... + "freqai": { + ... + "model_training_parameters" : { + "learning_rate": 3e-4, + "trainer_kwargs": { + "max_iters": 5000, + "batch_size": 64, + "max_n_eval_batches": null, + "window_size": 10 + }, + "model_kwargs": { + "hidden_dim": 512, + "dropout_percent": 0.2, + "n_layer": 1, + }, + } + } + } + """ + + @property + def data_convertor(self) -> PyTorchDataConvertor: + return DefaultPyTorchDataConvertor(target_tensor_type=torch.float) + + def __init__(self, **kwargs) -> None: + super().__init__(**kwargs) + config = self.freqai_info.get("model_training_parameters", {}) + self.learning_rate: float = config.get("learning_rate", 3e-4) + self.model_kwargs: Dict[str, Any] = config.get("model_kwargs", {}) + self.trainer_kwargs: Dict[str, Any] = config.get("trainer_kwargs", {}) + + def fit(self, data_dictionary: Dict, dk: FreqaiDataKitchen, **kwargs) -> Any: + """ + User sets up the training and test data to fit their desired model here + :param data_dictionary: the dictionary holding all data for train, test, + labels, weights + :param dk: The datakitchen object for the current coin/model + """ + + n_features = data_dictionary["train_features"].shape[-1] + n_labels = data_dictionary["train_labels"].shape[-1] + model = PyTorchTransformerModel( + input_dim=n_features, + output_dim=n_labels, + time_window=self.window_size, + **self.model_kwargs + ) + model.to(self.device) + optimizer = torch.optim.AdamW(model.parameters(), lr=self.learning_rate) + criterion = torch.nn.MSELoss() + init_model = self.get_init_model(dk.pair) + trainer = PyTorchTransformerTrainer( + model=model, + optimizer=optimizer, + criterion=criterion, + device=self.device, + init_model=init_model, + data_convertor=self.data_convertor, + window_size=self.window_size, + **self.trainer_kwargs, + ) + trainer.fit(data_dictionary, self.splits) + return trainer + + def predict( + self, unfiltered_df: pd.DataFrame, dk: FreqaiDataKitchen, **kwargs + ) -> Tuple[pd.DataFrame, npt.NDArray[np.int_]]: + """ + Filter the prediction features data and predict with it. + :param unfiltered_df: Full dataframe for the current backtest period. + :return: + :pred_df: dataframe containing the predictions + :do_predict: np.array of 1s and 0s to indicate places where freqai needed to remove + data (NaNs) or felt uncertain about data (PCA and DI index) + """ + + dk.find_features(unfiltered_df) + filtered_df, _ = dk.filter_features( + unfiltered_df, dk.training_features_list, training_filter=False + ) + filtered_df = dk.normalize_data_from_metadata(filtered_df) + dk.data_dictionary["prediction_features"] = filtered_df + + self.data_cleaning_predict(dk) + x = self.data_convertor.convert_x( + dk.data_dictionary["prediction_features"], + device=self.device + ) + # if user is asking for multiple predictions, slide the window + # along the tensor + x = x.unsqueeze(0) + # create empty torch tensor + self.model.model.eval() + yb = torch.empty(0) + if x.shape[1] > 1: + ws = self.window_size + for i in range(0, x.shape[1] - ws): + xb = x[:, i:i + ws, :] + y = self.model.model(xb) + yb = torch.cat((yb, y), dim=0) + else: + yb = self.model.model(x) + + yb = yb.cpu().squeeze() + pred_df = pd.DataFrame(yb.detach().numpy(), columns=dk.label_list) + pred_df = dk.denormalize_labels_from_metadata(pred_df) + + if x.shape[1] > 1: + zeros_df = pd.DataFrame(np.zeros((x.shape[1] - len(pred_df), len(pred_df.columns))), + columns=pred_df.columns) + pred_df = pd.concat([zeros_df, pred_df], axis=0, ignore_index=True) + return (pred_df, dk.do_predict) diff --git a/freqtrade/freqai/torch/PyTorchDataConvertor.py b/freqtrade/freqai/torch/PyTorchDataConvertor.py index a31ccdc79..e6b815373 100644 --- a/freqtrade/freqai/torch/PyTorchDataConvertor.py +++ b/freqtrade/freqai/torch/PyTorchDataConvertor.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import List, Optional +from typing import Optional import pandas as pd import torch @@ -12,14 +12,14 @@ class PyTorchDataConvertor(ABC): """ @abstractmethod - def convert_x(self, df: pd.DataFrame, device: Optional[str] = None) -> List[torch.Tensor]: + def convert_x(self, df: pd.DataFrame, device: Optional[str] = None) -> torch.Tensor: """ :param df: "*_features" dataframe. :param device: The device to use for training (e.g. 'cpu', 'cuda'). """ @abstractmethod - def convert_y(self, df: pd.DataFrame, device: Optional[str] = None) -> List[torch.Tensor]: + def convert_y(self, df: pd.DataFrame, device: Optional[str] = None) -> torch.Tensor: """ :param df: "*_labels" dataframe. :param device: The device to use for training (e.g. 'cpu', 'cuda'). @@ -45,14 +45,14 @@ class DefaultPyTorchDataConvertor(PyTorchDataConvertor): self._target_tensor_type = target_tensor_type self._squeeze_target_tensor = squeeze_target_tensor - def convert_x(self, df: pd.DataFrame, device: Optional[str] = None) -> List[torch.Tensor]: + def convert_x(self, df: pd.DataFrame, device: Optional[str] = None) -> torch.Tensor: x = torch.from_numpy(df.values).float() if device: x = x.to(device) - return [x] + return x - def convert_y(self, df: pd.DataFrame, device: Optional[str] = None) -> List[torch.Tensor]: + def convert_y(self, df: pd.DataFrame, device: Optional[str] = None) -> torch.Tensor: y = torch.from_numpy(df.values) if self._target_tensor_type: @@ -64,4 +64,4 @@ class DefaultPyTorchDataConvertor(PyTorchDataConvertor): if device: y = y.to(device) - return [y] + return y diff --git a/freqtrade/freqai/torch/PyTorchMLPModel.py b/freqtrade/freqai/torch/PyTorchMLPModel.py index 62d3216df..0093388f8 100644 --- a/freqtrade/freqai/torch/PyTorchMLPModel.py +++ b/freqtrade/freqai/torch/PyTorchMLPModel.py @@ -1,5 +1,4 @@ import logging -from typing import List import torch from torch import nn @@ -47,8 +46,8 @@ class PyTorchMLPModel(nn.Module): self.relu = nn.ReLU() self.dropout = nn.Dropout(p=dropout_percent) - def forward(self, tensors: List[torch.Tensor]) -> torch.Tensor: - x: torch.Tensor = tensors[0] + def forward(self, x: torch.Tensor) -> torch.Tensor: + # x: torch.Tensor = tensors[0] x = self.relu(self.input_layer(x)) x = self.dropout(x) x = self.blocks(x) diff --git a/freqtrade/freqai/torch/PyTorchModelTrainer.py b/freqtrade/freqai/torch/PyTorchModelTrainer.py index 8277ba937..a3b0d9b9c 100644 --- a/freqtrade/freqai/torch/PyTorchModelTrainer.py +++ b/freqtrade/freqai/torch/PyTorchModelTrainer.py @@ -12,6 +12,8 @@ from torch.utils.data import DataLoader, TensorDataset from freqtrade.freqai.torch.PyTorchDataConvertor import PyTorchDataConvertor from freqtrade.freqai.torch.PyTorchTrainerInterface import PyTorchTrainerInterface +from .datasets import WindowDataset + logger = logging.getLogger(__name__) @@ -26,6 +28,7 @@ class PyTorchModelTrainer(PyTorchTrainerInterface): init_model: Dict, data_convertor: PyTorchDataConvertor, model_meta_data: Dict[str, Any] = {}, + window_size: int = 1, **kwargs ): """ @@ -52,6 +55,7 @@ class PyTorchModelTrainer(PyTorchTrainerInterface): self.batch_size: int = kwargs.get("batch_size", 64) self.max_n_eval_batches: Optional[int] = kwargs.get("max_n_eval_batches", None) self.data_convertor = data_convertor + self.window_size: int = window_size if init_model: self.load_from_checkpoint(init_model) @@ -75,16 +79,15 @@ class PyTorchModelTrainer(PyTorchTrainerInterface): batch_size=self.batch_size, n_iters=self.max_iters ) + self.model.train() for epoch in range(1, epochs + 1): # training losses = [] for i, batch_data in enumerate(data_loaders_dictionary["train"]): - for tensor in batch_data: - tensor.to(self.device) - - xb = batch_data[:-1] - yb = batch_data[-1] + xb, yb = batch_data + xb.to(self.device) + yb.to(self.device) yb_pred = self.model(xb) loss = self.criterion(yb_pred, yb) @@ -120,12 +123,10 @@ class PyTorchModelTrainer(PyTorchTrainerInterface): if max_n_eval_batches and i > max_n_eval_batches: n_batches += 1 break + xb, yb = batch_data + xb.to(self.device) + yb.to(self.device) - for tensor in batch_data: - tensor.to(self.device) - - xb = batch_data[:-1] - yb = batch_data[-1] yb_pred = self.model(xb) loss = self.criterion(yb_pred, yb) losses.append(loss.item()) @@ -145,7 +146,7 @@ class PyTorchModelTrainer(PyTorchTrainerInterface): for split in splits: x = self.data_convertor.convert_x(data_dictionary[f"{split}_features"], self.device) y = self.data_convertor.convert_y(data_dictionary[f"{split}_labels"], self.device) - dataset = TensorDataset(*x, *y) + dataset = TensorDataset(x, y) data_loader = DataLoader( dataset, batch_size=self.batch_size, @@ -206,3 +207,33 @@ class PyTorchModelTrainer(PyTorchTrainerInterface): self.optimizer.load_state_dict(checkpoint["optimizer_state_dict"]) self.model_meta_data = checkpoint["model_meta_data"] return self + + +class PyTorchTransformerTrainer(PyTorchModelTrainer): + """ + Creating a trainer for the Transformer model. + """ + + def create_data_loaders_dictionary( + self, + data_dictionary: Dict[str, pd.DataFrame], + splits: List[str] + ) -> Dict[str, DataLoader]: + """ + Converts the input data to PyTorch tensors using a data loader. + """ + data_loader_dictionary = {} + for split in splits: + x = self.data_convertor.convert_x(data_dictionary[f"{split}_features"], self.device) + y = self.data_convertor.convert_y(data_dictionary[f"{split}_labels"], self.device) + dataset = WindowDataset(x, y, self.window_size) + data_loader = DataLoader( + dataset, + batch_size=self.batch_size, + shuffle=False, + drop_last=True, + num_workers=0, + ) + data_loader_dictionary[split] = data_loader + + return data_loader_dictionary diff --git a/freqtrade/freqai/torch/PyTorchTransformerModel.py b/freqtrade/freqai/torch/PyTorchTransformerModel.py new file mode 100644 index 000000000..0a252112a --- /dev/null +++ b/freqtrade/freqai/torch/PyTorchTransformerModel.py @@ -0,0 +1,91 @@ +import math + +import torch +import torch.nn as nn + + +""" +The architecture is based on the paper “Attention Is All You Need”. +Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N Gomez, +Lukasz Kaiser, and Illia Polosukhin. 2017. +""" + + +class PyTorchTransformerModel(nn.Module): + """ + A transformer approach to time series modeling using positional encoding. + The architecture is based on the paper “Attention Is All You Need”. + Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N Gomez, + Lukasz Kaiser, and Illia Polosukhin. 2017. + """ + + def __init__(self, input_dim: int = 7, output_dim: int = 7, hidden_dim=1024, + n_layer=2, dropout_percent=0.1, time_window=10): + super().__init__() + self.time_window = time_window + self.input_net = nn.Sequential( + nn.Dropout(dropout_percent), nn.Linear(input_dim, hidden_dim) + ) + + # Encode the timeseries with Positional encoding + self.positional_encoding = PositionalEncoding(d_model=hidden_dim, max_len=hidden_dim) + + # Define the encoder block of the Transformer + self.encoder_layer = nn.TransformerEncoderLayer( + d_model=hidden_dim, nhead=8, dropout=dropout_percent, batch_first=True) + self.transformer = nn.TransformerEncoder(self.encoder_layer, num_layers=n_layer) + + # Pseudo decoder + self.output_net = nn.Sequential( + nn.Linear(hidden_dim, hidden_dim), + nn.LayerNorm(hidden_dim), + nn.Tanh(), + nn.Dropout(dropout_percent), + ) + + self.output_layer = nn.Sequential( + nn.Linear(hidden_dim * time_window, output_dim), + nn.Tanh() + ) + + def forward(self, x, mask=None, add_positional_encoding=True): + """ + Args: + x: Input features of shape [Batch, SeqLen, input_dim] + mask: Mask to apply on the attention outputs (optional) + add_positional_encoding: If True, we add the positional encoding to the input. + Might not be desired for some tasks. + """ + x = self.input_net(x) + if add_positional_encoding: + x = self.positional_encoding(x) + x = self.transformer(x, mask=mask) + x = self.output_net(x) + x = x.reshape(-1, 1, self.time_window * x.shape[-1]) + x = self.output_layer(x) + return x + + +class PositionalEncoding(torch.nn.Module): + def __init__(self, d_model, max_len=5000): + """ + Args + d_model: Hidden dimensionality of the input. + max_len: Maximum length of a sequence to expect. + """ + super().__init__() + + # Create matrix of [SeqLen, HiddenDim] representing the positional encoding + # for max_len inputs + pe = torch.zeros(max_len, d_model) + position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1) + div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model)) + pe[:, 0::2] = torch.sin(position * div_term) + pe[:, 1::2] = torch.cos(position * div_term) + pe = pe.unsqueeze(0) + + self.register_buffer("pe", pe, persistent=False) + + def forward(self, x): + x = x + self.pe[:, : x.size(1)] + return x diff --git a/freqtrade/freqai/torch/datasets.py b/freqtrade/freqai/torch/datasets.py new file mode 100644 index 000000000..120d8a116 --- /dev/null +++ b/freqtrade/freqai/torch/datasets.py @@ -0,0 +1,19 @@ +import torch + + +class WindowDataset(torch.utils.data.Dataset): + def __init__(self, xs, ys, window_size): + self.xs = xs + self.ys = ys + self.window_size = window_size + + def __len__(self): + return len(self.xs) - self.window_size + + def __getitem__(self, index): + idx_rev = len(self.xs) - self.window_size - index - 1 + window_x = self.xs[idx_rev:idx_rev + self.window_size, :] + # Beware of indexing, these two window_x and window_y are aimed at the same row! + # this is what happens when you use : + window_y = self.ys[idx_rev + self.window_size - 1, :].unsqueeze(0) + return window_x, window_y diff --git a/requirements-freqai-rl.txt b/requirements-freqai-rl.txt index 45ccc40cc..525c25229 100644 --- a/requirements-freqai-rl.txt +++ b/requirements-freqai-rl.txt @@ -2,7 +2,7 @@ -r requirements-freqai.txt # Required for freqai-rl -torch==1.13.1; python_version < '3.11' +torch==2.0.0; python_version < '3.11' #until these branches will be released we can use this gymnasium==0.28.1 stable_baselines3==2.0.0a5 diff --git a/tests/freqai/test_freqai_interface.py b/tests/freqai/test_freqai_interface.py index 7346191db..ed0910089 100644 --- a/tests/freqai/test_freqai_interface.py +++ b/tests/freqai/test_freqai_interface.py @@ -50,7 +50,8 @@ def can_run_model(model: str) -> None: ('XGBoostRegressor', False, True, False, True, False, 10), ('XGBoostRFRegressor', False, False, False, True, False, 0), ('CatboostRegressor', False, False, False, True, True, 0), - ('PyTorchMLPRegressor', False, False, False, True, False, 0), + ('PyTorchMLPRegressor', False, False, False, False, False, 0), + ('PyTorchTransformerRegressor', False, False, False, False, False, 0), ('ReinforcementLearner', False, True, False, True, False, 0), ('ReinforcementLearner_multiproc', False, False, False, True, False, 0), ('ReinforcementLearner_test_3ac', False, False, False, False, False, 0), @@ -82,10 +83,13 @@ def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca, freqai_conf["freqaimodel_path"] = str(Path(__file__).parents[1] / "freqai" / "test_models") freqai_conf["freqai"]["rl_config"]["drop_ohlc_from_features"] = True - if 'PyTorchMLPRegressor' in model: + if 'PyTorch' in model: model_save_ext = 'zip' pytorch_mlp_mtp = mock_pytorch_mlp_model_training_parameters() freqai_conf['freqai']['model_training_parameters'].update(pytorch_mlp_mtp) + if 'Transformer' in model: + # transformer model takes a window, unlike the MLP regressor + freqai_conf.update({"conv_width": 10}) strategy = get_patched_freqai_strategy(mocker, freqai_conf) exchange = get_patched_exchange(mocker, freqai_conf) @@ -228,6 +232,7 @@ def test_extract_data_and_train_model_Classifiers(mocker, freqai_conf, model): ("XGBoostRegressor", 2, "freqai_test_strat"), ("CatboostRegressor", 2, "freqai_test_strat"), ("PyTorchMLPRegressor", 2, "freqai_test_strat"), + ("PyTorchTransformerRegressor", 2, "freqai_test_strat"), ("ReinforcementLearner", 3, "freqai_rl_test_strat"), ("XGBoostClassifier", 2, "freqai_test_classifier"), ("LightGBMClassifier", 2, "freqai_test_classifier"), @@ -253,9 +258,12 @@ def test_start_backtesting(mocker, freqai_conf, model, num_files, strat, caplog) if 'test_4ac' in model: freqai_conf["freqaimodel_path"] = str(Path(__file__).parents[1] / "freqai" / "test_models") - if 'PyTorchMLP' in model: + if 'PyTorch' in model: pytorch_mlp_mtp = mock_pytorch_mlp_model_training_parameters() freqai_conf['freqai']['model_training_parameters'].update(pytorch_mlp_mtp) + if 'Transformer' in model: + # transformer model takes a window, unlike the MLP regressor + freqai_conf.update({"conv_width": 10}) freqai_conf.get("freqai", {}).get("feature_parameters", {}).update( {"indicator_periods_candles": [2]}) From c2beeb4c790614f1face9fa08709d7d1de9046bb Mon Sep 17 00:00:00 2001 From: robcaulk Date: Sat, 6 May 2023 15:53:58 +0000 Subject: [PATCH 02/21] bug fix backtest feature validation --- freqtrade/freqai/freqai_interface.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqai/freqai_interface.py b/freqtrade/freqai/freqai_interface.py index 3580963d4..cf4123a5a 100644 --- a/freqtrade/freqai/freqai_interface.py +++ b/freqtrade/freqai/freqai_interface.py @@ -306,10 +306,11 @@ class IFreqaiModel(ABC): if dk.check_if_backtest_prediction_is_valid(len_backtest_df): if check_features: self.dd.load_metadata(dk) - dataframe_dummy_features = self.dk.use_strategy_to_populate_indicators( + df_fts = self.dk.use_strategy_to_populate_indicators( strategy, prediction_dataframe=dataframe.tail(1), pair=pair ) - dk.find_features(dataframe_dummy_features) + df_fts = dk.remove_special_chars_from_feature_names(df_fts) + dk.find_features(df_fts) self.check_if_feature_list_matches_strategy(dk) check_features = False append_df = dk.get_backtesting_prediction() From 3bbb7e38ead75996f5ba1bf23098067f57ed313b Mon Sep 17 00:00:00 2001 From: robcaulk Date: Sat, 6 May 2023 16:12:10 +0000 Subject: [PATCH 03/21] improve transformer architecture, remove 3.10 install constraint, add documentation for torch.compile() --- docs/freqai-configuration.md | 18 +++++++++++ .../freqai/torch/PyTorchTransformerModel.py | 32 ++++++++++--------- requirements-freqai-rl.txt | 2 +- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/docs/freqai-configuration.md b/docs/freqai-configuration.md index e7aca20be..ad7cafd3d 100644 --- a/docs/freqai-configuration.md +++ b/docs/freqai-configuration.md @@ -395,3 +395,21 @@ Here we create a `PyTorchMLPRegressor` class that implements the `fit` method. T return dataframe ``` To see a full example, you can refer to the [classifier test strategy class](https://github.com/freqtrade/freqtrade/blob/develop/tests/strategy/strats/freqai_test_classifier.py). + + +#### Improving performance with `torch.compile()` + +Torch provides a `torch.compile()` method that can be used to improve performance for specific GPU hardware. More details can be found [here](https://pytorch.org/tutorials/intermediate/torch_compile_tutorial.html). In brief, you simply wrap your `model` in `torch.compile()`: + + +```python + model = PyTorchMLPModel( + input_dim=n_features, + output_dim=1, + **self.model_kwargs + ) + model.to(self.device) + model = torch.compile(model) +``` + +Then proceed to use the model as normal. Keep in mind that doing this will remove eager execution, which means errors and tracebacks will not be informative. diff --git a/freqtrade/freqai/torch/PyTorchTransformerModel.py b/freqtrade/freqai/torch/PyTorchTransformerModel.py index 0a252112a..2ab3ea434 100644 --- a/freqtrade/freqai/torch/PyTorchTransformerModel.py +++ b/freqtrade/freqai/torch/PyTorchTransformerModel.py @@ -20,32 +20,35 @@ class PyTorchTransformerModel(nn.Module): """ def __init__(self, input_dim: int = 7, output_dim: int = 7, hidden_dim=1024, - n_layer=2, dropout_percent=0.1, time_window=10): + n_layer=2, dropout_percent=0.1, time_window=10, nhead=8): super().__init__() self.time_window = time_window + # ensure the input dimension to the transformer is divisible by nhead + self.dim_val = input_dim - (input_dim % nhead) self.input_net = nn.Sequential( - nn.Dropout(dropout_percent), nn.Linear(input_dim, hidden_dim) + nn.Dropout(dropout_percent), nn.Linear(input_dim, self.dim_val) ) # Encode the timeseries with Positional encoding - self.positional_encoding = PositionalEncoding(d_model=hidden_dim, max_len=hidden_dim) + self.positional_encoding = PositionalEncoding(d_model=self.dim_val, max_len=self.dim_val) # Define the encoder block of the Transformer self.encoder_layer = nn.TransformerEncoderLayer( - d_model=hidden_dim, nhead=8, dropout=dropout_percent, batch_first=True) + d_model=self.dim_val, nhead=nhead, dropout=dropout_percent, batch_first=True) self.transformer = nn.TransformerEncoder(self.encoder_layer, num_layers=n_layer) - # Pseudo decoder + # the pseudo decoding FC self.output_net = nn.Sequential( - nn.Linear(hidden_dim, hidden_dim), - nn.LayerNorm(hidden_dim), - nn.Tanh(), + nn.Linear(hidden_dim * time_window, int(hidden_dim)), + nn.ReLU(), nn.Dropout(dropout_percent), - ) - - self.output_layer = nn.Sequential( - nn.Linear(hidden_dim * time_window, output_dim), - nn.Tanh() + nn.Linear(int(hidden_dim), int(hidden_dim / 2)), + nn.ReLU(), + nn.Dropout(dropout_percent), + nn.Linear(int(hidden_dim / 2), int(hidden_dim / 4)), + nn.ReLU(), + nn.Dropout(dropout_percent), + nn.Linear(int(hidden_dim / 4), output_dim) ) def forward(self, x, mask=None, add_positional_encoding=True): @@ -60,9 +63,8 @@ class PyTorchTransformerModel(nn.Module): if add_positional_encoding: x = self.positional_encoding(x) x = self.transformer(x, mask=mask) - x = self.output_net(x) x = x.reshape(-1, 1, self.time_window * x.shape[-1]) - x = self.output_layer(x) + x = self.output_net(x) return x diff --git a/requirements-freqai-rl.txt b/requirements-freqai-rl.txt index 525c25229..6b9c1c298 100644 --- a/requirements-freqai-rl.txt +++ b/requirements-freqai-rl.txt @@ -2,7 +2,7 @@ -r requirements-freqai.txt # Required for freqai-rl -torch==2.0.0; python_version < '3.11' +torch==2.0.0 #until these branches will be released we can use this gymnasium==0.28.1 stable_baselines3==2.0.0a5 From 36e1e58dad0c63a41cf97792cea248942d590ca4 Mon Sep 17 00:00:00 2001 From: robcaulk Date: Sat, 6 May 2023 17:40:04 +0000 Subject: [PATCH 04/21] fix arch --- freqtrade/freqai/torch/PyTorchTransformerModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqai/torch/PyTorchTransformerModel.py b/freqtrade/freqai/torch/PyTorchTransformerModel.py index 2ab3ea434..702a7a08b 100644 --- a/freqtrade/freqai/torch/PyTorchTransformerModel.py +++ b/freqtrade/freqai/torch/PyTorchTransformerModel.py @@ -39,7 +39,7 @@ class PyTorchTransformerModel(nn.Module): # the pseudo decoding FC self.output_net = nn.Sequential( - nn.Linear(hidden_dim * time_window, int(hidden_dim)), + nn.Linear(self.dim_val * time_window, int(hidden_dim)), nn.ReLU(), nn.Dropout(dropout_percent), nn.Linear(int(hidden_dim), int(hidden_dim / 2)), From 4b1cb964464e2bb9870de659658f785d9347eb3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 03:56:59 +0000 Subject: [PATCH 05/21] Bump orjson from 3.8.11 to 3.8.12 Bumps [orjson](https://github.com/ijl/orjson) from 3.8.11 to 3.8.12. - [Release notes](https://github.com/ijl/orjson/releases) - [Changelog](https://github.com/ijl/orjson/blob/master/CHANGELOG.md) - [Commits](https://github.com/ijl/orjson/compare/3.8.11...3.8.12) --- updated-dependencies: - dependency-name: orjson dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e3e8a5938..07722c7c7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -31,7 +31,7 @@ py_find_1st==1.1.5 # Load ticker files 30% faster python-rapidjson==1.10 # Properly format api responses -orjson==3.8.11 +orjson==3.8.12 # Notify systemd sdnotify==0.3.2 From fa40e4e888cbdd026543355c8828041e1c2f5be5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 03:57:11 +0000 Subject: [PATCH 06/21] Bump websockets from 11.0.2 to 11.0.3 Bumps [websockets](https://github.com/aaugustin/websockets) from 11.0.2 to 11.0.3. - [Release notes](https://github.com/aaugustin/websockets/releases) - [Commits](https://github.com/aaugustin/websockets/compare/11.0.2...11.0.3) --- updated-dependencies: - dependency-name: websockets dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e3e8a5938..e9d33c394 100644 --- a/requirements.txt +++ b/requirements.txt @@ -56,7 +56,7 @@ python-dateutil==2.8.2 schedule==1.2.0 #WS Messages -websockets==11.0.2 +websockets==11.0.3 janus==1.0.0 ast-comments==1.0.1 From 3bb872a5e7a2a601745242c32a2c36a45858d3be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 03:57:22 +0000 Subject: [PATCH 07/21] Bump mkdocs from 1.4.2 to 1.4.3 Bumps [mkdocs](https://github.com/mkdocs/mkdocs) from 1.4.2 to 1.4.3. - [Release notes](https://github.com/mkdocs/mkdocs/releases) - [Commits](https://github.com/mkdocs/mkdocs/compare/1.4.2...1.4.3) --- updated-dependencies: - dependency-name: mkdocs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 40f7d9b1c..89ed13549 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,5 +1,5 @@ markdown==3.3.7 -mkdocs==1.4.2 +mkdocs==1.4.3 mkdocs-material==9.1.8 mdx_truly_sane_lists==1.3 pymdown-extensions==9.11 From 4a911bbe90a481203af1bb3cfcdcb85b54c52127 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 03:57:44 +0000 Subject: [PATCH 08/21] Bump pypa/gh-action-pypi-publish from 1.8.5 to 1.8.6 Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.8.5 to 1.8.6. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.8.5...v1.8.6) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 52c772bd3..8e7b11672 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -425,7 +425,7 @@ jobs: python setup.py sdist bdist_wheel - name: Publish to PyPI (Test) - uses: pypa/gh-action-pypi-publish@v1.8.5 + uses: pypa/gh-action-pypi-publish@v1.8.6 if: (github.event_name == 'release') with: user: __token__ @@ -433,7 +433,7 @@ jobs: repository_url: https://test.pypi.org/legacy/ - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@v1.8.5 + uses: pypa/gh-action-pypi-publish@v1.8.6 if: (github.event_name == 'release') with: user: __token__ From ecd34ce470afbec09fd18236d74c94c13ac68efd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 03:57:54 +0000 Subject: [PATCH 09/21] Bump requests from 2.29.0 to 2.30.0 Bumps [requests](https://github.com/psf/requests) from 2.29.0 to 2.30.0. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.29.0...v2.30.0) --- updated-dependencies: - dependency-name: requests dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e3e8a5938..685b0a44c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ python-telegram-bot==20.2 httpx>=0.23.3 arrow==1.2.3 cachetools==4.2.2 -requests==2.29.0 +requests==2.30.0 urllib3==1.26.15 jsonschema==4.17.3 TA-Lib==0.4.26 From 68a37cb71b0f81a02bbeafdf461d7acac5838754 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 03:58:05 +0000 Subject: [PATCH 10/21] Bump tensorboard from 2.12.2 to 2.13.0 Bumps [tensorboard](https://github.com/tensorflow/tensorboard) from 2.12.2 to 2.13.0. - [Release notes](https://github.com/tensorflow/tensorboard/releases) - [Changelog](https://github.com/tensorflow/tensorboard/blob/master/RELEASE.md) - [Commits](https://github.com/tensorflow/tensorboard/compare/2.12.2...2.13.0) --- updated-dependencies: - dependency-name: tensorboard dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-freqai.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-freqai.txt b/requirements-freqai.txt index 51396ab91..e5bc23d56 100644 --- a/requirements-freqai.txt +++ b/requirements-freqai.txt @@ -8,4 +8,4 @@ joblib==1.2.0 catboost==1.1.1; platform_machine != 'aarch64' and 'arm' not in platform_machine and python_version < '3.11' lightgbm==3.3.5 xgboost==1.7.5 -tensorboard==2.12.2 +tensorboard==2.13.0 From 7bd33be8f7afad92a55d6152eaca1af306f506b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 03:58:16 +0000 Subject: [PATCH 11/21] Bump python-telegram-bot from 20.2 to 20.3 Bumps [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) from 20.2 to 20.3. - [Release notes](https://github.com/python-telegram-bot/python-telegram-bot/releases) - [Changelog](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/CHANGES.rst) - [Commits](https://github.com/python-telegram-bot/python-telegram-bot/compare/v20.2...v20.3) --- updated-dependencies: - dependency-name: python-telegram-bot dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e3e8a5938..b52ed5fa7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ ccxt==3.0.85 cryptography==40.0.2 aiohttp==3.8.4 SQLAlchemy==2.0.12 -python-telegram-bot==20.2 +python-telegram-bot==20.3 # can't be hard-pinned due to telegram-bot pinning httpx with ~ httpx>=0.23.3 arrow==1.2.3 From bf7c52a9eee32e55795033a6fb4b86175e10a28a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 03:58:37 +0000 Subject: [PATCH 12/21] Bump types-requests from 2.29.0.0 to 2.30.0.0 Bumps [types-requests](https://github.com/python/typeshed) from 2.29.0.0 to 2.30.0.0. - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-requests dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 5cc0e78ce..253942e0e 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -25,6 +25,6 @@ nbconvert==7.3.1 # mypy types types-cachetools==5.3.0.5 types-filelock==3.2.7 -types-requests==2.29.0.0 +types-requests==2.30.0.0 types-tabulate==0.9.0.2 types-python-dateutil==2.8.19.12 From 75e5f325a9a34087107949f613b42f9625b8e83d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 03:59:08 +0000 Subject: [PATCH 13/21] Bump ruff from 0.0.263 to 0.0.265 Bumps [ruff](https://github.com/charliermarsh/ruff) from 0.0.263 to 0.0.265. - [Release notes](https://github.com/charliermarsh/ruff/releases) - [Changelog](https://github.com/charliermarsh/ruff/blob/main/BREAKING_CHANGES.md) - [Commits](https://github.com/charliermarsh/ruff/compare/v0.0.263...v0.0.265) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 5cc0e78ce..e3c4740e8 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,7 +7,7 @@ -r docs/requirements-docs.txt coveralls==3.3.1 -ruff==0.0.263 +ruff==0.0.265 mypy==1.2.0 pre-commit==3.2.2 pytest==7.3.1 From 10604bf49c9be5637c5a70076bb140989423144e Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 8 May 2023 06:46:30 +0200 Subject: [PATCH 14/21] Run Torch tests on 3.11 --- tests/freqai/test_freqai_interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/freqai/test_freqai_interface.py b/tests/freqai/test_freqai_interface.py index ed0910089..95414a83f 100644 --- a/tests/freqai/test_freqai_interface.py +++ b/tests/freqai/test_freqai_interface.py @@ -41,7 +41,7 @@ def can_run_model(model: str) -> None: if is_pytorch_model and is_mac() and not is_arm(): pytest.skip("Reinforcement learning / PyTorch module not available on intel based Mac OS.") - if is_pytorch_model and is_py11(): + if is_pytorch_model: pytest.skip("Reinforcement learning / PyTorch currently not available on python 3.11.") From e0c63e12e4ac117638377467d82867b5244add09 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 05:15:49 +0000 Subject: [PATCH 15/21] Bump mkdocs-material from 9.1.8 to 9.1.9 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.1.8 to 9.1.9. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.1.8...9.1.9) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 89ed13549..7aea682ee 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,6 +1,6 @@ markdown==3.3.7 mkdocs==1.4.3 -mkdocs-material==9.1.8 +mkdocs-material==9.1.9 mdx_truly_sane_lists==1.3 pymdown-extensions==9.11 jinja2==3.1.2 From 39522322143c89a301336f49a12fa9bdacebeb02 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 8 May 2023 08:59:15 +0200 Subject: [PATCH 16/21] Bump pre-commit requests types --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 13216e495..6c3b99de8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: additional_dependencies: - types-cachetools==5.3.0.5 - types-filelock==3.2.7 - - types-requests==2.29.0.0 + - types-requests==2.30.0.0 - types-tabulate==0.9.0.2 - types-python-dateutil==2.8.19.12 - SQLAlchemy==2.0.12 From 9d7c90e9da193618b77b52cdca785f11ef8e41a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 08:11:43 +0000 Subject: [PATCH 17/21] Bump pre-commit from 3.2.2 to 3.3.1 Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 3.2.2 to 3.3.1. - [Release notes](https://github.com/pre-commit/pre-commit/releases) - [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md) - [Commits](https://github.com/pre-commit/pre-commit/compare/v3.2.2...v3.3.1) --- updated-dependencies: - dependency-name: pre-commit dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index e3c4740e8..29b610868 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -9,7 +9,7 @@ coveralls==3.3.1 ruff==0.0.265 mypy==1.2.0 -pre-commit==3.2.2 +pre-commit==3.3.1 pytest==7.3.1 pytest-asyncio==0.21.0 pytest-cov==4.0.0 From 591a51e4bce93240e8b9b08a840c7dfa18b56561 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 16:16:45 +0000 Subject: [PATCH 18/21] Bump nbconvert from 7.3.1 to 7.4.0 Bumps [nbconvert](https://github.com/jupyter/nbconvert) from 7.3.1 to 7.4.0. - [Release notes](https://github.com/jupyter/nbconvert/releases) - [Changelog](https://github.com/jupyter/nbconvert/blob/main/CHANGELOG.md) - [Commits](https://github.com/jupyter/nbconvert/compare/v7.3.1...v7.4.0) --- updated-dependencies: - dependency-name: nbconvert dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index ac53306a3..95e7d5bf9 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -20,7 +20,7 @@ isort==5.12.0 time-machine==2.9.0 # Convert jupyter notebooks to markdown documents -nbconvert==7.3.1 +nbconvert==7.4.0 # mypy types types-cachetools==5.3.0.5 From 7e31cc4100ba58a6f3301f2dc295b250fc5d51d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 16:17:04 +0000 Subject: [PATCH 19/21] Bump mkdocs-material from 9.1.9 to 9.1.10 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.1.9 to 9.1.10. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.1.9...9.1.10) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 7aea682ee..de2050335 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,6 +1,6 @@ markdown==3.3.7 mkdocs==1.4.3 -mkdocs-material==9.1.9 +mkdocs-material==9.1.10 mdx_truly_sane_lists==1.3 pymdown-extensions==9.11 jinja2==3.1.2 From 3e3945390597533f903a06dd5ffbfd753680845e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 16:17:19 +0000 Subject: [PATCH 20/21] Bump urllib3 from 1.26.15 to 2.0.2 Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.15 to 2.0.2. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/1.26.15...2.0.2) --- updated-dependencies: - dependency-name: urllib3 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 19b4d0082..844ecb41b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ httpx>=0.23.3 arrow==1.2.3 cachetools==4.2.2 requests==2.30.0 -urllib3==1.26.15 +urllib3==2.0.2 jsonschema==4.17.3 TA-Lib==0.4.26 technical==1.4.0 From f2a65437a687dd9595607cf9b2387ed1fb4822c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 16:18:57 +0000 Subject: [PATCH 21/21] Bump ccxt from 3.0.85 to 3.0.97 Bumps [ccxt](https://github.com/ccxt/ccxt) from 3.0.85 to 3.0.97. - [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md) - [Commits](https://github.com/ccxt/ccxt/compare/3.0.85...3.0.97) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 19b4d0082..4ac9fe017 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ numpy==1.24.3 pandas==2.0.1 pandas-ta==0.3.14b -ccxt==3.0.85 +ccxt==3.0.97 cryptography==40.0.2 aiohttp==3.8.4 SQLAlchemy==2.0.12