diff --git a/docs/freqai-parameter-table.md b/docs/freqai-parameter-table.md
index ef1a23401..cc92c2457 100644
--- a/docs/freqai-parameter-table.md
+++ b/docs/freqai-parameter-table.md
@@ -21,6 +21,7 @@ Mandatory parameters are marked as **Required** and have to be set in one of the
| `continual_learning` | Use the final state of the most recently trained model as starting point for the new model, allowing for incremental learning (more information can be found [here](freqai-running.md#continual-learning)). Beware that this is currently a naive approach to incremental learning, and it has a high probability of overfitting/getting stuck in local minima while the market moves away from your model. We have the connections here primarily for experimental purposes and so that it is ready for more mature approaches to continual learning in chaotic systems like the crypto market.
**Datatype:** Boolean.
Default: `False`.
| `write_metrics_to_disk` | Collect train timings, inference timings and cpu usage in json file.
**Datatype:** Boolean.
Default: `False`
| `data_kitchen_thread_count` |
Designate the number of threads you want to use for data processing (outlier methods, normalization, etc.). This has no impact on the number of threads used for training. If user does not set it (default), FreqAI will use max number of threads - 2 (leaving 1 physical core available for Freqtrade bot and FreqUI)
**Datatype:** Positive integer.
+| `activate_tensorboard` |
Indicate whether or not to activate tensorboard for the tensorboard enabled modules (currently Reinforcment Learning, XGBoost, Catboost, and PyTorch). Tensorboard needs Torch installed, which means you will need the torch/RL docker image or you need to answer "yes" to the install question about whether or not you wish to install Torch.
**Datatype:** Boolean.
Default: `True`.
### Feature parameters
diff --git a/docs/freqai-running.md b/docs/freqai-running.md
index cf1856041..55f302d40 100644
--- a/docs/freqai-running.md
+++ b/docs/freqai-running.md
@@ -168,7 +168,7 @@ This specific hyperopt would help you understand the appropriate `DI_values` for
Tensorboard logging requires the FreqAI torch installation/docker image.
-The easiest way to use tensorboard is to open a separate shell and run:
+The easiest way to use tensorboard is to ensure `freqai.activate_tensorboard` is set to `True` (default setting) in your configuration file, run FreqAI, then open a separate shell and run:
```bash
cd freqtrade
@@ -178,3 +178,7 @@ tensorboard --logdir user_data/models/unique-id
where `unique-id` is the `identifier` set in the `freqai` configuration file. This command must be run in a separate shell if you wish to view the output in your browser at 127.0.0.1:6060 (6060 is the default port used by Tensorboard).

+
+
+!!! note "Deactivate for improved performance"
+ Tensorboard logging can slow down training and should be deactivated for production use.
diff --git a/freqtrade/freqai/RL/BaseReinforcementLearningModel.py b/freqtrade/freqai/RL/BaseReinforcementLearningModel.py
index 8ee3c7c56..4d540ee36 100644
--- a/freqtrade/freqai/RL/BaseReinforcementLearningModel.py
+++ b/freqtrade/freqai/RL/BaseReinforcementLearningModel.py
@@ -68,8 +68,11 @@ class BaseReinforcementLearningModel(IFreqaiModel):
self.unset_outlier_removal()
self.net_arch = self.rl_config.get('net_arch', [128, 128])
self.dd.model_type = import_str
- self.tensorboard_callback: TensorboardCallback = \
- TensorboardCallback(verbose=1, actions=BaseActions)
+ if self.activate_tensorboard:
+ self.tensorboard_callback: TensorboardCallback = \
+ TensorboardCallback(verbose=1, actions=BaseActions)
+ else:
+ self.tenorboard_callback = None
def unset_outlier_removal(self):
"""
@@ -156,7 +159,10 @@ class BaseReinforcementLearningModel(IFreqaiModel):
best_model_save_path=str(dk.data_path))
actions = self.train_env.get_actions()
- self.tensorboard_callback = TensorboardCallback(verbose=1, actions=actions)
+ if self.activate_tensorboard:
+ self.tensorboard_callback = TensorboardCallback(verbose=1, actions=actions)
+ else:
+ self.tensorboard_callback = None # type: ignore
def pack_env_dict(self, pair: str) -> Dict[str, Any]:
"""
diff --git a/freqtrade/freqai/freqai_interface.py b/freqtrade/freqai/freqai_interface.py
index ae16d3b99..9cfda05ee 100644
--- a/freqtrade/freqai/freqai_interface.py
+++ b/freqtrade/freqai/freqai_interface.py
@@ -21,8 +21,7 @@ from freqtrade.exceptions import OperationalException
from freqtrade.exchange import timeframe_to_seconds
from freqtrade.freqai.data_drawer import FreqaiDataDrawer
from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
-from freqtrade.freqai.tensorboard import TBLogger
-from freqtrade.freqai.utils import plot_feature_importance, record_params
+from freqtrade.freqai.utils import get_tb_logger, plot_feature_importance, record_params
from freqtrade.strategy.interface import IStrategy
@@ -111,6 +110,7 @@ class IFreqaiModel(ABC):
if self.ft_params.get('principal_component_analysis', False) and self.continual_learning:
self.ft_params.update({'principal_component_analysis': False})
logger.warning('User tried to use PCA with continual learning. Deactivating PCA.')
+ self.activate_tensorboard: bool = self.freqai_info.get('activate_tensorboard', True)
record_params(config, self.full_path)
@@ -345,7 +345,8 @@ class IFreqaiModel(ABC):
dk.find_labels(dataframe_train)
try:
- self.tb_logger = TBLogger(dk.data_path)
+ self.tb_logger = get_tb_logger(self.dd.model_type, dk.data_path,
+ self.activate_tensorboard)
self.model = self.train(dataframe_train, pair, dk)
self.tb_logger.close()
except Exception as msg:
@@ -635,11 +636,10 @@ class IFreqaiModel(ABC):
dk.find_features(unfiltered_dataframe)
dk.find_labels(unfiltered_dataframe)
- if self.dd.model_type == "pytorch":
- self.tb_logger = TBLogger(dk.data_path)
+ self.tb_logger = get_tb_logger(self.dd.model_type, dk.data_path,
+ self.activate_tensorboard)
model = self.train(unfiltered_dataframe, pair, dk)
- if self.dd.model_type == "pytorch":
- self.tb_logger.close()
+ self.tb_logger.close()
self.dd.pair_dict[pair]["trained_timestamp"] = trained_timestamp
dk.set_new_model_names(pair, trained_timestamp)
diff --git a/freqtrade/freqai/prediction_models/ReinforcementLearner.py b/freqtrade/freqai/prediction_models/ReinforcementLearner.py
index 8c9d9bdef..0d6c52445 100644
--- a/freqtrade/freqai/prediction_models/ReinforcementLearner.py
+++ b/freqtrade/freqai/prediction_models/ReinforcementLearner.py
@@ -70,9 +70,14 @@ class ReinforcementLearner(BaseReinforcementLearningModel):
model = self.dd.model_dictionary[dk.pair]
model.set_env(self.train_env)
+ callbacks = [self.eval_callback]
+
+ if self.activate_tensorboard:
+ callbacks.append(self.tensorboard_callback)
+
model.learn(
total_timesteps=int(total_timesteps),
- callback=[self.eval_callback, self.tensorboard_callback],
+ callback=callbacks,
progress_bar=self.rl_config.get('progress_bar', False)
)
diff --git a/freqtrade/freqai/prediction_models/XGBoostRegressor.py b/freqtrade/freqai/prediction_models/XGBoostRegressor.py
index f1a2474da..f8b4d353d 100644
--- a/freqtrade/freqai/prediction_models/XGBoostRegressor.py
+++ b/freqtrade/freqai/prediction_models/XGBoostRegressor.py
@@ -45,7 +45,7 @@ class XGBoostRegressor(BaseRegressionModel):
model = XGBRegressor(**self.model_training_parameters)
- model.set_params(callbacks=[TBCallback(dk.data_path)])
+ model.set_params(callbacks=[TBCallback(dk.data_path)], activate=self.activate_tensorboard)
model.fit(X=X, y=y, sample_weight=sample_weight, eval_set=eval_set,
sample_weight_eval_set=eval_weights, xgb_model=xgb_model)
# set the callbacks to empty so that we can serialize to disk later
diff --git a/freqtrade/freqai/tensorboard/base_tensorboard.py b/freqtrade/freqai/tensorboard/base_tensorboard.py
index 186658532..8d396832b 100644
--- a/freqtrade/freqai/tensorboard/base_tensorboard.py
+++ b/freqtrade/freqai/tensorboard/base_tensorboard.py
@@ -9,7 +9,7 @@ logger = logging.getLogger(__name__)
class BaseTensorboardLogger:
- def __init__(self, logdir: Path):
+ def __init__(self, logdir: Path, activate: bool = True):
logger.warning("Tensorboard is not installed, no logs will be written."
"Ensure torch is installed, or use the torch/RL docker images")
@@ -22,7 +22,7 @@ class BaseTensorboardLogger:
class BaseTensorBoardCallback(xgb.callback.TrainingCallback):
- def __init__(self, logdir: Path):
+ def __init__(self, logdir: Path, activate: bool = True):
logger.warning("Tensorboard is not installed, no logs will be written."
"Ensure torch is installed, or use the torch/RL docker images")
diff --git a/freqtrade/freqai/tensorboard/tensorboard.py b/freqtrade/freqai/tensorboard/tensorboard.py
index f9070be6e..46bf8dc61 100644
--- a/freqtrade/freqai/tensorboard/tensorboard.py
+++ b/freqtrade/freqai/tensorboard/tensorboard.py
@@ -13,25 +13,33 @@ logger = logging.getLogger(__name__)
class TensorboardLogger(BaseTensorboardLogger):
- def __init__(self, logdir: Path):
- self.writer: SummaryWriter = SummaryWriter(f"{str(logdir)}/tensorboard")
+ def __init__(self, logdir: Path, activate: bool = True):
+ self.activate = activate
+ if self.activate:
+ self.writer: SummaryWriter = SummaryWriter(f"{str(logdir)}/tensorboard")
def log_scalar(self, tag: str, scalar_value: Any, step: int):
- self.writer.add_scalar(tag, scalar_value, step)
+ if self.activate:
+ self.writer.add_scalar(tag, scalar_value, step)
def close(self):
- self.writer.flush()
- self.writer.close()
+ if self.activate:
+ self.writer.flush()
+ self.writer.close()
class TensorBoardCallback(BaseTensorBoardCallback):
- def __init__(self, logdir: Path):
- self.writer: SummaryWriter = SummaryWriter(f"{str(logdir)}/tensorboard")
+ def __init__(self, logdir: Path, activate: bool = True):
+ self.activate = activate
+ if self.activate:
+ self.writer: SummaryWriter = SummaryWriter(f"{str(logdir)}/tensorboard")
def after_iteration(
self, model, epoch: int, evals_log: callback.TrainingCallback.EvalsLog
) -> bool:
+ if not self.activate:
+ return False
if not evals_log:
return False
@@ -39,13 +47,15 @@ class TensorBoardCallback(BaseTensorBoardCallback):
for metric_name, log in metric.items():
score = log[-1][0] if isinstance(log[-1], tuple) else log[-1]
if data == "train":
- self.writer.add_scalar("train_loss", score**2, epoch)
+ self.writer.add_scalar("train_loss", score, epoch)
else:
- self.writer.add_scalar("valid_loss", score**2, epoch)
+ self.writer.add_scalar("valid_loss", score, epoch)
return False
def after_training(self, model):
+ if not self.activate:
+ return model
self.writer.flush()
self.writer.close()
diff --git a/freqtrade/freqai/utils.py b/freqtrade/freqai/utils.py
index 2ba49ac40..cc803ae1e 100644
--- a/freqtrade/freqai/utils.py
+++ b/freqtrade/freqai/utils.py
@@ -1,7 +1,7 @@
import logging
from datetime import datetime, timezone
from pathlib import Path
-from typing import Any, Dict
+from typing import Any, Dict, Union
import numpy as np
import pandas as pd
@@ -16,6 +16,8 @@ from freqtrade.exchange import timeframe_to_seconds
from freqtrade.exchange.exchange import market_is_active
from freqtrade.freqai.data_drawer import FreqaiDataDrawer
from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
+from freqtrade.freqai.tensorboard import TBLogger
+from freqtrade.freqai.tensorboard.base_tensorboard import BaseTensorboardLogger
from freqtrade.plugins.pairlist.pairlist_helpers import dynamic_expand_pairlist
@@ -92,55 +94,6 @@ def get_required_data_timerange(config: Config) -> TimeRange:
return data_load_timerange
-# Keep below for when we wish to download heterogeneously lengthed data for FreqAI.
-# def download_all_data_for_training(dp: DataProvider, config: Config) -> None:
-# """
-# Called only once upon start of bot to download the necessary data for
-# populating indicators and training a FreqAI model.
-# :param timerange: TimeRange = The full data timerange for populating the indicators
-# and training the model.
-# :param dp: DataProvider instance attached to the strategy
-# """
-
-# if dp._exchange is not None:
-# markets = [p for p, m in dp._exchange.markets.items() if market_is_active(m)
-# or config.get('include_inactive')]
-# else:
-# # This should not occur:
-# raise OperationalException('No exchange object found.')
-
-# all_pairs = dynamic_expand_pairlist(config, markets)
-
-# if not dp._exchange:
-# # Not realistic - this is only called in live mode.
-# raise OperationalException("Dataprovider did not have an exchange attached.")
-
-# time = datetime.now(tz=timezone.utc).timestamp()
-
-# for tf in config["freqai"]["feature_parameters"].get("include_timeframes"):
-# timerange = TimeRange()
-# timerange.startts = int(time)
-# timerange.stopts = int(time)
-# startup_candles = dp.get_required_startup(str(tf))
-# tf_seconds = timeframe_to_seconds(str(tf))
-# timerange.subtract_start(tf_seconds * startup_candles)
-# new_pairs_days = int((timerange.stopts - timerange.startts) / 86400)
-# # FIXME: now that we are looping on `refresh_backtest_ohlcv_data`, the function
-# # redownloads the funding rate for each pair.
-# refresh_backtest_ohlcv_data(
-# dp._exchange,
-# pairs=all_pairs,
-# timeframes=[tf],
-# datadir=config["datadir"],
-# timerange=timerange,
-# new_pairs_days=new_pairs_days,
-# erase=False,
-# data_format=config.get("dataformat_ohlcv", "json"),
-# trading_mode=config.get("trading_mode", "spot"),
-# prepend=config.get("prepend_data", False),
-# )
-
-
def plot_feature_importance(model: Any, pair: str, dk: FreqaiDataKitchen,
count_max: int = 25) -> None:
"""
@@ -233,3 +186,14 @@ def get_timerange_backtest_live_models(config: Config) -> str:
dd = FreqaiDataDrawer(models_path, config)
timerange = dd.get_timerange_from_live_historic_predictions()
return timerange.timerange_str
+
+
+def get_tb_logger(model_type: str, path: Path, activate: bool) -> Union[TBLogger,
+ BaseTensorboardLogger]:
+ tb_logger: Union[TBLogger, BaseTensorboardLogger]
+ if model_type == "pytorch":
+ tb_logger = TBLogger(path, activate)
+ else:
+ tb_logger = BaseTensorboardLogger(path, activate)
+
+ return tb_logger
diff --git a/tests/freqai/test_freqai_datakitchen.py b/tests/freqai/test_freqai_datakitchen.py
index 13dc6b4b0..6d6e10b94 100644
--- a/tests/freqai/test_freqai_datakitchen.py
+++ b/tests/freqai/test_freqai_datakitchen.py
@@ -183,6 +183,7 @@ def test_get_full_model_path(mocker, freqai_conf, model):
strategy.freqai_info = freqai_conf.get("freqai", {})
freqai = strategy.freqai
freqai.live = True
+ freqai.activate_tensorboard = False
freqai.dk = FreqaiDataKitchen(freqai_conf)
freqai.dk.live = True
timerange = TimeRange.parse_timerange("20180110-20180130")
diff --git a/tests/freqai/test_freqai_interface.py b/tests/freqai/test_freqai_interface.py
index 407fb32ab..46e25462b 100644
--- a/tests/freqai/test_freqai_interface.py
+++ b/tests/freqai/test_freqai_interface.py
@@ -55,6 +55,10 @@ def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca,
can_run_model(model)
+ test_tb = True
+ if is_mac():
+ test_tb = False
+
model_save_ext = 'joblib'
freqai_conf.update({"freqaimodel": model})
freqai_conf.update({"timerange": "20180110-20180130"})
@@ -90,6 +94,7 @@ def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca,
strategy.freqai_info = freqai_conf.get("freqai", {})
freqai = strategy.freqai
freqai.live = True
+ freqai.activate_tensorboard = test_tb
freqai.can_short = can_short
freqai.dk = FreqaiDataKitchen(freqai_conf)
freqai.dk.live = True
@@ -139,6 +144,7 @@ def test_extract_data_and_train_model_MultiTargets(mocker, freqai_conf, model, s
strategy.freqai_info = freqai_conf.get("freqai", {})
freqai = strategy.freqai
freqai.live = True
+ freqai.activate_tensorboard = False
freqai.dk = FreqaiDataKitchen(freqai_conf)
freqai.dk.live = True
timerange = TimeRange.parse_timerange("20180110-20180130")
@@ -183,6 +189,7 @@ def test_extract_data_and_train_model_Classifiers(mocker, freqai_conf, model):
strategy.freqai_info = freqai_conf.get("freqai", {})
freqai = strategy.freqai
freqai.live = True
+ freqai.activate_tensorboard = False
freqai.dk = FreqaiDataKitchen(freqai_conf)
freqai.dk.live = True
timerange = TimeRange.parse_timerange("20180110-20180130")
@@ -235,6 +242,9 @@ def test_extract_data_and_train_model_Classifiers(mocker, freqai_conf, model):
)
def test_start_backtesting(mocker, freqai_conf, model, num_files, strat, caplog):
can_run_model(model)
+ test_tb = True
+ if is_mac():
+ test_tb = False
freqai_conf.get("freqai", {}).update({"save_backtest_models": True})
freqai_conf['runmode'] = RunMode.BACKTEST
@@ -267,6 +277,7 @@ def test_start_backtesting(mocker, freqai_conf, model, num_files, strat, caplog)
strategy.freqai_info = freqai_conf.get("freqai", {})
freqai = strategy.freqai
freqai.live = False
+ freqai.activate_tensorboard = test_tb
freqai.dk = FreqaiDataKitchen(freqai_conf)
timerange = TimeRange.parse_timerange("20180110-20180130")
freqai.dd.load_all_pair_histories(timerange, freqai.dk)
@@ -308,6 +319,7 @@ def test_start_backtesting_subdaily_backtest_period(mocker, freqai_conf):
strategy.freqai_info = freqai_conf.get("freqai", {})
freqai = strategy.freqai
freqai.live = False
+ freqai.activate_tensorboard = False
freqai.dk = FreqaiDataKitchen(freqai_conf)
timerange = TimeRange.parse_timerange("20180110-20180130")
freqai.dd.load_all_pair_histories(timerange, freqai.dk)
@@ -427,6 +439,7 @@ def test_principal_component_analysis(mocker, freqai_conf):
strategy.freqai_info = freqai_conf.get("freqai", {})
freqai = strategy.freqai
freqai.live = True
+ freqai.activate_tensorboard = False
freqai.dk = FreqaiDataKitchen(freqai_conf)
freqai.dk.live = True
timerange = TimeRange.parse_timerange("20180110-20180130")
@@ -460,6 +473,7 @@ def test_plot_feature_importance(mocker, freqai_conf):
strategy.freqai_info = freqai_conf.get("freqai", {})
freqai = strategy.freqai
freqai.live = True
+ freqai.activate_tensorboard = False
freqai.dk = FreqaiDataKitchen(freqai_conf)
freqai.dk.live = True
timerange = TimeRange.parse_timerange("20180110-20180130")