From 2403a03fcbbae135b9919e635ef8d63257610c97 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 29 Aug 2022 06:28:53 +0200 Subject: [PATCH 001/795] Version bump 2022.8 --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 2572c03f1..6c5c52a04 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ Freqtrade bot """ -__version__ = '2022.8.dev' +__version__ = '2022.8' if 'dev' in __version__: try: From d0456b698cb2ed8f60d8a3b80165c36b657bf59c Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 29 Sep 2022 07:22:41 +0200 Subject: [PATCH 002/795] Version bump 2022.9 --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 6c5c52a04..634377e05 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ Freqtrade bot """ -__version__ = '2022.8' +__version__ = '2022.9' if 'dev' in __version__: try: From 8e101a9f1c3d0b76ef7be383644f62b2cf178d41 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 1 Oct 2022 09:35:21 +0200 Subject: [PATCH 003/795] Disable log spam from analyze_df in webhook/discord --- freqtrade/rpc/discord.py | 2 +- freqtrade/rpc/webhook.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/freqtrade/rpc/discord.py b/freqtrade/rpc/discord.py index 9efe6f427..c48508300 100644 --- a/freqtrade/rpc/discord.py +++ b/freqtrade/rpc/discord.py @@ -30,9 +30,9 @@ class Discord(Webhook): pass def send_msg(self, msg) -> None: - logger.info(f"Sending discord message: {msg}") if msg['type'].value in self.config['discord']: + logger.info(f"Sending discord message: {msg}") msg['strategy'] = self.strategy msg['timeframe'] = self.timeframe diff --git a/freqtrade/rpc/webhook.py b/freqtrade/rpc/webhook.py index 6109e80bc..bb3b3922f 100644 --- a/freqtrade/rpc/webhook.py +++ b/freqtrade/rpc/webhook.py @@ -61,6 +61,14 @@ class Webhook(RPCHandler): RPCMessageType.STARTUP, RPCMessageType.WARNING): valuedict = whconfig.get('webhookstatus') + elif msg['type'] in ( + RPCMessageType.PROTECTION_TRIGGER, + RPCMessageType.PROTECTION_TRIGGER_GLOBAL, + RPCMessageType.WHITELIST, + RPCMessageType.ANALYZED_DF, + RPCMessageType.STRATEGY_MSG): + # Don't fail for non-implemented types + return else: raise NotImplementedError('Unknown message type: {}'.format(msg['type'])) if not valuedict: From 6841bdaa8193278d6863131f8866f4a59286da37 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 1 Oct 2022 09:45:58 +0200 Subject: [PATCH 004/795] Update test to verify webhook won't log-spam on new messagetypes --- tests/rpc/test_rpc_webhook.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/rpc/test_rpc_webhook.py b/tests/rpc/test_rpc_webhook.py index 4d65b4966..3bbb85d54 100644 --- a/tests/rpc/test_rpc_webhook.py +++ b/tests/rpc/test_rpc_webhook.py @@ -365,6 +365,14 @@ def test_exception_send_msg(default_conf, mocker, caplog): with pytest.raises(NotImplementedError): webhook.send_msg(msg) + # Test no failure for not implemented but known messagetypes + for e in RPCMessageType: + msg = { + 'type': e, + 'status': 'whatever' + } + webhook.send_msg(msg) + def test__send_msg(default_conf, mocker, caplog): default_conf["webhook"] = get_webhook_dict() From 19b3669d971231654c3ff0ff5a52a6fe6a4e447e Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 1 Oct 2022 21:20:14 +0200 Subject: [PATCH 005/795] Decrease message throughput fixes memory leak by queue raising indefinitely --- freqtrade/rpc/api_server/webserver.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/freqtrade/rpc/api_server/webserver.py b/freqtrade/rpc/api_server/webserver.py index df4324740..53af91477 100644 --- a/freqtrade/rpc/api_server/webserver.py +++ b/freqtrade/rpc/api_server/webserver.py @@ -198,8 +198,10 @@ class ApiServer(RPCHandler): logger.debug(f"Found message of type: {message.get('type')}") # Broadcast it await self._ws_channel_manager.broadcast(message) - # Sleep, make this configurable? - await asyncio.sleep(0.1) + # Limit messages per sec. + # Could cause problems with queue size if too low, and + # problems with network traffik if too high. + await asyncio.sleep(0.001) except asyncio.CancelledError: pass From 03256fc7768bfdebaa8ecbb9e547682ab36f0591 Mon Sep 17 00:00:00 2001 From: Robert Caulk Date: Sat, 1 Oct 2022 16:50:29 +0200 Subject: [PATCH 006/795] Merge pull request #7508 from aemr3/fix-pca-errors Fix feature list match for PCA --- .../freqai/base_models/BaseClassifierModel.py | 2 +- .../freqai/base_models/BaseRegressionModel.py | 2 +- freqtrade/freqai/data_drawer.py | 2 +- freqtrade/freqai/data_kitchen.py | 1 + freqtrade/freqai/freqai_interface.py | 17 +++++++++-------- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/freqtrade/freqai/base_models/BaseClassifierModel.py b/freqtrade/freqai/base_models/BaseClassifierModel.py index 70f212d2a..09f1bf98c 100644 --- a/freqtrade/freqai/base_models/BaseClassifierModel.py +++ b/freqtrade/freqai/base_models/BaseClassifierModel.py @@ -92,7 +92,7 @@ class BaseClassifierModel(IFreqaiModel): filtered_df = dk.normalize_data_from_metadata(filtered_df) dk.data_dictionary["prediction_features"] = filtered_df - self.data_cleaning_predict(dk, filtered_df) + self.data_cleaning_predict(dk) predictions = self.model.predict(dk.data_dictionary["prediction_features"]) pred_df = DataFrame(predictions, columns=dk.label_list) diff --git a/freqtrade/freqai/base_models/BaseRegressionModel.py b/freqtrade/freqai/base_models/BaseRegressionModel.py index 2450bf305..5d89dd356 100644 --- a/freqtrade/freqai/base_models/BaseRegressionModel.py +++ b/freqtrade/freqai/base_models/BaseRegressionModel.py @@ -92,7 +92,7 @@ class BaseRegressionModel(IFreqaiModel): dk.data_dictionary["prediction_features"] = filtered_df # optional additional data cleaning/analysis - self.data_cleaning_predict(dk, filtered_df) + self.data_cleaning_predict(dk) predictions = self.model.predict(dk.data_dictionary["prediction_features"]) pred_df = DataFrame(predictions, columns=dk.label_list) diff --git a/freqtrade/freqai/data_drawer.py b/freqtrade/freqai/data_drawer.py index 1839724f8..471f6875c 100644 --- a/freqtrade/freqai/data_drawer.py +++ b/freqtrade/freqai/data_drawer.py @@ -423,7 +423,7 @@ class FreqaiDataDrawer: dk.data["data_path"] = str(dk.data_path) dk.data["model_filename"] = str(dk.model_filename) - dk.data["training_features_list"] = list(dk.data_dictionary["train_features"].columns) + dk.data["training_features_list"] = dk.training_features_list dk.data["label_list"] = dk.label_list # store the metadata with open(save_path / f"{dk.model_filename}_metadata.json", "w") as fp: diff --git a/freqtrade/freqai/data_kitchen.py b/freqtrade/freqai/data_kitchen.py index f4fa4e5fd..697fd85cf 100644 --- a/freqtrade/freqai/data_kitchen.py +++ b/freqtrade/freqai/data_kitchen.py @@ -881,6 +881,7 @@ class FreqaiDataKitchen: """ column_names = dataframe.columns features = [c for c in column_names if "%" in c] + if not features: raise OperationalException("Could not find any features!") diff --git a/freqtrade/freqai/freqai_interface.py b/freqtrade/freqai/freqai_interface.py index d9f917338..78539bae5 100644 --- a/freqtrade/freqai/freqai_interface.py +++ b/freqtrade/freqai/freqai_interface.py @@ -275,7 +275,8 @@ class IFreqaiModel(ABC): if dk.check_if_backtest_prediction_exists(): self.dd.load_metadata(dk) - self.check_if_feature_list_matches_strategy(dataframe_train, dk) + dk.find_features(dataframe_train) + self.check_if_feature_list_matches_strategy(dk) append_df = dk.get_backtesting_prediction() dk.append_predictions(append_df) else: @@ -296,7 +297,6 @@ class IFreqaiModel(ABC): else: self.model = self.dd.load_data(pair, dk) - # self.check_if_feature_list_matches_strategy(dataframe_train, dk) pred_df, do_preds = self.predict(dataframe_backtest, dk) append_df = dk.get_predictions_to_append(pred_df, do_preds) dk.append_predictions(append_df) @@ -420,7 +420,7 @@ class IFreqaiModel(ABC): return def check_if_feature_list_matches_strategy( - self, dataframe: DataFrame, dk: FreqaiDataKitchen + self, dk: FreqaiDataKitchen ) -> None: """ Ensure user is passing the proper feature set if they are reusing an `identifier` pointing @@ -429,11 +429,12 @@ class IFreqaiModel(ABC): :param dk: FreqaiDataKitchen = non-persistent data container/analyzer for current coin/bot loop """ - dk.find_features(dataframe) + if "training_features_list_raw" in dk.data: feature_list = dk.data["training_features_list_raw"] else: feature_list = dk.data['training_features_list'] + if dk.training_features_list != feature_list: raise OperationalException( "Trying to access pretrained model with `identifier` " @@ -481,13 +482,16 @@ class IFreqaiModel(ABC): if self.freqai_info["feature_parameters"].get('noise_standard_deviation', 0): dk.add_noise_to_training_features() - def data_cleaning_predict(self, dk: FreqaiDataKitchen, dataframe: DataFrame) -> None: + def data_cleaning_predict(self, dk: FreqaiDataKitchen) -> None: """ Base data cleaning method for predict. Functions here are complementary to the functions of data_cleaning_train. """ ft_params = self.freqai_info["feature_parameters"] + # ensure user is feeding the correct indicators to the model + self.check_if_feature_list_matches_strategy(dk) + if ft_params.get('inlier_metric_window', 0): dk.compute_inlier_metric(set_='predict') @@ -505,9 +509,6 @@ class IFreqaiModel(ABC): if ft_params.get("use_DBSCAN_to_remove_outliers", False): dk.use_DBSCAN_to_remove_outliers(predict=True) - # ensure user is feeding the correct indicators to the model - self.check_if_feature_list_matches_strategy(dk.data_dictionary['prediction_features'], dk) - def model_exists(self, dk: FreqaiDataKitchen) -> bool: """ Given a pair and path, check if a model already exists From c53ff94b8e5de6a2cbbad69f386eb1c717211ce2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 30 Sep 2022 13:47:26 +0200 Subject: [PATCH 007/795] Force joblib update via setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0581081fa..d3f9ea7c0 100644 --- a/setup.py +++ b/setup.py @@ -72,7 +72,7 @@ setup( 'pandas', 'tables', 'blosc', - 'joblib', + 'joblib>=1.2.0', 'pyarrow; platform_machine != "armv7l"', 'fastapi', 'uvicorn', From 59cfde3767f449c588500acb07c12503ac3d9032 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 30 Sep 2022 15:43:05 +0200 Subject: [PATCH 008/795] Fix pandas deprecation warnings from freqAI --- freqtrade/freqai/data_kitchen.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/freqtrade/freqai/data_kitchen.py b/freqtrade/freqai/data_kitchen.py index 697fd85cf..766eb981f 100644 --- a/freqtrade/freqai/data_kitchen.py +++ b/freqtrade/freqai/data_kitchen.py @@ -210,7 +210,7 @@ class FreqaiDataKitchen: filtered_df = unfiltered_df.filter(training_feature_list, axis=1) filtered_df = filtered_df.replace([np.inf, -np.inf], np.nan) - drop_index = pd.isnull(filtered_df).any(1) # get the rows that have NaNs, + drop_index = pd.isnull(filtered_df).any(axis=1) # get the rows that have NaNs, drop_index = drop_index.replace(True, 1).replace(False, 0) # pep8 requirement. if (training_filter): const_cols = list((filtered_df.nunique() == 1).loc[lambda x: x].index) @@ -221,7 +221,7 @@ class FreqaiDataKitchen: # about removing any row with NaNs # if labels has multiple columns (user wants to train multiple modelEs), we detect here labels = unfiltered_df.filter(label_list, axis=1) - drop_index_labels = pd.isnull(labels).any(1) + drop_index_labels = pd.isnull(labels).any(axis=1) drop_index_labels = drop_index_labels.replace(True, 1).replace(False, 0) dates = unfiltered_df['date'] filtered_df = filtered_df[ @@ -249,7 +249,7 @@ class FreqaiDataKitchen: else: # we are backtesting so we need to preserve row number to send back to strategy, # so now we use do_predict to avoid any prediction based on a NaN - drop_index = pd.isnull(filtered_df).any(1) + drop_index = pd.isnull(filtered_df).any(axis=1) self.data["filter_drop_index_prediction"] = drop_index filtered_df.fillna(0, inplace=True) # replacing all NaNs with zeros to avoid issues in 'prediction', but any prediction @@ -808,7 +808,7 @@ class FreqaiDataKitchen: :, :no_prev_pts ] distances = distances.replace([np.inf, -np.inf], np.nan) - drop_index = pd.isnull(distances).any(1) + drop_index = pd.isnull(distances).any(axis=1) distances = distances[drop_index == 0] inliers = pd.DataFrame(index=distances.index) From 851d1e9da106aad2153ec38919873f61ce4b384f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 2 Oct 2022 06:56:02 +0200 Subject: [PATCH 009/795] Version bump 2022.9.1 --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 634377e05..2bd09a377 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ Freqtrade bot """ -__version__ = '2022.9' +__version__ = '2022.9.1' if 'dev' in __version__: try: From ec7d6634963cd9482f47e5db0c6e90e9ffd176ba Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 28 Oct 2022 19:34:30 +0200 Subject: [PATCH 010/795] Version bump 2022.10 --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index ce5f420e4..74c558a37 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ Freqtrade bot """ -__version__ = '2022.9.1' +__version__ = '2022.10' if 'dev' in __version__: try: From 5c571f565ff639d10b5095db7f7429e35a9c0b73 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 27 Nov 2022 15:34:00 +0100 Subject: [PATCH 011/795] Version bump 2022.11 --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 74c558a37..47d095f65 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ Freqtrade bot """ -__version__ = '2022.10' +__version__ = '2022.11' if 'dev' in __version__: try: From f410b1b14d71ea170a03b6e95576f70a8f7f6385 Mon Sep 17 00:00:00 2001 From: Stefano Ariestasia Date: Mon, 28 Nov 2022 08:56:49 +0900 Subject: [PATCH 012/795] Update metrics.py --- freqtrade/data/metrics.py | 129 +++++++++++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 2 deletions(-) diff --git a/freqtrade/data/metrics.py b/freqtrade/data/metrics.py index c11a2df88..4d442ac6a 100644 --- a/freqtrade/data/metrics.py +++ b/freqtrade/data/metrics.py @@ -1,9 +1,9 @@ import logging from typing import Dict, Tuple - +from datetime import datetime import numpy as np import pandas as pd - +import math logger = logging.getLogger(__name__) @@ -190,3 +190,128 @@ def calculate_cagr(days_passed: int, starting_balance: float, final_balance: flo :return: CAGR """ return (final_balance / starting_balance) ** (1 / (days_passed / 365)) - 1 + + +def calculate_expectancy(trades: pd.DataFrame) -> float: + """ + Calculate expectancy + :param trades: DataFrame containing trades (requires columns close_date and profit_ratio) + :return: expectancy + """ + if len(trades) == 0: + return 0 + + expectancy = 1 + + profit_sum = trades.loc[trades['profit_abs'] > 0, 'profit_abs'].sum() + loss_sum = abs(trades.loc[trades['profit_abs'] < 0, 'profit_abs'].sum()) + nb_win_trades = len(trades.loc[trades['profit_abs'] > 0]) + nb_loss_trades = len(trades.loc[trades['profit_abs'] < 0]) + + if (nb_win_trades > 0) and (nb_loss_trades > 0): + average_win = profit_sum / nb_win_trades + average_loss = loss_sum / nb_loss_trades + risk_reward_ratio = average_win / average_loss + winrate = nb_win_trades / len(trades) + expectancy = ((1 + risk_reward_ratio) * winrate) - 1 + elif nb_win_trades == 0: + expectancy = 0 + + return expectancy + +def calculate_sortino(trades: pd.DataFrame, + min_date: datetime, max_date: datetime) -> float: + """ + Calculate sortino + :param trades: DataFrame containing trades (requires columns profit_ratio) + :return: sortino + """ + if (len(trades) == 0) or (min_date == None) or (max_date == None) or (min_date == max_date): + return 0 + + total_profit = trades["profit_ratio"] + days_period = (max_date - min_date).days + + if days_period == 0: + return 0 + + # adding slippage of 0.1% per trade + # total_profit = total_profit - 0.0005 + expected_returns_mean = total_profit.sum() / days_period + + trades['downside_returns'] = 0 + trades.loc[total_profit < 0, 'downside_returns'] = trades['profit_ratio'] + down_stdev = np.std(trades['downside_returns']) + + if down_stdev != 0: + sortino_ratio = expected_returns_mean / down_stdev * np.sqrt(365) + else: + # Define high (negative) sortino ratio to be clear that this is NOT optimal. + sortino_ratio = -100 + + # print(expected_returns_mean, down_stdev, sortino_ratio) + return sortino_ratio + +def calculate_sharpe(trades: pd.DataFrame, + min_date: datetime, max_date: datetime) -> float: + """ + Calculate sharpe + :param trades: DataFrame containing trades (requires columns close_date and profit_ratio) + :return: sharpe + """ + if (len(trades) == 0) or (min_date == None) or (max_date == None) or (min_date == max_date): + return 0 + + total_profit = trades["profit_ratio"] + days_period = (max_date - min_date).days + + if days_period == 0: + return 0 + + # adding slippage of 0.1% per trade + # total_profit = total_profit - 0.0005 + expected_returns_mean = total_profit.sum() / days_period + up_stdev = np.std(total_profit) + + if up_stdev != 0: + sharp_ratio = expected_returns_mean / up_stdev * np.sqrt(365) + else: + # Define high (negative) sharpe ratio to be clear that this is NOT optimal. + sharp_ratio = -100 + + # print(expected_returns_mean, up_stdev, sharp_ratio) + return sharp_ratio + +def calculate_calmar(trades: pd.DataFrame, + min_date: datetime, max_date: datetime) -> float: + """ + Calculate calmar + :param trades: DataFrame containing trades (requires columns close_date and profit_ratio) + :return: calmar + """ + if (len(trades) == 0) or (min_date == None) or (max_date == None) or (min_date == max_date): + return 0 + + total_profit = trades["profit_ratio"] + days_period = (max_date - min_date).days + + # adding slippage of 0.1% per trade + # total_profit = total_profit - 0.0005 + expected_returns_mean = total_profit.sum() / days_period * 100 + + # calculate max drawdown + try: + _, _, _, _, _, max_drawdown = calculate_max_drawdown( + trades, value_col="profit_abs" + ) + except ValueError: + max_drawdown = 0 + + if max_drawdown != 0: + calmar_ratio = expected_returns_mean / max_drawdown * math.sqrt(365) + else: + # Define high (negative) calmar ratio to be clear that this is NOT optimal. + calmar_ratio = -100 + + # print(expected_returns_mean, max_drawdown, calmar_ratio) + return calmar_ratio From e734b399296cd88e77d6962281f13f49a9a9b016 Mon Sep 17 00:00:00 2001 From: Emre Date: Mon, 5 Dec 2022 14:54:42 +0300 Subject: [PATCH 013/795] Make model_training_parameters optional --- config_examples/config_freqai.example.json | 4 +--- docs/freqai-configuration.md | 11 ++++------- freqtrade/constants.py | 5 ++--- .../freqai/prediction_models/ReinforcementLearner.py | 2 +- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/config_examples/config_freqai.example.json b/config_examples/config_freqai.example.json index 5e564a1fc..dfd54b3d9 100644 --- a/config_examples/config_freqai.example.json +++ b/config_examples/config_freqai.example.json @@ -79,9 +79,7 @@ "test_size": 0.33, "random_state": 1 }, - "model_training_parameters": { - "n_estimators": 1000 - } + "model_training_parameters": {} }, "bot_name": "", "force_entry_enable": true, diff --git a/docs/freqai-configuration.md b/docs/freqai-configuration.md index 5c3bbf90c..10f5838c9 100644 --- a/docs/freqai-configuration.md +++ b/docs/freqai-configuration.md @@ -26,10 +26,7 @@ FreqAI is configured through the typical [Freqtrade config file](configuration.m }, "data_split_parameters" : { "test_size": 0.25 - }, - "model_training_parameters" : { - "n_estimators": 100 - }, + } } ``` @@ -118,7 +115,7 @@ The FreqAI strategy requires including the following lines of code in the standa ``` -Notice how the `populate_any_indicators()` is where [features](freqai-feature-engineering.md#feature-engineering) and labels/targets are added. A full example strategy is available in `templates/FreqaiExampleStrategy.py`. +Notice how the `populate_any_indicators()` is where [features](freqai-feature-engineering.md#feature-engineering) and labels/targets are added. A full example strategy is available in `templates/FreqaiExampleStrategy.py`. Notice also the location of the labels under `if set_generalized_indicators:` at the bottom of the example. This is where single features and labels/targets should be added to the feature set to avoid duplication of them from various configuration parameters that multiply the feature set, such as `include_timeframes`. @@ -182,7 +179,7 @@ The `startup_candle_count` in the FreqAI strategy needs to be set up in the same ## Creating a dynamic target threshold -Deciding when to enter or exit a trade can be done in a dynamic way to reflect current market conditions. FreqAI allows you to return additional information from the training of a model (more info [here](freqai-feature-engineering.md#returning-additional-info-from-training)). For example, the `&*_std/mean` return values describe the statistical distribution of the target/label *during the most recent training*. Comparing a given prediction to these values allows you to know the rarity of the prediction. In `templates/FreqaiExampleStrategy.py`, the `target_roi` and `sell_roi` are defined to be 1.25 z-scores away from the mean which causes predictions that are closer to the mean to be filtered out. +Deciding when to enter or exit a trade can be done in a dynamic way to reflect current market conditions. FreqAI allows you to return additional information from the training of a model (more info [here](freqai-feature-engineering.md#returning-additional-info-from-training)). For example, the `&*_std/mean` return values describe the statistical distribution of the target/label *during the most recent training*. Comparing a given prediction to these values allows you to know the rarity of the prediction. In `templates/FreqaiExampleStrategy.py`, the `target_roi` and `sell_roi` are defined to be 1.25 z-scores away from the mean which causes predictions that are closer to the mean to be filtered out. ```python dataframe["target_roi"] = dataframe["&-s_close_mean"] + dataframe["&-s_close_std"] * 1.25 @@ -230,7 +227,7 @@ If you want to predict multiple targets, you need to define multiple labels usin #### Classifiers -If you are using a classifier, you need to specify a target that has discrete values. FreqAI includes a variety of classifiers, such as the `CatboostClassifier` via the flag `--freqaimodel CatboostClassifier`. If you elects to use a classifier, the classes need to be set using strings. For example, if you want to predict if the price 100 candles into the future goes up or down you would set +If you are using a classifier, you need to specify a target that has discrete values. FreqAI includes a variety of classifiers, such as the `CatboostClassifier` via the flag `--freqaimodel CatboostClassifier`. If you elects to use a classifier, the classes need to be set using strings. For example, if you want to predict if the price 100 candles into the future goes up or down you would set ```python df['&s-up_or_down'] = np.where( df["close"].shift(-100) > df["close"], 'up', 'down') diff --git a/freqtrade/constants.py b/freqtrade/constants.py index d869b89f6..ca1be1d6a 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -608,9 +608,8 @@ CONF_SCHEMA = { "backtest_period_days", "identifier", "feature_parameters", - "data_split_parameters", - "model_training_parameters" - ] + "data_split_parameters" + ] }, }, } diff --git a/freqtrade/freqai/prediction_models/ReinforcementLearner.py b/freqtrade/freqai/prediction_models/ReinforcementLearner.py index 61b01e21b..39901859c 100644 --- a/freqtrade/freqai/prediction_models/ReinforcementLearner.py +++ b/freqtrade/freqai/prediction_models/ReinforcementLearner.py @@ -61,7 +61,7 @@ class ReinforcementLearner(BaseReinforcementLearningModel): model = self.MODELCLASS(self.policy_type, self.train_env, policy_kwargs=policy_kwargs, tensorboard_log=Path( dk.full_path / "tensorboard" / dk.pair.split('/')[0]), - **self.freqai_info['model_training_parameters'] + **self.freqai_info.get('model_training_parameters', {}) ) else: logger.info('Continual training activated - starting training from previously ' From 730fba956b55b67555bf5766532faf7ddc8ba856 Mon Sep 17 00:00:00 2001 From: Emre Date: Mon, 5 Dec 2022 16:16:17 +0300 Subject: [PATCH 014/795] Ensure base tf included in include_timeframes --- freqtrade/freqai/utils.py | 20 ++++++++++++++++++++ freqtrade/strategy/interface.py | 4 +++- tests/freqai/test_freqai_interface.py | 17 ++++++++++++++++- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqai/utils.py b/freqtrade/freqai/utils.py index 806e3ca15..7a9d3df76 100644 --- a/freqtrade/freqai/utils.py +++ b/freqtrade/freqai/utils.py @@ -233,3 +233,23 @@ 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 ensure_base_tf_in_include_timeframes(config: Config) -> Config: + """ + Ensure that the base timeframe is included in the include_timeframes list + :param config: Configuration dictionary + + :return config: Configuration dictionary + """ + feature_parameters = config.get('freqai', {}).get('feature_parameters', {}) + include_timeframes = feature_parameters.get('include_timeframes', []) + + if config['timeframe'] in include_timeframes: + return config + + include_timeframes = [config['timeframe']] + include_timeframes + config.get('freqai', {}).get('feature_parameters', {}) \ + .update({**feature_parameters, 'include_timeframes': include_timeframes}) + + return config diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 681c5fcbb..48a03e216 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -148,9 +148,11 @@ class IStrategy(ABC, HyperStrategyMixin): def load_freqAI_model(self) -> None: if self.config.get('freqai', {}).get('enabled', False): # Import here to avoid importing this if freqAI is disabled - from freqtrade.freqai.utils import download_all_data_for_training + from freqtrade.freqai.utils import (download_all_data_for_training, + ensure_base_tf_in_include_timeframes) from freqtrade.resolvers.freqaimodel_resolver import FreqaiModelResolver self.freqai = FreqaiModelResolver.load_freqaimodel(self.config) + self.config = ensure_base_tf_in_include_timeframes(self.config) self.freqai_info = self.config["freqai"] # download the desired data in dry/live diff --git a/tests/freqai/test_freqai_interface.py b/tests/freqai/test_freqai_interface.py index c53137093..6f01c66f6 100644 --- a/tests/freqai/test_freqai_interface.py +++ b/tests/freqai/test_freqai_interface.py @@ -9,7 +9,9 @@ from freqtrade.configuration import TimeRange from freqtrade.data.dataprovider import DataProvider from freqtrade.enums import RunMode from freqtrade.freqai.data_kitchen import FreqaiDataKitchen -from freqtrade.freqai.utils import download_all_data_for_training, get_required_data_timerange +from freqtrade.freqai.utils import (download_all_data_for_training, + ensure_base_tf_in_include_timeframes, + get_required_data_timerange) from freqtrade.optimize.backtesting import Backtesting from freqtrade.persistence import Trade from freqtrade.plugins.pairlistmanager import PairListManager @@ -528,6 +530,19 @@ def test_start_set_train_queue(mocker, freqai_conf, caplog): ) +def test_base_tf_in_include_timeframes(mocker, freqai_conf): + freqai_conf['timeframe'] = '5m' + freqai_conf['freqai']['feature_parameters'].update({ + 'include_timeframes': ['15m', '1h'] + }) + updated_conf = ensure_base_tf_in_include_timeframes(freqai_conf) + assert updated_conf['freqai']['feature_parameters']['include_timeframes'] == [ + '5m', '15m', '1h', + ] + last_conf = ensure_base_tf_in_include_timeframes(updated_conf) + assert last_conf == updated_conf + + def test_get_required_data_timerange(mocker, freqai_conf): time_range = get_required_data_timerange(freqai_conf) assert (time_range.stopts - time_range.startts) == 177300 From bc48099e48333d5c657fcbb11831ea8cd1700697 Mon Sep 17 00:00:00 2001 From: Emre Date: Mon, 5 Dec 2022 23:52:48 +0300 Subject: [PATCH 015/795] Revert changes --- freqtrade/freqai/utils.py | 20 -------------------- freqtrade/strategy/interface.py | 4 +--- tests/freqai/test_freqai_interface.py | 17 +---------------- 3 files changed, 2 insertions(+), 39 deletions(-) diff --git a/freqtrade/freqai/utils.py b/freqtrade/freqai/utils.py index 7a9d3df76..806e3ca15 100644 --- a/freqtrade/freqai/utils.py +++ b/freqtrade/freqai/utils.py @@ -233,23 +233,3 @@ 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 ensure_base_tf_in_include_timeframes(config: Config) -> Config: - """ - Ensure that the base timeframe is included in the include_timeframes list - :param config: Configuration dictionary - - :return config: Configuration dictionary - """ - feature_parameters = config.get('freqai', {}).get('feature_parameters', {}) - include_timeframes = feature_parameters.get('include_timeframes', []) - - if config['timeframe'] in include_timeframes: - return config - - include_timeframes = [config['timeframe']] + include_timeframes - config.get('freqai', {}).get('feature_parameters', {}) \ - .update({**feature_parameters, 'include_timeframes': include_timeframes}) - - return config diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 48a03e216..681c5fcbb 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -148,11 +148,9 @@ class IStrategy(ABC, HyperStrategyMixin): def load_freqAI_model(self) -> None: if self.config.get('freqai', {}).get('enabled', False): # Import here to avoid importing this if freqAI is disabled - from freqtrade.freqai.utils import (download_all_data_for_training, - ensure_base_tf_in_include_timeframes) + from freqtrade.freqai.utils import download_all_data_for_training from freqtrade.resolvers.freqaimodel_resolver import FreqaiModelResolver self.freqai = FreqaiModelResolver.load_freqaimodel(self.config) - self.config = ensure_base_tf_in_include_timeframes(self.config) self.freqai_info = self.config["freqai"] # download the desired data in dry/live diff --git a/tests/freqai/test_freqai_interface.py b/tests/freqai/test_freqai_interface.py index 6f01c66f6..c53137093 100644 --- a/tests/freqai/test_freqai_interface.py +++ b/tests/freqai/test_freqai_interface.py @@ -9,9 +9,7 @@ from freqtrade.configuration import TimeRange from freqtrade.data.dataprovider import DataProvider from freqtrade.enums import RunMode from freqtrade.freqai.data_kitchen import FreqaiDataKitchen -from freqtrade.freqai.utils import (download_all_data_for_training, - ensure_base_tf_in_include_timeframes, - get_required_data_timerange) +from freqtrade.freqai.utils import download_all_data_for_training, get_required_data_timerange from freqtrade.optimize.backtesting import Backtesting from freqtrade.persistence import Trade from freqtrade.plugins.pairlistmanager import PairListManager @@ -530,19 +528,6 @@ def test_start_set_train_queue(mocker, freqai_conf, caplog): ) -def test_base_tf_in_include_timeframes(mocker, freqai_conf): - freqai_conf['timeframe'] = '5m' - freqai_conf['freqai']['feature_parameters'].update({ - 'include_timeframes': ['15m', '1h'] - }) - updated_conf = ensure_base_tf_in_include_timeframes(freqai_conf) - assert updated_conf['freqai']['feature_parameters']['include_timeframes'] == [ - '5m', '15m', '1h', - ] - last_conf = ensure_base_tf_in_include_timeframes(updated_conf) - assert last_conf == updated_conf - - def test_get_required_data_timerange(mocker, freqai_conf): time_range = get_required_data_timerange(freqai_conf) assert (time_range.stopts - time_range.startts) == 177300 From 26a61afa15bce5d85256e8706534295f8cb033c3 Mon Sep 17 00:00:00 2001 From: Emre Date: Mon, 5 Dec 2022 23:54:15 +0300 Subject: [PATCH 016/795] Move base tf logic to config validation --- freqtrade/configuration/config_validation.py | 7 +++++++ tests/test_configuration.py | 7 ++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index bf0657994..606f081ef 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -355,6 +355,13 @@ def _validate_freqai_include_timeframes(conf: Dict[str, Any]) -> None: f"Main timeframe of {main_tf} must be smaller or equal to FreqAI " f"`include_timeframes`.Offending include-timeframes: {', '.join(offending_lines)}") + # Ensure that the base timeframe is included in the include_timeframes list + if main_tf not in freqai_include_timeframes: + feature_parameters = conf.get('freqai', {}).get('feature_parameters', {}) + include_timeframes = [main_tf] + freqai_include_timeframes + conf.get('freqai', {}).get('feature_parameters', {}) \ + .update({**feature_parameters, 'include_timeframes': include_timeframes}) + def _validate_freqai_backtest(conf: Dict[str, Any]) -> None: if conf.get('runmode', RunMode.OTHER) == RunMode.BACKTEST: diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 1bcff20db..cdf9f2f2e 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -1046,8 +1046,13 @@ def test__validate_freqai_include_timeframes(default_conf, caplog) -> None: # Validation pass conf.update({'timeframe': '1m'}) validate_config_consistency(conf) - conf.update({'analyze_per_epoch': True}) + # Ensure base timeframe is in include_timeframes + conf['freqai']['feature_parameters']['include_timeframes'] = ["5m", "15m"] + validate_config_consistency(conf) + assert conf['freqai']['feature_parameters']['include_timeframes'] == ["1m", "5m", "15m"] + + conf.update({'analyze_per_epoch': True}) with pytest.raises(OperationalException, match=r"Using analyze-per-epoch .* not supported with a FreqAI strategy."): validate_config_consistency(conf) From 227cdb09386153fd7a871e3b72ff46cd2999962e Mon Sep 17 00:00:00 2001 From: Emre Date: Mon, 5 Dec 2022 23:58:04 +0300 Subject: [PATCH 017/795] Change dict update order --- freqtrade/configuration/config_validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index 606f081ef..7e291cb90 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -360,7 +360,7 @@ def _validate_freqai_include_timeframes(conf: Dict[str, Any]) -> None: feature_parameters = conf.get('freqai', {}).get('feature_parameters', {}) include_timeframes = [main_tf] + freqai_include_timeframes conf.get('freqai', {}).get('feature_parameters', {}) \ - .update({**feature_parameters, 'include_timeframes': include_timeframes}) + .update({'include_timeframes': include_timeframes, **feature_parameters}) def _validate_freqai_backtest(conf: Dict[str, Any]) -> None: From 611e35ed81dd305b36fc6a4a1a8cf1371585a3da Mon Sep 17 00:00:00 2001 From: Stefano Ariestasia Date: Wed, 7 Dec 2022 15:47:58 +0900 Subject: [PATCH 018/795] flake8 fix --- freqtrade/data/metrics.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/freqtrade/data/metrics.py b/freqtrade/data/metrics.py index 4d442ac6a..02a57517b 100644 --- a/freqtrade/data/metrics.py +++ b/freqtrade/data/metrics.py @@ -219,6 +219,7 @@ def calculate_expectancy(trades: pd.DataFrame) -> float: return expectancy + def calculate_sortino(trades: pd.DataFrame, min_date: datetime, max_date: datetime) -> float: """ @@ -226,7 +227,7 @@ def calculate_sortino(trades: pd.DataFrame, :param trades: DataFrame containing trades (requires columns profit_ratio) :return: sortino """ - if (len(trades) == 0) or (min_date == None) or (max_date == None) or (min_date == max_date): + if (len(trades) == 0) or (min_date is None) or (max_date is None) or (min_date == max_date): return 0 total_profit = trades["profit_ratio"] @@ -252,14 +253,15 @@ def calculate_sortino(trades: pd.DataFrame, # print(expected_returns_mean, down_stdev, sortino_ratio) return sortino_ratio + def calculate_sharpe(trades: pd.DataFrame, - min_date: datetime, max_date: datetime) -> float: + min_date: datetime, max_date: datetime) -> float: """ Calculate sharpe :param trades: DataFrame containing trades (requires columns close_date and profit_ratio) :return: sharpe """ - if (len(trades) == 0) or (min_date == None) or (max_date == None) or (min_date == max_date): + if (len(trades) == 0) or (min_date is None) or (max_date is None) or (min_date == max_date): return 0 total_profit = trades["profit_ratio"] @@ -282,14 +284,15 @@ def calculate_sharpe(trades: pd.DataFrame, # print(expected_returns_mean, up_stdev, sharp_ratio) return sharp_ratio + def calculate_calmar(trades: pd.DataFrame, - min_date: datetime, max_date: datetime) -> float: + min_date: datetime, max_date: datetime) -> float: """ Calculate calmar :param trades: DataFrame containing trades (requires columns close_date and profit_ratio) :return: calmar """ - if (len(trades) == 0) or (min_date == None) or (max_date == None) or (min_date == max_date): + if (len(trades) == 0) or (min_date is None) or (max_date is None) or (min_date == max_date): return 0 total_profit = trades["profit_ratio"] From 89c7c2fec647df3c5760ddf206ade0a3cf2b9c03 Mon Sep 17 00:00:00 2001 From: Stefano Ariestasia Date: Wed, 7 Dec 2022 18:09:57 +0900 Subject: [PATCH 019/795] isort fix --- freqtrade/data/metrics.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/freqtrade/data/metrics.py b/freqtrade/data/metrics.py index 02a57517b..eccb8a04d 100644 --- a/freqtrade/data/metrics.py +++ b/freqtrade/data/metrics.py @@ -1,9 +1,11 @@ import logging -from typing import Dict, Tuple +import math from datetime import datetime +from typing import Dict, Tuple + import numpy as np import pandas as pd -import math + logger = logging.getLogger(__name__) From 58604c747e759161f25ad4c90571fbaf6a1c5233 Mon Sep 17 00:00:00 2001 From: initrv Date: Wed, 7 Dec 2022 14:37:55 +0300 Subject: [PATCH 020/795] cleanup tensorboard callback --- freqtrade/freqai/RL/BaseEnvironment.py | 10 ++----- freqtrade/freqai/RL/TensorboardCallback.py | 27 +++++++++---------- .../prediction_models/ReinforcementLearner.py | 14 +++++----- 3 files changed, 21 insertions(+), 30 deletions(-) diff --git a/freqtrade/freqai/RL/BaseEnvironment.py b/freqtrade/freqai/RL/BaseEnvironment.py index a31ded0c6..71b423844 100644 --- a/freqtrade/freqai/RL/BaseEnvironment.py +++ b/freqtrade/freqai/RL/BaseEnvironment.py @@ -137,15 +137,9 @@ class BaseEnvironment(gym.Env): Reset is called at the beginning of every episode """ # custom_info is used for episodic reports and tensorboard logging - self.custom_info["Invalid"] = 0 - self.custom_info["Hold"] = 0 - self.custom_info["Unknown"] = 0 - self.custom_info["pnl_factor"] = 0 - self.custom_info["duration_factor"] = 0 - self.custom_info["reward_exit"] = 0 - self.custom_info["reward_hold"] = 0 + self.custom_info: dict = {} for action in self.actions: - self.custom_info[f"{action.name}"] = 0 + self.custom_info[action.name] = 0 self._done = False diff --git a/freqtrade/freqai/RL/TensorboardCallback.py b/freqtrade/freqai/RL/TensorboardCallback.py index f590bdf84..d03c040d4 100644 --- a/freqtrade/freqai/RL/TensorboardCallback.py +++ b/freqtrade/freqai/RL/TensorboardCallback.py @@ -42,19 +42,18 @@ class TensorboardCallback(BaseCallback): ) def _on_step(self) -> bool: + + local_info = self.locals["infos"][0] custom_info = self.training_env.get_attr("custom_info")[0] - self.logger.record("_state/position", self.locals["infos"][0]["position"]) - self.logger.record("_state/trade_duration", self.locals["infos"][0]["trade_duration"]) - self.logger.record("_state/current_profit_pct", self.locals["infos"] - [0]["current_profit_pct"]) - self.logger.record("_reward/total_profit", self.locals["infos"][0]["total_profit"]) - self.logger.record("_reward/total_reward", self.locals["infos"][0]["total_reward"]) - self.logger.record_mean("_reward/mean_trade_duration", self.locals["infos"] - [0]["trade_duration"]) - self.logger.record("_actions/action", self.locals["infos"][0]["action"]) - self.logger.record("_actions/_Invalid", custom_info["Invalid"]) - self.logger.record("_actions/_Unknown", custom_info["Unknown"]) - self.logger.record("_actions/Hold", custom_info["Hold"]) - for action in self.actions: - self.logger.record(f"_actions/{action.name}", custom_info[action.name]) + + for info in local_info: + if info not in ["episode", "terminal_observation"]: + self.logger.record(f"_info/{info}", local_info[info]) + + for info in custom_info: + if info in [action.name for action in self.actions]: + self.logger.record(f"_actions/{info}", custom_info[info]) + else: + self.logger.record(f"_custom/{info}", custom_info[info]) + return True diff --git a/freqtrade/freqai/prediction_models/ReinforcementLearner.py b/freqtrade/freqai/prediction_models/ReinforcementLearner.py index 47dbaf99e..1383ad15e 100644 --- a/freqtrade/freqai/prediction_models/ReinforcementLearner.py +++ b/freqtrade/freqai/prediction_models/ReinforcementLearner.py @@ -100,7 +100,6 @@ class ReinforcementLearner(BaseReinforcementLearningModel): """ # first, penalize if the action is not valid if not self._is_valid(action): - self.custom_info["Invalid"] += 1 return -2 pnl = self.get_unrealized_profit() @@ -109,15 +108,15 @@ class ReinforcementLearner(BaseReinforcementLearningModel): # reward agent for entering trades if (action == Actions.Long_enter.value and self._position == Positions.Neutral): - self.custom_info[f"{Actions.Long_enter.name}"] += 1 + self.custom_info[Actions.Long_enter.name] += 1 return 25 if (action == Actions.Short_enter.value and self._position == Positions.Neutral): - self.custom_info[f"{Actions.Short_enter.name}"] += 1 + self.custom_info[Actions.Short_enter.name] += 1 return 25 # discourage agent from not entering trades if action == Actions.Neutral.value and self._position == Positions.Neutral: - self.custom_info[f"{Actions.Neutral.name}"] += 1 + self.custom_info[Actions.Neutral.name] += 1 return -1 max_trade_duration = self.rl_config.get('max_trade_duration_candles', 300) @@ -131,22 +130,21 @@ class ReinforcementLearner(BaseReinforcementLearningModel): # discourage sitting in position if (self._position in (Positions.Short, Positions.Long) and action == Actions.Neutral.value): - self.custom_info["Hold"] += 1 + self.custom_info[Actions.Neutral.name] += 1 return -1 * trade_duration / max_trade_duration # close long if action == Actions.Long_exit.value and self._position == Positions.Long: if pnl > self.profit_aim * self.rr: factor *= self.rl_config['model_reward_parameters'].get('win_reward_factor', 2) - self.custom_info[f"{Actions.Long_exit.name}"] += 1 + self.custom_info[Actions.Long_exit.name] += 1 return float(pnl * factor) # close short if action == Actions.Short_exit.value and self._position == Positions.Short: if pnl > self.profit_aim * self.rr: factor *= self.rl_config['model_reward_parameters'].get('win_reward_factor', 2) - self.custom_info[f"{Actions.Short_exit.name}"] += 1 + self.custom_info[Actions.Short_exit.name] += 1 return float(pnl * factor) - self.custom_info["Unknown"] += 1 return 0. From 199fd2d074a7d02c5f5ddad205ca59032591d519 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Wed, 7 Dec 2022 15:08:33 +0100 Subject: [PATCH 021/795] +Remote Pairlist --- freqtrade/constants.py | 2 +- freqtrade/plugins/pairlist/RemotePairlist.py | 152 +++++++++++++++++++ 2 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 freqtrade/plugins/pairlist/RemotePairlist.py diff --git a/freqtrade/constants.py b/freqtrade/constants.py index d869b89f6..dba277916 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -31,7 +31,7 @@ HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss', 'CalmarHyperOptLoss', 'MaxDrawDownHyperOptLoss', 'MaxDrawDownRelativeHyperOptLoss', 'ProfitDrawDownHyperOptLoss'] -AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', 'ProducerPairList', +AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', 'ProducerPairList', 'RemotePairlist', 'AgeFilter', 'OffsetFilter', 'PerformanceFilter', 'PrecisionFilter', 'PriceFilter', 'RangeStabilityFilter', 'ShuffleFilter', 'SpreadFilter', 'VolatilityFilter'] diff --git a/freqtrade/plugins/pairlist/RemotePairlist.py b/freqtrade/plugins/pairlist/RemotePairlist.py new file mode 100644 index 000000000..3b1b56069 --- /dev/null +++ b/freqtrade/plugins/pairlist/RemotePairlist.py @@ -0,0 +1,152 @@ +""" +Remote PairList provider + +Provides dynamic pair list based on trade volumes +""" +import json +import logging +from typing import Any, Dict, List + +import requests +from cachetools import TTLCache + +from freqtrade.constants import Config +from freqtrade.exceptions import OperationalException +from freqtrade.exchange.types import Tickers +from freqtrade.plugins.pairlist.IPairList import IPairList + + +logger = logging.getLogger(__name__) + + +class RemotePairlist(IPairList): + + def __init__(self, exchange, pairlistmanager, + config: Config, pairlistconfig: Dict[str, Any], + pairlist_pos: int) -> None: + super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + + if 'number_assets' not in self._pairlistconfig: + raise OperationalException( + '`number_assets` not specified. Please check your configuration ' + 'for "pairlist.config.number_assets"') + + self._number_pairs = self._pairlistconfig['number_assets'] + self._refresh_period = self._pairlistconfig.get('refresh_period', 1800) + self._keep_pairlist_on_failure = self._pairlistconfig.get('keep_pairlist_on_failure', True) + self._pair_cache: TTLCache = TTLCache(maxsize=1, ttl=self._refresh_period) + self._pairlist_url = self._pairlistconfig.get('pairlist_url', + 'http://pairlist.robot.co.network') + self._stake_currency = config['stake_currency'] + + if (self._refresh_period < 850): + raise OperationalException( + 'Please set a Refresh Period higher than 850 for the Remotepairlist.' + ) + + @property + def needstickers(self) -> bool: + """ + Boolean property defining if tickers are necessary. + If no Pairlist requires tickers, an empty Dict is passed + as tickers argument to filter_pairlist + """ + return False + + def short_desc(self) -> str: + """ + Short whitelist method description - used for startup-messages + """ + return f"{self.name} - {self._pairlistconfig['number_assets']} pairs from Remote." + + def gen_pairlist(self, tickers: Tickers) -> List[str]: + """ + Generate the pairlist + :param tickers: Tickers (from exchange.get_tickers). May be cached. + :return: List of pairs + """ + hick = "'" + double = '"' + # Generate dynamic whitelist + # Must always run if this pairlist is not the first in the list. + pairlist = self._pair_cache.get('pairlist') + + if pairlist: + # Item found - no refresh necessary + return pairlist.copy() + else: + + headers = { + 'User-Agent': 'Freqtrade Pairlist Fetcher', + } + + if "limit" not in self._pairlist_url: + url = self._pairlist_url + "&limit=" + str(self._number_pairs) + else: + url = self._pairlist_url + + if "stake" not in self._pairlist_url: + url = self._pairlist_url + "&stake=" + str(self._config['stake_currency']) + else: + url = self._pairlist_url + + if "exchange" not in self._pairlist_url: + url = self._pairlist_url + "&exchange=" + str(self._config['exchange']) + else: + url = self._pairlist_url + + try: + response = requests.get(url, headers=headers, timeout=60) + responser = response.text.replace(hick, double) + time_elapsed = response.elapsed.total_seconds() + rsplit = responser.split("#") + plist = rsplit[0].strip() + plist = plist.replace("
", "") + plist = json.loads(plist) + info = rsplit[1].strip() + + except Exception as e: + print(e) + self.log_once(f'Was not able to receive pairlist from' + f' {self._pairlist_url}', logger.info) + + if self._keep_pairlist_on_failure: + plist = pairlist + else: + plist = "" + + + pairlist = [] + + for i in plist: + if i not in pairlist: + if "/" in i: + if self._stake_currency in i: + pairlist.append(i) + else: + continue + else: + pairlist.append(i + "/" + self._config['stake_currency']) + + pairlist = self.filter_pairlist(pairlist, tickers) + self._pair_cache['pairlist'] = pairlist.copy() + self.log_once(info + " | " + "Fetched in " + str(time_elapsed) + " seconds.", logger.info) + return pairlist + + def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: + """ + Filters and sorts pairlist and returns the whitelist again. + Called on each bot iteration - please use internal caching if necessary + :param pairlist: pairlist to filter or sort + :param tickers: Tickers (from exchange.get_tickers). May be cached. + :return: new whitelist + """ + + # Validate whitelist to only have active market pairs + pairlist = self._whitelist_for_active_markets(pairlist) + pairlist = self.verify_blacklist(pairlist, logger.info) + # Limit pairlist to the requested number of pairs + pairlist = pairlist[:self._number_pairs] + self.log_once(f"Searching {self._number_pairs} pairs: {pairlist}", logger.info) + + return pairlist From 48160f3fe9b099aa0c286fc78efcc5971186a323 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Wed, 7 Dec 2022 17:01:45 +0100 Subject: [PATCH 022/795] Flake 8 fix, Json Fetching --- freqtrade/constants.py | 2 +- freqtrade/plugins/pairlist/RemotePairList.py | 146 +++++++++++++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 freqtrade/plugins/pairlist/RemotePairList.py diff --git a/freqtrade/constants.py b/freqtrade/constants.py index dba277916..e2eccfed3 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -31,7 +31,7 @@ HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss', 'CalmarHyperOptLoss', 'MaxDrawDownHyperOptLoss', 'MaxDrawDownRelativeHyperOptLoss', 'ProfitDrawDownHyperOptLoss'] -AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', 'ProducerPairList', 'RemotePairlist', +AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', 'ProducerPairList', 'RemotePairList', 'AgeFilter', 'OffsetFilter', 'PerformanceFilter', 'PrecisionFilter', 'PriceFilter', 'RangeStabilityFilter', 'ShuffleFilter', 'SpreadFilter', 'VolatilityFilter'] diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py new file mode 100644 index 000000000..684e68a1b --- /dev/null +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -0,0 +1,146 @@ +""" +Remote PairList provider + +Provides pair list fetched from a remote source +""" +import json +import logging +from typing import Any, Dict, List + +import requests +from cachetools import TTLCache + +from freqtrade.constants import Config +from freqtrade.exceptions import OperationalException +from freqtrade.exchange.types import Tickers +from freqtrade.plugins.pairlist.IPairList import IPairList + + +logger = logging.getLogger(__name__) + + +class RemotePairList(IPairList): + + def __init__(self, exchange, pairlistmanager, + config: Config, pairlistconfig: Dict[str, Any], + pairlist_pos: int) -> None: + super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + + if 'number_assets' not in self._pairlistconfig: + raise OperationalException( + '`number_assets` not specified. Please check your configuration ' + 'for "pairlist.config.number_assets"') + + if 'pairlist_url' not in self._pairlistconfig: + raise OperationalException( + '`pairlist_url` not specified. Please check your configuration ' + 'for "pairlist.config.pairlist_url"') + + self._number_pairs = self._pairlistconfig['number_assets'] + self._refresh_period = self._pairlistconfig.get('refresh_period', 1800) + self._keep_pairlist_on_failure = self._pairlistconfig.get('keep_pairlist_on_failure', True) + self._pair_cache: TTLCache = TTLCache(maxsize=1, ttl=self._refresh_period) + self._pairlist_url = self._pairlistconfig.get('pairlist_url', '') + self._read_timeout = self._pairlistconfig.get('read_timeout', 60) + self._last_pairlist: List[Any] = list() + + @property + def needstickers(self) -> bool: + """ + Boolean property defining if tickers are necessary. + If no Pairlist requires tickers, an empty Dict is passed + as tickers argument to filter_pairlist + """ + return False + + def short_desc(self) -> str: + """ + Short whitelist method description - used for startup-messages + """ + return f"{self.name} - {self._pairlistconfig['number_assets']} pairs from RemotePairlist." + + def gen_pairlist(self, tickers: Tickers) -> List[str]: + """ + Generate the pairlist + :param tickers: Tickers (from exchange.get_tickers). May be cached. + :return: List of pairs + """ + pairlist = self._pair_cache.get('pairlist') + info = "" + + if pairlist: + # Item found - no refresh necessary + return pairlist.copy() + else: + # Fetch Pairlist from Remote + headers = { + 'User-Agent': 'Freqtrade - Remotepairlist', + } + + try: + response = requests.get(self._pairlist_url, headers=headers, + timeout=self._read_timeout) + content_type = response.headers.get('content-type') + time_elapsed = response.elapsed.total_seconds() + + rsplit = response.text.split("#") + + if "text/html" in str(content_type): + if len(rsplit) > 1: + plist = rsplit[0].strip() + plist = json.loads(plist) + info = rsplit[1].strip() + else: + plist = json.loads(rsplit[0]) + elif "application/json" in str(content_type): + jsonp = json.loads(' '.join(rsplit)) + plist = jsonp['pairs'] + info = jsonp['info'] + + except requests.exceptions.RequestException: + self.log_once(f'Was not able to fetch pairlist from:' + f' {self._pairlist_url}', logger.info) + + if self._keep_pairlist_on_failure: + plist = str(self._last_pairlist) + self.log_once('Keeping last fetched pairlist', logger.info) + else: + plist = "" + + time_elapsed = 0 + + pairlist = [] + + for i in plist: + if i not in pairlist: + pairlist.append(i) + else: + continue + + pairlist = self.filter_pairlist(pairlist, tickers) + self._pair_cache['pairlist'] = pairlist.copy() + + if(time_elapsed): + self.log_once(info + " | " + " Fetched in " + str(time_elapsed) + + " seconds.", logger.info) + + self._last_pairlist = list(pairlist) + return pairlist + + def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: + """ + Filters and sorts pairlist and returns the whitelist again. + Called on each bot iteration - please use internal caching if necessary + :param pairlist: pairlist to filter or sort + :param tickers: Tickers (from exchange.get_tickers). May be cached. + :return: new whitelist + """ + + # Validate whitelist to only have active market pairs + pairlist = self._whitelist_for_active_markets(pairlist) + pairlist = self.verify_blacklist(pairlist, logger.info) + # Limit pairlist to the requested number of pairs + pairlist = pairlist[:self._number_pairs] + self.log_once(f"Searching {self._number_pairs} pairs: {pairlist}", logger.info) + + return pairlist From 607d5b2f8f0e870c34fe3bdee2c8fe6cff4af37c Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Wed, 7 Dec 2022 17:47:38 +0100 Subject: [PATCH 023/795] Split to fetch_pairlist function, Info Message --- freqtrade/plugins/pairlist/RemotePairList.py | 87 +++++++++++--------- 1 file changed, 47 insertions(+), 40 deletions(-) diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index 684e68a1b..b6d0abe35 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -59,6 +59,49 @@ class RemotePairList(IPairList): """ return f"{self.name} - {self._pairlistconfig['number_assets']} pairs from RemotePairlist." + def fetch_pairlist(self): + headers = { + 'User-Agent': 'Freqtrade - Remotepairlist', + } + + try: + response = requests.get(self._pairlist_url, headers=headers, + timeout=self._read_timeout) + content_type = response.headers.get('content-type') + time_elapsed = response.elapsed.total_seconds() + + rsplit = response.text.split("#") + + if "text/html" in str(content_type): + if len(rsplit) > 1: + plist = rsplit[0].strip() + plist = json.loads(plist) + info = rsplit[1].strip() + else: + plist = json.loads(rsplit[0]) + elif "application/json" in str(content_type): + jsonr = response.json() + plist = jsonr['pairs'] + + if 'info' in jsonr: + info = jsonr['info'] + if 'refresh_period' in jsonr: + self._refresh_period = jsonr['refresh_period'] + + except requests.exceptions.RequestException: + self.log_once(f'Was not able to fetch pairlist from:' + f' {self._pairlist_url}', logger.info) + + if self._keep_pairlist_on_failure: + plist = str(self._last_pairlist) + self.log_once('Keeping last fetched pairlist', logger.info) + else: + plist = "" + + time_elapsed = 0 + + return plist, time_elapsed, info + def gen_pairlist(self, tickers: Tickers) -> List[str]: """ Generate the pairlist @@ -66,49 +109,14 @@ class RemotePairList(IPairList): :return: List of pairs """ pairlist = self._pair_cache.get('pairlist') - info = "" + info = "Pairlist" if pairlist: # Item found - no refresh necessary return pairlist.copy() else: - # Fetch Pairlist from Remote - headers = { - 'User-Agent': 'Freqtrade - Remotepairlist', - } - - try: - response = requests.get(self._pairlist_url, headers=headers, - timeout=self._read_timeout) - content_type = response.headers.get('content-type') - time_elapsed = response.elapsed.total_seconds() - - rsplit = response.text.split("#") - - if "text/html" in str(content_type): - if len(rsplit) > 1: - plist = rsplit[0].strip() - plist = json.loads(plist) - info = rsplit[1].strip() - else: - plist = json.loads(rsplit[0]) - elif "application/json" in str(content_type): - jsonp = json.loads(' '.join(rsplit)) - plist = jsonp['pairs'] - info = jsonp['info'] - - except requests.exceptions.RequestException: - self.log_once(f'Was not able to fetch pairlist from:' - f' {self._pairlist_url}', logger.info) - - if self._keep_pairlist_on_failure: - plist = str(self._last_pairlist) - self.log_once('Keeping last fetched pairlist', logger.info) - else: - plist = "" - - time_elapsed = 0 - + # Fetch Pairlist from Remote URL + plist, time_elapsed, info = self.fetch_pairlist() pairlist = [] for i in plist: @@ -121,8 +129,7 @@ class RemotePairList(IPairList): self._pair_cache['pairlist'] = pairlist.copy() if(time_elapsed): - self.log_once(info + " | " + " Fetched in " + str(time_elapsed) + - " seconds.", logger.info) + self.log_once(f'{info} Fetched in {time_elapsed} seconds.', logger.info) self._last_pairlist = list(pairlist) return pairlist From 547a75d9c1abc42db10b811c152147a66d48a6af Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Wed, 7 Dec 2022 17:49:21 +0100 Subject: [PATCH 024/795] Fix Info --- freqtrade/plugins/pairlist/RemotePairList.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index b6d0abe35..07829d246 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -64,6 +64,8 @@ class RemotePairList(IPairList): 'User-Agent': 'Freqtrade - Remotepairlist', } + info = "Pairlist" + try: response = requests.get(self._pairlist_url, headers=headers, timeout=self._read_timeout) @@ -109,7 +111,6 @@ class RemotePairList(IPairList): :return: List of pairs """ pairlist = self._pair_cache.get('pairlist') - info = "Pairlist" if pairlist: # Item found - no refresh necessary From b144a6357d7cbafa1ab7ded091f6e5ad79a78027 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Wed, 7 Dec 2022 18:24:55 +0100 Subject: [PATCH 025/795] Remove Duplicate --- freqtrade/plugins/pairlist/RemotePairlist.py | 152 ------------------- 1 file changed, 152 deletions(-) delete mode 100644 freqtrade/plugins/pairlist/RemotePairlist.py diff --git a/freqtrade/plugins/pairlist/RemotePairlist.py b/freqtrade/plugins/pairlist/RemotePairlist.py deleted file mode 100644 index 3b1b56069..000000000 --- a/freqtrade/plugins/pairlist/RemotePairlist.py +++ /dev/null @@ -1,152 +0,0 @@ -""" -Remote PairList provider - -Provides dynamic pair list based on trade volumes -""" -import json -import logging -from typing import Any, Dict, List - -import requests -from cachetools import TTLCache - -from freqtrade.constants import Config -from freqtrade.exceptions import OperationalException -from freqtrade.exchange.types import Tickers -from freqtrade.plugins.pairlist.IPairList import IPairList - - -logger = logging.getLogger(__name__) - - -class RemotePairlist(IPairList): - - def __init__(self, exchange, pairlistmanager, - config: Config, pairlistconfig: Dict[str, Any], - pairlist_pos: int) -> None: - super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) - - if 'number_assets' not in self._pairlistconfig: - raise OperationalException( - '`number_assets` not specified. Please check your configuration ' - 'for "pairlist.config.number_assets"') - - self._number_pairs = self._pairlistconfig['number_assets'] - self._refresh_period = self._pairlistconfig.get('refresh_period', 1800) - self._keep_pairlist_on_failure = self._pairlistconfig.get('keep_pairlist_on_failure', True) - self._pair_cache: TTLCache = TTLCache(maxsize=1, ttl=self._refresh_period) - self._pairlist_url = self._pairlistconfig.get('pairlist_url', - 'http://pairlist.robot.co.network') - self._stake_currency = config['stake_currency'] - - if (self._refresh_period < 850): - raise OperationalException( - 'Please set a Refresh Period higher than 850 for the Remotepairlist.' - ) - - @property - def needstickers(self) -> bool: - """ - Boolean property defining if tickers are necessary. - If no Pairlist requires tickers, an empty Dict is passed - as tickers argument to filter_pairlist - """ - return False - - def short_desc(self) -> str: - """ - Short whitelist method description - used for startup-messages - """ - return f"{self.name} - {self._pairlistconfig['number_assets']} pairs from Remote." - - def gen_pairlist(self, tickers: Tickers) -> List[str]: - """ - Generate the pairlist - :param tickers: Tickers (from exchange.get_tickers). May be cached. - :return: List of pairs - """ - hick = "'" - double = '"' - # Generate dynamic whitelist - # Must always run if this pairlist is not the first in the list. - pairlist = self._pair_cache.get('pairlist') - - if pairlist: - # Item found - no refresh necessary - return pairlist.copy() - else: - - headers = { - 'User-Agent': 'Freqtrade Pairlist Fetcher', - } - - if "limit" not in self._pairlist_url: - url = self._pairlist_url + "&limit=" + str(self._number_pairs) - else: - url = self._pairlist_url - - if "stake" not in self._pairlist_url: - url = self._pairlist_url + "&stake=" + str(self._config['stake_currency']) - else: - url = self._pairlist_url - - if "exchange" not in self._pairlist_url: - url = self._pairlist_url + "&exchange=" + str(self._config['exchange']) - else: - url = self._pairlist_url - - try: - response = requests.get(url, headers=headers, timeout=60) - responser = response.text.replace(hick, double) - time_elapsed = response.elapsed.total_seconds() - rsplit = responser.split("#") - plist = rsplit[0].strip() - plist = plist.replace("
", "") - plist = json.loads(plist) - info = rsplit[1].strip() - - except Exception as e: - print(e) - self.log_once(f'Was not able to receive pairlist from' - f' {self._pairlist_url}', logger.info) - - if self._keep_pairlist_on_failure: - plist = pairlist - else: - plist = "" - - - pairlist = [] - - for i in plist: - if i not in pairlist: - if "/" in i: - if self._stake_currency in i: - pairlist.append(i) - else: - continue - else: - pairlist.append(i + "/" + self._config['stake_currency']) - - pairlist = self.filter_pairlist(pairlist, tickers) - self._pair_cache['pairlist'] = pairlist.copy() - self.log_once(info + " | " + "Fetched in " + str(time_elapsed) + " seconds.", logger.info) - return pairlist - - def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: - """ - Filters and sorts pairlist and returns the whitelist again. - Called on each bot iteration - please use internal caching if necessary - :param pairlist: pairlist to filter or sort - :param tickers: Tickers (from exchange.get_tickers). May be cached. - :return: new whitelist - """ - - # Validate whitelist to only have active market pairs - pairlist = self._whitelist_for_active_markets(pairlist) - pairlist = self.verify_blacklist(pairlist, logger.info) - # Limit pairlist to the requested number of pairs - pairlist = pairlist[:self._number_pairs] - self.log_once(f"Searching {self._number_pairs} pairs: {pairlist}", logger.info) - - return pairlist From da2747d487ced9129a3b3ae8336e6d7533da5132 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Thu, 8 Dec 2022 00:52:54 +0100 Subject: [PATCH 026/795] Add Local .json file Loading --- freqtrade/plugins/pairlist/RemotePairList.py | 30 +++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index 07829d246..c3b612067 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -5,6 +5,7 @@ Provides pair list fetched from a remote source """ import json import logging +from pathlib import Path from typing import Any, Dict, List import requests @@ -110,21 +111,36 @@ class RemotePairList(IPairList): :param tickers: Tickers (from exchange.get_tickers). May be cached. :return: List of pairs """ + + time_elapsed = 0 pairlist = self._pair_cache.get('pairlist') if pairlist: # Item found - no refresh necessary return pairlist.copy() else: - # Fetch Pairlist from Remote URL - plist, time_elapsed, info = self.fetch_pairlist() - pairlist = [] + if self._pairlist_url.startswith("file:///"): + filename = self._pairlist_url.split("file:///", 1)[1] + file_path = Path(filename) - for i in plist: - if i not in pairlist: - pairlist.append(i) + if file_path.exists(): + with open(filename) as json_file: + # Load the JSON data into a dictionary + jsonp = json.load(json_file) + plist = jsonp['pairs'] else: - continue + raise ValueError(f"{self._pairlist_url} does not exist.") + else: + # Fetch Pairlist from Remote URL + plist, time_elapsed, info = self.fetch_pairlist() + + pairlist = [] + + for i in plist: + if i not in pairlist: + pairlist.append(i) + else: + continue pairlist = self.filter_pairlist(pairlist, tickers) self._pair_cache['pairlist'] = pairlist.copy() From 7efcbbb4573c3a5ff75cca0fc892cdc6a743e779 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Thu, 8 Dec 2022 01:09:17 +0100 Subject: [PATCH 027/795] Local File Loading --- freqtrade/plugins/pairlist/RemotePairList.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index c3b612067..af8b67577 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -83,13 +83,11 @@ class RemotePairList(IPairList): else: plist = json.loads(rsplit[0]) elif "application/json" in str(content_type): - jsonr = response.json() - plist = jsonr['pairs'] + jsonp = response.json() + plist = jsonp['pairs'] - if 'info' in jsonr: - info = jsonr['info'] - if 'refresh_period' in jsonr: - self._refresh_period = jsonr['refresh_period'] + info = jsonp.get('info', '') + self._refresh_period = jsonp.get('refresh_period', self._refresh_period) except requests.exceptions.RequestException: self.log_once(f'Was not able to fetch pairlist from:' @@ -128,6 +126,10 @@ class RemotePairList(IPairList): # Load the JSON data into a dictionary jsonp = json.load(json_file) plist = jsonp['pairs'] + + info = jsonp.get('info', '') + self._refresh_period = jsonp.get('refresh_period', self._refresh_period) + else: raise ValueError(f"{self._pairlist_url} does not exist.") else: @@ -145,8 +147,10 @@ class RemotePairList(IPairList): pairlist = self.filter_pairlist(pairlist, tickers) self._pair_cache['pairlist'] = pairlist.copy() - if(time_elapsed): + if (time_elapsed) in locals(): self.log_once(f'{info} Fetched in {time_elapsed} seconds.', logger.info) + else: + self.log_once(f'{info} Fetched Pairlist.', logger.info) self._last_pairlist = list(pairlist) return pairlist From 66412bfa58645177ebcef18c4c8ecf4a875527c2 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Thu, 8 Dec 2022 01:51:12 +0100 Subject: [PATCH 028/795] Remove unnecessary loop --- freqtrade/plugins/pairlist/RemotePairList.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index af8b67577..7367f713c 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -110,8 +110,8 @@ class RemotePairList(IPairList): :return: List of pairs """ - time_elapsed = 0 pairlist = self._pair_cache.get('pairlist') + time_elapsed = 0 if pairlist: # Item found - no refresh necessary @@ -125,7 +125,7 @@ class RemotePairList(IPairList): with open(filename) as json_file: # Load the JSON data into a dictionary jsonp = json.load(json_file) - plist = jsonp['pairs'] + pairlist = jsonp['pairs'] info = jsonp.get('info', '') self._refresh_period = jsonp.get('refresh_period', self._refresh_period) @@ -134,20 +134,12 @@ class RemotePairList(IPairList): raise ValueError(f"{self._pairlist_url} does not exist.") else: # Fetch Pairlist from Remote URL - plist, time_elapsed, info = self.fetch_pairlist() - - pairlist = [] - - for i in plist: - if i not in pairlist: - pairlist.append(i) - else: - continue + pairlist, time_elapsed, info = self.fetch_pairlist() pairlist = self.filter_pairlist(pairlist, tickers) self._pair_cache['pairlist'] = pairlist.copy() - if (time_elapsed) in locals(): + if time_elapsed: self.log_once(f'{info} Fetched in {time_elapsed} seconds.', logger.info) else: self.log_once(f'{info} Fetched Pairlist.', logger.info) From 980a5a9b521d1a905a7beae383fd9ff8a8fd5302 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 10 Dec 2022 19:54:04 +0100 Subject: [PATCH 029/795] Fix docs typo --- freqtrade/plugins/pairlist/VolumePairList.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index ad27a93d8..be58ec1a1 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -218,7 +218,7 @@ class VolumePairList(IPairList): else: filtered_tickers[i]['quoteVolume'] = 0 else: - # Tickers mode - filter based on incomming pairlist. + # Tickers mode - filter based on incoming pairlist. filtered_tickers = [v for k, v in tickers.items() if k in pairlist] if self._min_value > 0: From 6b9f3f279587e1097915732ad3ac6e69c00c9bb5 Mon Sep 17 00:00:00 2001 From: Emre Date: Sun, 11 Dec 2022 13:24:24 +0300 Subject: [PATCH 030/795] Fix test validation --- freqtrade/configuration/config_validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index 7e291cb90..606f081ef 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -360,7 +360,7 @@ def _validate_freqai_include_timeframes(conf: Dict[str, Any]) -> None: feature_parameters = conf.get('freqai', {}).get('feature_parameters', {}) include_timeframes = [main_tf] + freqai_include_timeframes conf.get('freqai', {}).get('feature_parameters', {}) \ - .update({'include_timeframes': include_timeframes, **feature_parameters}) + .update({**feature_parameters, 'include_timeframes': include_timeframes}) def _validate_freqai_backtest(conf: Dict[str, Any]) -> None: From 85f22b5c3029a3f613d0b0da7b61eeef8f6685d5 Mon Sep 17 00:00:00 2001 From: robcaulk Date: Sun, 11 Dec 2022 12:15:19 +0100 Subject: [PATCH 031/795] fix bug in MultiOutput* with conv_width = 1 --- freqtrade/freqai/base_models/BaseClassifierModel.py | 3 +++ freqtrade/freqai/base_models/BaseRegressionModel.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/freqtrade/freqai/base_models/BaseClassifierModel.py b/freqtrade/freqai/base_models/BaseClassifierModel.py index 17bffa85b..a5cea879f 100644 --- a/freqtrade/freqai/base_models/BaseClassifierModel.py +++ b/freqtrade/freqai/base_models/BaseClassifierModel.py @@ -95,6 +95,9 @@ class BaseClassifierModel(IFreqaiModel): self.data_cleaning_predict(dk) predictions = self.model.predict(dk.data_dictionary["prediction_features"]) + if self.CONV_WIDTH == 1: + predictions = np.reshape(predictions, (-1, len(dk.label_list))) + pred_df = DataFrame(predictions, columns=dk.label_list) predictions_prob = self.model.predict_proba(dk.data_dictionary["prediction_features"]) diff --git a/freqtrade/freqai/base_models/BaseRegressionModel.py b/freqtrade/freqai/base_models/BaseRegressionModel.py index 766579cb6..1f9b4f5a6 100644 --- a/freqtrade/freqai/base_models/BaseRegressionModel.py +++ b/freqtrade/freqai/base_models/BaseRegressionModel.py @@ -95,6 +95,9 @@ class BaseRegressionModel(IFreqaiModel): self.data_cleaning_predict(dk) predictions = self.model.predict(dk.data_dictionary["prediction_features"]) + if self.CONV_WIDTH == 1: + predictions = np.reshape(predictions, (-1, len(dk.label_list))) + pred_df = DataFrame(predictions, columns=dk.label_list) pred_df = dk.denormalize_labels_from_metadata(pred_df) From 8c7ec07951eadf53a5722fe7d7489e9a95e5ab46 Mon Sep 17 00:00:00 2001 From: robcaulk Date: Sun, 11 Dec 2022 12:39:31 +0100 Subject: [PATCH 032/795] ensure predict_proba follows suit. Remove all lib specific params from example config --- config_examples/config_freqai.example.json | 1 - freqtrade/freqai/base_models/BaseClassifierModel.py | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/config_examples/config_freqai.example.json b/config_examples/config_freqai.example.json index 5e564a1fc..f58a4468b 100644 --- a/config_examples/config_freqai.example.json +++ b/config_examples/config_freqai.example.json @@ -80,7 +80,6 @@ "random_state": 1 }, "model_training_parameters": { - "n_estimators": 1000 } }, "bot_name": "", diff --git a/freqtrade/freqai/base_models/BaseClassifierModel.py b/freqtrade/freqai/base_models/BaseClassifierModel.py index a5cea879f..ffd42dd1d 100644 --- a/freqtrade/freqai/base_models/BaseClassifierModel.py +++ b/freqtrade/freqai/base_models/BaseClassifierModel.py @@ -101,6 +101,8 @@ class BaseClassifierModel(IFreqaiModel): pred_df = DataFrame(predictions, columns=dk.label_list) predictions_prob = self.model.predict_proba(dk.data_dictionary["prediction_features"]) + if self.CONV_WIDTH == 1: + predictions_prob = np.reshape(predictions_prob, (-1, len(self.model.classes_))) pred_df_prob = DataFrame(predictions_prob, columns=self.model.classes_) pred_df = pd.concat([pred_df, pred_df_prob], axis=1) From cb8fc3c8c7c392b75493d8da7f748760372040a9 Mon Sep 17 00:00:00 2001 From: initrv Date: Sun, 11 Dec 2022 15:37:45 +0300 Subject: [PATCH 033/795] custom info to tensorboard_metrics --- freqtrade/freqai/RL/Base4ActionRLEnv.py | 2 +- freqtrade/freqai/RL/Base5ActionRLEnv.py | 1 + freqtrade/freqai/RL/BaseEnvironment.py | 8 ++++---- freqtrade/freqai/RL/TensorboardCallback.py | 8 ++++---- .../freqai/prediction_models/ReinforcementLearner.py | 6 ------ 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/freqtrade/freqai/RL/Base4ActionRLEnv.py b/freqtrade/freqai/RL/Base4ActionRLEnv.py index 79616d778..02e182bbd 100644 --- a/freqtrade/freqai/RL/Base4ActionRLEnv.py +++ b/freqtrade/freqai/RL/Base4ActionRLEnv.py @@ -46,9 +46,9 @@ class Base4ActionRLEnv(BaseEnvironment): self._done = True self._update_unrealized_total_profit() - step_reward = self.calculate_reward(action) self.total_reward += step_reward + self.tensorboard_metrics[self.actions._member_names_[action]] += 1 trade_type = None if self.is_tradesignal(action): diff --git a/freqtrade/freqai/RL/Base5ActionRLEnv.py b/freqtrade/freqai/RL/Base5ActionRLEnv.py index 1c09f9386..baf7dde9f 100644 --- a/freqtrade/freqai/RL/Base5ActionRLEnv.py +++ b/freqtrade/freqai/RL/Base5ActionRLEnv.py @@ -49,6 +49,7 @@ class Base5ActionRLEnv(BaseEnvironment): self._update_unrealized_total_profit() step_reward = self.calculate_reward(action) self.total_reward += step_reward + self.tensorboard_metrics[self.actions._member_names_[action]] += 1 trade_type = None if self.is_tradesignal(action): diff --git a/freqtrade/freqai/RL/BaseEnvironment.py b/freqtrade/freqai/RL/BaseEnvironment.py index 71b423844..0da13db7c 100644 --- a/freqtrade/freqai/RL/BaseEnvironment.py +++ b/freqtrade/freqai/RL/BaseEnvironment.py @@ -77,7 +77,7 @@ class BaseEnvironment(gym.Env): # set here to default 5Ac, but all children envs can override this self.actions: Type[Enum] = BaseActions - self.custom_info: dict = {} + self.tensorboard_metrics: dict = {} def reset_env(self, df: DataFrame, prices: DataFrame, window_size: int, reward_kwargs: dict, starting_point=True): @@ -136,10 +136,10 @@ class BaseEnvironment(gym.Env): """ Reset is called at the beginning of every episode """ - # custom_info is used for episodic reports and tensorboard logging - self.custom_info: dict = {} + # tensorboard_metrics is used for episodic reports and tensorboard logging + self.tensorboard_metrics: dict = {} for action in self.actions: - self.custom_info[action.name] = 0 + self.tensorboard_metrics[action.name] = 0 self._done = False diff --git a/freqtrade/freqai/RL/TensorboardCallback.py b/freqtrade/freqai/RL/TensorboardCallback.py index d03c040d4..b596742e9 100644 --- a/freqtrade/freqai/RL/TensorboardCallback.py +++ b/freqtrade/freqai/RL/TensorboardCallback.py @@ -44,16 +44,16 @@ class TensorboardCallback(BaseCallback): def _on_step(self) -> bool: local_info = self.locals["infos"][0] - custom_info = self.training_env.get_attr("custom_info")[0] + tensorboard_metrics = self.training_env.get_attr("tensorboard_metrics")[0] for info in local_info: if info not in ["episode", "terminal_observation"]: self.logger.record(f"_info/{info}", local_info[info]) - for info in custom_info: + for info in tensorboard_metrics: if info in [action.name for action in self.actions]: - self.logger.record(f"_actions/{info}", custom_info[info]) + self.logger.record(f"_actions/{info}", tensorboard_metrics[info]) else: - self.logger.record(f"_custom/{info}", custom_info[info]) + self.logger.record(f"_custom/{info}", tensorboard_metrics[info]) return True diff --git a/freqtrade/freqai/prediction_models/ReinforcementLearner.py b/freqtrade/freqai/prediction_models/ReinforcementLearner.py index 1383ad15e..e015b138a 100644 --- a/freqtrade/freqai/prediction_models/ReinforcementLearner.py +++ b/freqtrade/freqai/prediction_models/ReinforcementLearner.py @@ -108,15 +108,12 @@ class ReinforcementLearner(BaseReinforcementLearningModel): # reward agent for entering trades if (action == Actions.Long_enter.value and self._position == Positions.Neutral): - self.custom_info[Actions.Long_enter.name] += 1 return 25 if (action == Actions.Short_enter.value and self._position == Positions.Neutral): - self.custom_info[Actions.Short_enter.name] += 1 return 25 # discourage agent from not entering trades if action == Actions.Neutral.value and self._position == Positions.Neutral: - self.custom_info[Actions.Neutral.name] += 1 return -1 max_trade_duration = self.rl_config.get('max_trade_duration_candles', 300) @@ -130,21 +127,18 @@ class ReinforcementLearner(BaseReinforcementLearningModel): # discourage sitting in position if (self._position in (Positions.Short, Positions.Long) and action == Actions.Neutral.value): - self.custom_info[Actions.Neutral.name] += 1 return -1 * trade_duration / max_trade_duration # close long if action == Actions.Long_exit.value and self._position == Positions.Long: if pnl > self.profit_aim * self.rr: factor *= self.rl_config['model_reward_parameters'].get('win_reward_factor', 2) - self.custom_info[Actions.Long_exit.name] += 1 return float(pnl * factor) # close short if action == Actions.Short_exit.value and self._position == Positions.Short: if pnl > self.profit_aim * self.rr: factor *= self.rl_config['model_reward_parameters'].get('win_reward_factor', 2) - self.custom_info[Actions.Short_exit.name] += 1 return float(pnl * factor) return 0. From 0fd8e214e4f95a4c2c1929e9b26da43c70fd47dc Mon Sep 17 00:00:00 2001 From: robcaulk Date: Sun, 11 Dec 2022 15:31:29 +0100 Subject: [PATCH 034/795] add documentation for tensorboard_log, change how users interact with tensorboard_log --- docs/freqai-reinforcement-learning.md | 26 +++++++++++++++ freqtrade/freqai/RL/Base4ActionRLEnv.py | 2 +- freqtrade/freqai/RL/Base5ActionRLEnv.py | 2 +- freqtrade/freqai/RL/BaseEnvironment.py | 33 ++++++++++++++++--- .../prediction_models/ReinforcementLearner.py | 1 + 5 files changed, 57 insertions(+), 7 deletions(-) diff --git a/docs/freqai-reinforcement-learning.md b/docs/freqai-reinforcement-learning.md index b1a212a92..b831c90a0 100644 --- a/docs/freqai-reinforcement-learning.md +++ b/docs/freqai-reinforcement-learning.md @@ -247,6 +247,32 @@ where `unique-id` is the `identifier` set in the `freqai` configuration file. Th ![tensorboard](assets/tensorboard.jpg) + +### Custom logging + +FreqAI also provides a built in episodic summary logger called `self.tensorboard_log` for adding custom information to the Tensorboard log. By default, this function is already called once per step inside the environment to record the agent actions. All values accumulated for all steps in a single episode are reported at the conclusion of each episode, followed by a full reset of all metrics to 0 in preparation for the subsequent episode. + + +`self.tensorboard_log` can also be used anywhere inside the environment, for example, it can be added to the `calculate_reward` function to collect more detailed information about how often various parts of the reward were called: + +```py + class MyRLEnv(Base5ActionRLEnv): + """ + User made custom environment. This class inherits from BaseEnvironment and gym.env. + Users can override any functions from those parent classes. Here is an example + of a user customized `calculate_reward()` function. + """ + def calculate_reward(self, action: int) -> float: + if not self._is_valid(action): + self.tensorboard_log("is_valid") + return -2 + +``` + +!!! Note + The `self.tensorboard_log()` function is designed for tracking incremented objects only i.e. events, actions inside the training environment. If the event of interest is a float, the float can be passed as the second argument e.g. `self.tensorboard_log("float_metric1", 0.23)` would add 0.23 to `float_metric`. + + ### Choosing a base environment FreqAI provides two base environments, `Base4ActionEnvironment` and `Base5ActionEnvironment`. As the names imply, the environments are customized for agents that can select from 4 or 5 actions. In the `Base4ActionEnvironment`, the agent can enter long, enter short, hold neutral, or exit position. Meanwhile, in the `Base5ActionEnvironment`, the agent has the same actions as Base4, but instead of a single exit action, it separates exit long and exit short. The main changes stemming from the environment selection include: diff --git a/freqtrade/freqai/RL/Base4ActionRLEnv.py b/freqtrade/freqai/RL/Base4ActionRLEnv.py index 02e182bbd..a3ebfdbfa 100644 --- a/freqtrade/freqai/RL/Base4ActionRLEnv.py +++ b/freqtrade/freqai/RL/Base4ActionRLEnv.py @@ -48,7 +48,7 @@ class Base4ActionRLEnv(BaseEnvironment): self._update_unrealized_total_profit() step_reward = self.calculate_reward(action) self.total_reward += step_reward - self.tensorboard_metrics[self.actions._member_names_[action]] += 1 + self.tensorboard_log(self.actions._member_names_[action]) trade_type = None if self.is_tradesignal(action): diff --git a/freqtrade/freqai/RL/Base5ActionRLEnv.py b/freqtrade/freqai/RL/Base5ActionRLEnv.py index baf7dde9f..22d3cae30 100644 --- a/freqtrade/freqai/RL/Base5ActionRLEnv.py +++ b/freqtrade/freqai/RL/Base5ActionRLEnv.py @@ -49,7 +49,7 @@ class Base5ActionRLEnv(BaseEnvironment): self._update_unrealized_total_profit() step_reward = self.calculate_reward(action) self.total_reward += step_reward - self.tensorboard_metrics[self.actions._member_names_[action]] += 1 + self.tensorboard_log(self.actions._member_names_[action]) trade_type = None if self.is_tradesignal(action): diff --git a/freqtrade/freqai/RL/BaseEnvironment.py b/freqtrade/freqai/RL/BaseEnvironment.py index 0da13db7c..a5cee4def 100644 --- a/freqtrade/freqai/RL/BaseEnvironment.py +++ b/freqtrade/freqai/RL/BaseEnvironment.py @@ -2,7 +2,7 @@ import logging import random from abc import abstractmethod from enum import Enum -from typing import Optional, Type +from typing import Optional, Type, Union import gym import numpy as np @@ -132,14 +132,37 @@ class BaseEnvironment(gym.Env): self.np_random, seed = seeding.np_random(seed) return [seed] + def tensorboard_log(self, metric: str, inc: Union[int, float] = 1): + """ + Function builds the tensorboard_metrics dictionary + to be parsed by the TensorboardCallback. This + function is designed for tracking incremented objects, + events, actions inside the training environment. + For example, a user can call this to track the + frequency of occurence of an `is_valid` call in + their `calculate_reward()`: + + def calculate_reward(self, action: int) -> float: + if not self._is_valid(action): + self.tensorboard_log("is_valid") + return -2 + + :param metric: metric to be tracked and incremented + :param inc: value to increment `metric` by + """ + if metric not in self.tensorboard_metrics: + self.tensorboard_metrics[metric] = inc + else: + self.tensorboard_metrics[metric] += inc + + def reset_tensorboard_log(self): + self.tensorboard_metrics = {} + def reset(self): """ Reset is called at the beginning of every episode """ - # tensorboard_metrics is used for episodic reports and tensorboard logging - self.tensorboard_metrics: dict = {} - for action in self.actions: - self.tensorboard_metrics[action.name] = 0 + self.reset_tensorboard_log() self._done = False diff --git a/freqtrade/freqai/prediction_models/ReinforcementLearner.py b/freqtrade/freqai/prediction_models/ReinforcementLearner.py index e015b138a..38ea67e69 100644 --- a/freqtrade/freqai/prediction_models/ReinforcementLearner.py +++ b/freqtrade/freqai/prediction_models/ReinforcementLearner.py @@ -100,6 +100,7 @@ class ReinforcementLearner(BaseReinforcementLearningModel): """ # first, penalize if the action is not valid if not self._is_valid(action): + self.tensorboard_log("is_valid") return -2 pnl = self.get_unrealized_profit() From 78c40f0535617fc29047262719877e6b151075d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 03:00:40 +0000 Subject: [PATCH 035/795] Bump scikit-learn from 1.1.3 to 1.2.0 Bumps [scikit-learn](https://github.com/scikit-learn/scikit-learn) from 1.1.3 to 1.2.0. - [Release notes](https://github.com/scikit-learn/scikit-learn/releases) - [Commits](https://github.com/scikit-learn/scikit-learn/compare/1.1.3...1.2.0) --- updated-dependencies: - dependency-name: scikit-learn dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-freqai.txt | 2 +- requirements-hyperopt.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements-freqai.txt b/requirements-freqai.txt index 66730e29f..5eafc497b 100644 --- a/requirements-freqai.txt +++ b/requirements-freqai.txt @@ -3,7 +3,7 @@ -r requirements-plot.txt # Required for freqai -scikit-learn==1.1.3 +scikit-learn==1.2.0 joblib==1.2.0 catboost==1.1.1; platform_machine != 'aarch64' lightgbm==3.3.3 diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 4f59ad1fa..83ba62240 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -3,7 +3,7 @@ # Required for hyperopt scipy==1.9.3 -scikit-learn==1.1.3 +scikit-learn==1.2.0 scikit-optimize==0.9.0 filelock==3.8.0 progressbar2==4.2.0 From 434eec73341f9b38e34517b2a63d5125d94eeddb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 03:00:46 +0000 Subject: [PATCH 036/795] Bump blosc from 1.10.6 to 1.11.0 Bumps [blosc](https://github.com/blosc/python-blosc) from 1.10.6 to 1.11.0. - [Release notes](https://github.com/blosc/python-blosc/releases) - [Changelog](https://github.com/Blosc/python-blosc/blob/main/RELEASE_NOTES.rst) - [Commits](https://github.com/blosc/python-blosc/compare/v1.10.6...v1.11.0) --- updated-dependencies: - dependency-name: blosc 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 313e0ff9c..4bd527c90 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,7 +20,7 @@ tabulate==0.9.0 pycoingecko==3.1.0 jinja2==3.1.2 tables==3.7.0 -blosc==1.10.6 +blosc==1.11.0 joblib==1.2.0 pyarrow==10.0.1; platform_machine != 'armv7l' From 63d3a9ced66ecccafd77ec57f20a48b0c427993a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 03:00:49 +0000 Subject: [PATCH 037/795] Bump prompt-toolkit from 3.0.33 to 3.0.36 Bumps [prompt-toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit) from 3.0.33 to 3.0.36. - [Release notes](https://github.com/prompt-toolkit/python-prompt-toolkit/releases) - [Changelog](https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/CHANGELOG) - [Commits](https://github.com/prompt-toolkit/python-prompt-toolkit/compare/3.0.33...3.0.36) --- updated-dependencies: - dependency-name: prompt-toolkit 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 313e0ff9c..1bdcc82ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -47,7 +47,7 @@ psutil==5.9.4 colorama==0.4.6 # Building config files interactively questionary==1.10.0 -prompt-toolkit==3.0.33 +prompt-toolkit==3.0.36 # Extensions to datetime library python-dateutil==2.8.2 From a35111e55e55046504a922a952f90e091f28d49d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 03:00:54 +0000 Subject: [PATCH 038/795] Bump nbconvert from 7.2.5 to 7.2.6 Bumps [nbconvert](https://github.com/jupyter/nbconvert) from 7.2.5 to 7.2.6. - [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.2.5...v7.2.6) --- updated-dependencies: - dependency-name: nbconvert 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 463d2656a..e36419f6c 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -23,7 +23,7 @@ time-machine==2.8.2 httpx==0.23.1 # Convert jupyter notebooks to markdown documents -nbconvert==7.2.5 +nbconvert==7.2.6 # mypy types types-cachetools==5.2.1 From 56256480115c142b00587c6734b78092d4027c3c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 03:01:07 +0000 Subject: [PATCH 039/795] Bump pytest-asyncio from 0.20.2 to 0.20.3 Bumps [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) from 0.20.2 to 0.20.3. - [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases) - [Changelog](https://github.com/pytest-dev/pytest-asyncio/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v0.20.2...v0.20.3) --- updated-dependencies: - dependency-name: pytest-asyncio 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 463d2656a..843337c9b 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -12,7 +12,7 @@ flake8-tidy-imports==4.8.0 mypy==0.991 pre-commit==2.20.0 pytest==7.2.0 -pytest-asyncio==0.20.2 +pytest-asyncio==0.20.3 pytest-cov==4.0.0 pytest-mock==3.10.0 pytest-random-order==1.1.0 From 5a7b493d3ec9ccd557071ad70963041b31417bf7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 03:01:11 +0000 Subject: [PATCH 040/795] Bump xgboost from 1.7.1 to 1.7.2 Bumps [xgboost](https://github.com/dmlc/xgboost) from 1.7.1 to 1.7.2. - [Release notes](https://github.com/dmlc/xgboost/releases) - [Changelog](https://github.com/dmlc/xgboost/blob/master/NEWS.md) - [Commits](https://github.com/dmlc/xgboost/compare/v1.7.1...v1.7.2) --- updated-dependencies: - dependency-name: xgboost dependency-type: direct:production update-type: version-update:semver-patch ... 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 66730e29f..215a312bf 100644 --- a/requirements-freqai.txt +++ b/requirements-freqai.txt @@ -7,5 +7,5 @@ scikit-learn==1.1.3 joblib==1.2.0 catboost==1.1.1; platform_machine != 'aarch64' lightgbm==3.3.3 -xgboost==1.7.1 +xgboost==1.7.2 tensorboard==2.11.0 From 0344203372c84be49a9bd7d3d55c3b3456ce877a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 03:01:32 +0000 Subject: [PATCH 041/795] Bump sqlalchemy from 1.4.44 to 1.4.45 Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 1.4.44 to 1.4.45. - [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases) - [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES.rst) - [Commits](https://github.com/sqlalchemy/sqlalchemy/commits) --- updated-dependencies: - dependency-name: sqlalchemy 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 313e0ff9c..b36225aa6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ ccxt==2.2.67 cryptography==38.0.1; platform_machine == 'armv7l' cryptography==38.0.4; platform_machine != 'armv7l' aiohttp==3.8.3 -SQLAlchemy==1.4.44 +SQLAlchemy==1.4.45 python-telegram-bot==13.14 arrow==1.2.3 cachetools==4.2.2 From 2647c35f485406e50c6f8539510e66c83e20cc5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 03:02:53 +0000 Subject: [PATCH 042/795] Bump pypa/gh-action-pypi-publish from 1.6.1 to 1.6.4 Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.6.1 to 1.6.4. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.6.1...v1.6.4) --- 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 273fb7ea0..b15451a64 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -410,7 +410,7 @@ jobs: python setup.py sdist bdist_wheel - name: Publish to PyPI (Test) - uses: pypa/gh-action-pypi-publish@v1.6.1 + uses: pypa/gh-action-pypi-publish@v1.6.4 if: (github.event_name == 'release') with: user: __token__ @@ -418,7 +418,7 @@ jobs: repository_url: https://test.pypi.org/legacy/ - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@v1.6.1 + uses: pypa/gh-action-pypi-publish@v1.6.4 if: (github.event_name == 'release') with: user: __token__ From bc2b9981d3dbc782f20ed6730f8f712e02d61594 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 05:30:55 +0000 Subject: [PATCH 043/795] Bump python-telegram-bot from 13.14 to 13.15 Bumps [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) from 13.14 to 13.15. - [Release notes](https://github.com/python-telegram-bot/python-telegram-bot/releases) - [Changelog](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.15/CHANGES.rst) - [Commits](https://github.com/python-telegram-bot/python-telegram-bot/compare/v13.14...v13.15) --- 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 b36225aa6..3b572cce6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ cryptography==38.0.1; platform_machine == 'armv7l' cryptography==38.0.4; platform_machine != 'armv7l' aiohttp==3.8.3 SQLAlchemy==1.4.45 -python-telegram-bot==13.14 +python-telegram-bot==13.15 arrow==1.2.3 cachetools==4.2.2 requests==2.28.1 From 915e0ac62f940e0cb20484d582e8527b13488d3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 05:31:01 +0000 Subject: [PATCH 044/795] Bump ccxt from 2.2.67 to 2.2.92 Bumps [ccxt](https://github.com/ccxt/ccxt) from 2.2.67 to 2.2.92. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/2.2.67...2.2.92) --- 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 b36225aa6..fff69ffac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ numpy==1.23.5 pandas==1.5.2 pandas-ta==0.3.14b -ccxt==2.2.67 +ccxt==2.2.92 # Pin cryptography for now due to rust build errors with piwheels cryptography==38.0.1; platform_machine == 'armv7l' cryptography==38.0.4; platform_machine != 'armv7l' From de9784267a361ebb541fde4afaa23c8c6310a1fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 05:39:16 +0000 Subject: [PATCH 045/795] Bump filelock from 3.8.0 to 3.8.2 Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.8.0 to 3.8.2. - [Release notes](https://github.com/tox-dev/py-filelock/releases) - [Changelog](https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/py-filelock/compare/3.8.0...3.8.2) --- updated-dependencies: - dependency-name: filelock dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-hyperopt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 83ba62240..8fc58812b 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -5,5 +5,5 @@ scipy==1.9.3 scikit-learn==1.2.0 scikit-optimize==0.9.0 -filelock==3.8.0 +filelock==3.8.2 progressbar2==4.2.0 From f6b90595fae9a24cd0f2a3a3e83d824bf597e129 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Mon, 12 Dec 2022 11:05:03 +0100 Subject: [PATCH 046/795] remove html. change var names. --- freqtrade/plugins/pairlist/RemotePairList.py | 53 +++++++++----------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index 7367f713c..ef5463a56 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -6,7 +6,7 @@ Provides pair list fetched from a remote source import json import logging from pathlib import Path -from typing import Any, Dict, List +from typing import Any, Dict, List, Tuple import requests from cachetools import TTLCache @@ -60,7 +60,7 @@ class RemotePairList(IPairList): """ return f"{self.name} - {self._pairlistconfig['number_assets']} pairs from RemotePairlist." - def fetch_pairlist(self): + def fetch_pairlist(self) -> Tuple[List[str], float, str]: headers = { 'User-Agent': 'Freqtrade - Remotepairlist', } @@ -68,40 +68,35 @@ class RemotePairList(IPairList): info = "Pairlist" try: - response = requests.get(self._pairlist_url, headers=headers, - timeout=self._read_timeout) - content_type = response.headers.get('content-type') - time_elapsed = response.elapsed.total_seconds() + with requests.get(self._pairlist_url, headers=headers, + timeout=self._read_timeout) as response: + content_type = response.headers.get('content-type') + time_elapsed = response.elapsed.total_seconds() - rsplit = response.text.split("#") - - if "text/html" in str(content_type): - if len(rsplit) > 1: - plist = rsplit[0].strip() - plist = json.loads(plist) - info = rsplit[1].strip() + if "application/json" in str(content_type): + jsonparse = response.json() + pairlist = jsonparse['pairs'] + info = jsonparse.get('info', '') else: - plist = json.loads(rsplit[0]) - elif "application/json" in str(content_type): - jsonp = response.json() - plist = jsonp['pairs'] + raise OperationalException( + 'Remotepairlist is not of type JSON abort') - info = jsonp.get('info', '') - self._refresh_period = jsonp.get('refresh_period', self._refresh_period) + self._refresh_period = jsonparse.get('refresh_period', self._refresh_period) + self._pair_cache = TTLCache(maxsize=1, ttl=self._refresh_period) except requests.exceptions.RequestException: self.log_once(f'Was not able to fetch pairlist from:' f' {self._pairlist_url}', logger.info) if self._keep_pairlist_on_failure: - plist = str(self._last_pairlist) + pairlist = self._last_pairlist self.log_once('Keeping last fetched pairlist', logger.info) else: - plist = "" + pairlist = [] time_elapsed = 0 - return plist, time_elapsed, info + return pairlist, time_elapsed, info def gen_pairlist(self, tickers: Tickers) -> List[str]: """ @@ -111,7 +106,7 @@ class RemotePairList(IPairList): """ pairlist = self._pair_cache.get('pairlist') - time_elapsed = 0 + time_elapsed = 0.0 if pairlist: # Item found - no refresh necessary @@ -124,11 +119,11 @@ class RemotePairList(IPairList): if file_path.exists(): with open(filename) as json_file: # Load the JSON data into a dictionary - jsonp = json.load(json_file) - pairlist = jsonp['pairs'] - - info = jsonp.get('info', '') - self._refresh_period = jsonp.get('refresh_period', self._refresh_period) + jsonparse = json.load(json_file) + pairlist = jsonparse['pairs'] + info = jsonparse.get('info', '') + self._refresh_period = jsonparse.get('refresh_period', self._refresh_period) + self._pair_cache = TTLCache(maxsize=1, ttl=self._refresh_period) else: raise ValueError(f"{self._pairlist_url} does not exist.") @@ -139,7 +134,7 @@ class RemotePairList(IPairList): pairlist = self.filter_pairlist(pairlist, tickers) self._pair_cache['pairlist'] = pairlist.copy() - if time_elapsed: + if time_elapsed != 0.0: self.log_once(f'{info} Fetched in {time_elapsed} seconds.', logger.info) else: self.log_once(f'{info} Fetched Pairlist.', logger.info) From f9b7d35900b50cc786f8fee4943d5e301e3123b8 Mon Sep 17 00:00:00 2001 From: initrv Date: Mon, 12 Dec 2022 14:14:23 +0300 Subject: [PATCH 047/795] add increment param for tensorboard_log --- freqtrade/freqai/RL/BaseEnvironment.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/freqtrade/freqai/RL/BaseEnvironment.py b/freqtrade/freqai/RL/BaseEnvironment.py index 5a90d381e..5a5a950e7 100644 --- a/freqtrade/freqai/RL/BaseEnvironment.py +++ b/freqtrade/freqai/RL/BaseEnvironment.py @@ -139,7 +139,7 @@ class BaseEnvironment(gym.Env): self.np_random, seed = seeding.np_random(seed) return [seed] - def tensorboard_log(self, metric: str, inc: Union[int, float] = 1): + def tensorboard_log(self, metric: str, value: Union[int, float] = 1, inc: bool = True): """ Function builds the tensorboard_metrics dictionary to be parsed by the TensorboardCallback. This @@ -155,12 +155,13 @@ class BaseEnvironment(gym.Env): return -2 :param metric: metric to be tracked and incremented - :param inc: value to increment `metric` by + :param value: value to increment `metric` by + :param inc: sets whether the `value` is incremented or not """ - if metric not in self.tensorboard_metrics: - self.tensorboard_metrics[metric] = inc + if not inc or metric not in self.tensorboard_metrics: + self.tensorboard_metrics[metric] = value else: - self.tensorboard_metrics[metric] += inc + self.tensorboard_metrics[metric] += value def reset_tensorboard_log(self): self.tensorboard_metrics = {} From f940280d5e82d3574628af99f29d1fa0e2dd695a Mon Sep 17 00:00:00 2001 From: initrv Date: Mon, 12 Dec 2022 14:35:44 +0300 Subject: [PATCH 048/795] Fix tensorboard_log incrementing note --- docs/freqai-reinforcement-learning.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/freqai-reinforcement-learning.md b/docs/freqai-reinforcement-learning.md index b831c90a0..f3d6c97f8 100644 --- a/docs/freqai-reinforcement-learning.md +++ b/docs/freqai-reinforcement-learning.md @@ -270,7 +270,7 @@ FreqAI also provides a built in episodic summary logger called `self.tensorboard ``` !!! Note - The `self.tensorboard_log()` function is designed for tracking incremented objects only i.e. events, actions inside the training environment. If the event of interest is a float, the float can be passed as the second argument e.g. `self.tensorboard_log("float_metric1", 0.23)` would add 0.23 to `float_metric`. + The `self.tensorboard_log()` function is designed for tracking incremented objects only i.e. events, actions inside the training environment. If the event of interest is a float, the float can be passed as the second argument e.g. `self.tensorboard_log("float_metric1", 0.23)` would add 0.23 to `float_metric`. In this case you can also disable incrementing using `inc=False` parameter. ### Choosing a base environment From 6f92c58e3317773a2ffedaf33e9f465d358ec528 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Mon, 12 Dec 2022 13:24:33 +0100 Subject: [PATCH 049/795] add docs, add bearer token. --- docs/includes/pairlists.md | 46 +++++++++++++++++++- freqtrade/plugins/pairlist/RemotePairList.py | 12 +++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index d61718c7d..c12683e75 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -2,7 +2,7 @@ Pairlist Handlers define the list of pairs (pairlist) that the bot should trade. They are configured in the `pairlists` section of the configuration settings. -In your configuration, you can use Static Pairlist (defined by the [`StaticPairList`](#static-pair-list) Pairlist Handler) and Dynamic Pairlist (defined by the [`VolumePairList`](#volume-pair-list) Pairlist Handler). +In your configuration, you can use Static Pairlist (defined by the [`StaticPairList`](#static-pair-list) Pairlist Handler), Dynamic Pairlist (defined by the [`VolumePairList`](#volume-pair-list) Pairlist Handler). Additionally, [`AgeFilter`](#agefilter), [`PrecisionFilter`](#precisionfilter), [`PriceFilter`](#pricefilter), [`ShuffleFilter`](#shufflefilter), [`SpreadFilter`](#spreadfilter) and [`VolatilityFilter`](#volatilityfilter) act as Pairlist Filters, removing certain pairs and/or moving their positions in the pairlist. @@ -23,6 +23,7 @@ You may also use something like `.*DOWN/BTC` or `.*UP/BTC` to exclude leveraged * [`StaticPairList`](#static-pair-list) (default, if not configured differently) * [`VolumePairList`](#volume-pair-list) * [`ProducerPairList`](#producerpairlist) +* [`RemotePairList`](#remotepairlist) * [`AgeFilter`](#agefilter) * [`OffsetFilter`](#offsetfilter) * [`PerformanceFilter`](#performancefilter) @@ -173,6 +174,49 @@ You can limit the length of the pairlist with the optional parameter `number_ass `ProducerPairList` can also be used multiple times in sequence, combining the pairs from multiple producers. Obviously in complex such configurations, the Producer may not provide data for all pairs, so the strategy must be fit for this. +#### RemotePairList + +It allows the user to fetch a pairlist from a remote server or a locally stored json file within the freqtrade directory, enabling dynamic updates and customization of the trading pairlist. + +The RemotePairList is defined in the pairlists section of the configuration settings. It uses the following configuration options: + +```json +"pairlists": [ + { + "method": "RemotePairList", + "pairlist_url": "https://example.com/pairlist", + "number_assets": 10, + "refresh_period": 1800, + "keep_pairlist_on_failure": true, + "read_timeout": 60, + "bearer_token": "my-bearer-token" + } +] +``` + +The `pairlist_url` option specifies the URL of the remote server where the pairlist is located, or the path to a local file (if file:/// is prepended). This allows the user to use either a remote server or a local file as the source for the pairlist. + +The user is responsible for providing a server or local file that returns a JSON object with the following structure: + +```json +{ + "pairs": ["XRP/USDT", "ETH/USDT", "LTC/USDT"], + "refresh_period": 1800, + "info": "Pairlist updated on 2022-12-12 at 12:12" +} +``` + +The `pairs` property should contain a list of strings with the trading pairs to be used by the bot. The `refresh_period` property is optional and specifies the number of seconds that the pairlist should be cached before being refreshed. The `info` property is also optional and can be used to provide any additional information about the pairlist. + +The optional `keep_pairlist_on_failure` specifies whether the previous received pairlist should be used if the remote server is not reachable or returns an error. The default value is true. + +The optional `read_timeout` specifies the maximum amount of time (in seconds) to wait for a response from the remote source, The default value is 60. + +The optional `bearer_token` will be included in the requests Authorization Header. + +!!! Note + In case of a server error the last received pairlist will be kept if `keep_pairlist_on_failure` is set to true, when set to false a empty pairlist is returned. + #### AgeFilter Removes pairs that have been listed on the exchange for less than `min_days_listed` days (defaults to `10`) or more than `max_days_listed` days (defaults `None` mean infinity). diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index ef5463a56..7ef038da7 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -11,6 +11,7 @@ from typing import Any, Dict, List, Tuple import requests from cachetools import TTLCache +from freqtrade import __version__ from freqtrade.constants import Config from freqtrade.exceptions import OperationalException from freqtrade.exchange.types import Tickers @@ -43,6 +44,7 @@ class RemotePairList(IPairList): self._pair_cache: TTLCache = TTLCache(maxsize=1, ttl=self._refresh_period) self._pairlist_url = self._pairlistconfig.get('pairlist_url', '') self._read_timeout = self._pairlistconfig.get('read_timeout', 60) + self._bearer_token = self._pairlistconfig.get('bearer_token', '') self._last_pairlist: List[Any] = list() @property @@ -61,10 +63,14 @@ class RemotePairList(IPairList): return f"{self.name} - {self._pairlistconfig['number_assets']} pairs from RemotePairlist." def fetch_pairlist(self) -> Tuple[List[str], float, str]: + headers = { - 'User-Agent': 'Freqtrade - Remotepairlist', + 'User-Agent': 'Freqtrade/' + __version__ + ' Remotepairlist' } + if self._bearer_token: + headers['Authorization'] = f'Bearer {self._bearer_token}' + info = "Pairlist" try: @@ -76,7 +82,7 @@ class RemotePairList(IPairList): if "application/json" in str(content_type): jsonparse = response.json() pairlist = jsonparse['pairs'] - info = jsonparse.get('info', '') + info = jsonparse.get('info', '')[:1000] else: raise OperationalException( 'Remotepairlist is not of type JSON abort') @@ -121,7 +127,7 @@ class RemotePairList(IPairList): # Load the JSON data into a dictionary jsonparse = json.load(json_file) pairlist = jsonparse['pairs'] - info = jsonparse.get('info', '') + info = jsonparse.get('info', '')[:1000] self._refresh_period = jsonparse.get('refresh_period', self._refresh_period) self._pair_cache = TTLCache(maxsize=1, ttl=self._refresh_period) From 5c984bf5c23d8e14e7d79f7a12848225f93fe410 Mon Sep 17 00:00:00 2001 From: Emre Date: Mon, 12 Dec 2022 21:33:12 +0300 Subject: [PATCH 050/795] Temporarily downgrade blosc for arm64 --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5aba43edf..37f1d31e1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,7 +20,8 @@ tabulate==0.9.0 pycoingecko==3.1.0 jinja2==3.1.2 tables==3.7.0 -blosc==1.11.0 +blosc==1.10.6; platform_machine == 'arm64' +blosc==1.11.0; platform_machine != 'arm64' joblib==1.2.0 pyarrow==10.0.1; platform_machine != 'armv7l' From abc3badfb53cf3c5ba56258cd18c0c94517ab8e7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 12 Dec 2022 20:01:54 +0100 Subject: [PATCH 051/795] Improve shutdown behavior closes #7882 --- freqtrade/freqtradebot.py | 9 ++++++++- tests/test_freqtradebot.py | 12 ++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index f9cb28c28..f6c4a52bb 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -155,6 +155,8 @@ class FreqtradeBot(LoggingMixin): self.cancel_all_open_orders() self.check_for_open_trades() + except Exception as e: + logger.warning(f'Exception during cleanup: {e.__class__.__name__} {e}') finally: self.strategy.ft_bot_cleanup() @@ -162,8 +164,13 @@ class FreqtradeBot(LoggingMixin): self.rpc.cleanup() if self.emc: self.emc.shutdown() - Trade.commit() self.exchange.close() + try: + Trade.commit() + except Exception: + # Exeptions here will be happening if the db disappeared. + # At which point we can no longer commit anyway. + pass def startup(self) -> None: """ diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index b71b5b387..faaefcafb 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -88,6 +88,18 @@ def test_bot_cleanup(mocker, default_conf_usdt, caplog) -> None: assert coo_mock.call_count == 1 +def test_bot_cleanup_db_errors(mocker, default_conf_usdt, caplog) -> None: + mocker.patch('freqtrade.freqtradebot.Trade.commit', + side_effect=OperationalException()) + mocker.patch('freqtrade.freqtradebot.FreqtradeBot.check_for_open_trades', + side_effect=OperationalException()) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) + freqtrade.emc = MagicMock() + freqtrade.emc.shutdown = MagicMock() + freqtrade.cleanup() + assert freqtrade.emc.shutdown.call_count == 1 + + @pytest.mark.parametrize('runmode', [ RunMode.DRY_RUN, RunMode.LIVE From 9660e445b89c15c732b276d380f3ef1a27618d46 Mon Sep 17 00:00:00 2001 From: Timothy Pogue Date: Fri, 25 Nov 2022 18:09:47 -0700 Subject: [PATCH 052/795] use new channel apis in emc, extend analyzed df to include list of dates for candles --- freqtrade/data/dataprovider.py | 78 ++++++++++++- freqtrade/rpc/api_server/ws_schemas.py | 2 +- freqtrade/rpc/external_message_consumer.py | 128 ++++++++++++++++----- freqtrade/rpc/rpc.py | 46 ++++++-- 4 files changed, 212 insertions(+), 42 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 6b220c8b4..d6eb217a8 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -9,7 +9,7 @@ from collections import deque from datetime import datetime, timezone from typing import Any, Dict, List, Optional, Tuple -from pandas import DataFrame +from pandas import DataFrame, concat, date_range from freqtrade.configuration import TimeRange from freqtrade.constants import Config, ListPairsWithTimeframes, PairWithTimeframe @@ -120,7 +120,7 @@ class DataProvider: 'type': RPCMessageType.ANALYZED_DF, 'data': { 'key': pair_key, - 'df': dataframe, + 'df': dataframe.tail(1), 'la': datetime.now(timezone.utc) } } @@ -157,6 +157,80 @@ class DataProvider: self.__producer_pairs_df[producer_name][pair_key] = (dataframe, _last_analyzed) logger.debug(f"External DataFrame for {pair_key} from {producer_name} added.") + def _add_external_candle( + self, + pair: str, + dataframe: DataFrame, + last_analyzed: datetime, + timeframe: str, + candle_type: CandleType, + producer_name: str = "default" + ) -> Tuple[bool, Optional[List[str]]]: + """ + Append a candle to the existing external dataframe + + :param pair: pair to get the data for + :param timeframe: Timeframe to get data for + :param candle_type: Any of the enum CandleType (must match trading mode!) + :returns: A tuple with a boolean value signifying if the candle was correctly appended, + and a list of datetimes missing from the candle if it finds some. + Will return false if has no data for `producer_name`. + Will return false if no existing data for (pair, timeframe, candle_type). + Will return false if there's missing candles, and a list of datetimes of + the missing candles. + """ + pair_key = (pair, timeframe, candle_type) + + if producer_name not in self.__producer_pairs_df: + # We don't have data from this producer yet, + # so we can't append a candle + return (False, None) + + if pair_key not in self.__producer_pairs_df[producer_name]: + # We don't have data for this pair_key, + # so we can't append a candle + return (False, None) + + # CHECK FOR MISSING CANDLES + + existing_df, _ = self.__producer_pairs_df[producer_name][pair_key] + appended_df = self._append_candle_to_dataframe(existing_df, dataframe) + + # Everything is good, we appended + self.__producer_pairs_df[producer_name][pair_key] = appended_df, last_analyzed + return (True, None) + + def _append_candle_to_dataframe(self, existing: DataFrame, new: DataFrame) -> DataFrame: + """ + Append the `new` dataframe to the `existing` dataframe + + :param existing: The full dataframe you want appended to + :param new: The new dataframe containing the data you want appended + :returns: The dataframe with the new data in it + """ + if existing.iloc[-1]['date'] != new.iloc[-1]['date']: + existing = concat([existing, new]) + + # Only keep the last 1000 candles in memory + # TODO: Do this better + existing = existing[-1000:] if len(existing) > 1000 else existing + + return existing + + def _is_missing_candles(self, dataframe: DataFrame) -> bool: + """ + Check if the dataframe is missing any candles + + :param dataframe: The DataFrame to check + """ + logger.info(dataframe.index) + return len( + date_range( + dataframe.index.min(), + dataframe.index.max() + ).difference(dataframe.index) + ) > 0 + def get_producer_df( self, pair: str, diff --git a/freqtrade/rpc/api_server/ws_schemas.py b/freqtrade/rpc/api_server/ws_schemas.py index 877232213..292672b60 100644 --- a/freqtrade/rpc/api_server/ws_schemas.py +++ b/freqtrade/rpc/api_server/ws_schemas.py @@ -47,7 +47,7 @@ class WSWhitelistRequest(WSRequestSchema): class WSAnalyzedDFRequest(WSRequestSchema): type: RPCRequestType = RPCRequestType.ANALYZED_DF - data: Dict[str, Any] = {"limit": 1500} + data: Dict[str, Any] = {"limit": 1500, "pair": None} # ------------------------------ MESSAGE SCHEMAS ---------------------------- diff --git a/freqtrade/rpc/external_message_consumer.py b/freqtrade/rpc/external_message_consumer.py index 6078efd07..24731ef4f 100644 --- a/freqtrade/rpc/external_message_consumer.py +++ b/freqtrade/rpc/external_message_consumer.py @@ -8,7 +8,7 @@ import asyncio import logging import socket from threading import Thread -from typing import TYPE_CHECKING, Any, Callable, Dict, List, TypedDict +from typing import TYPE_CHECKING, Any, Callable, Dict, List, TypedDict, Union import websockets from pydantic import ValidationError @@ -16,7 +16,8 @@ from pydantic import ValidationError from freqtrade.data.dataprovider import DataProvider from freqtrade.enums import RPCMessageType from freqtrade.misc import remove_entry_exit_signals -from freqtrade.rpc.api_server.ws import WebSocketChannel +from freqtrade.rpc.api_server.ws.channel import WebSocketChannel, create_channel +from freqtrade.rpc.api_server.ws.message_stream import MessageStream from freqtrade.rpc.api_server.ws_schemas import (WSAnalyzedDFMessage, WSAnalyzedDFRequest, WSMessageSchema, WSRequestSchema, WSSubscribeRequest, WSWhitelistMessage, @@ -38,6 +39,14 @@ class Producer(TypedDict): logger = logging.getLogger(__name__) +def schema_to_dict(schema: Union[WSMessageSchema, WSRequestSchema]): + return schema.dict(exclude_none=True) + + +# def parse_message(message: Dict[str, Any], message_schema: Type[WSMessageSchema]): +# return message_schema.parse_obj(message) + + class ExternalMessageConsumer: """ The main controller class for consuming external messages from @@ -92,6 +101,8 @@ class ExternalMessageConsumer: RPCMessageType.ANALYZED_DF: self._consume_analyzed_df_message, } + self._channel_streams: Dict[str, MessageStream] = {} + self.start() def start(self): @@ -118,6 +129,8 @@ class ExternalMessageConsumer: logger.info("Stopping ExternalMessageConsumer") self._running = False + self._channel_streams = {} + if self._sub_tasks: # Cancel sub tasks for task in self._sub_tasks: @@ -175,7 +188,6 @@ class ExternalMessageConsumer: :param producer: Dictionary containing producer info :param lock: An asyncio Lock """ - channel = None while self._running: try: host, port = producer['host'], producer['port'] @@ -190,19 +202,17 @@ class ExternalMessageConsumer: max_size=self.message_size_limit, ping_interval=None ) as ws: - channel = WebSocketChannel(ws, channel_id=name) + async with create_channel(ws, channel_id=name) as channel: - logger.info(f"Producer connection success - {channel}") + # Create the message stream for this channel + self._channel_streams[name] = MessageStream() - # Now request the initial data from this Producer - for request in self._initial_requests: - await channel.send( - request.dict(exclude_none=True) + # Run the channel tasks while connected + await channel.run_channel_tasks( + self._receive_messages(channel, producer, lock), + self._send_requests(channel, self._channel_streams[name]) ) - # Now receive data, if none is within the time limit, ping - await self._receive_messages(channel, producer, lock) - except (websockets.exceptions.InvalidURI, ValueError) as e: logger.error(f"{ws_url} is an invalid WebSocket URL - {e}") break @@ -214,26 +224,33 @@ class ExternalMessageConsumer: websockets.exceptions.InvalidMessage ) as e: logger.error(f"Connection Refused - {e} retrying in {self.sleep_time}s") - await asyncio.sleep(self.sleep_time) - continue except ( websockets.exceptions.ConnectionClosedError, websockets.exceptions.ConnectionClosedOK ): # Just keep trying to connect again indefinitely - await asyncio.sleep(self.sleep_time) - continue + pass except Exception as e: # An unforseen error has occurred, log and continue logger.error("Unexpected error has occurred:") logger.exception(e) - continue finally: - if channel: - await channel.close() + await asyncio.sleep(self.sleep_time) + continue + + async def _send_requests(self, channel: WebSocketChannel, channel_stream: MessageStream): + # Send the initial requests + for init_request in self._initial_requests: + await channel.send(schema_to_dict(init_request)) + + # Now send any subsequent requests published to + # this channel's stream + async for request in channel_stream: + logger.info(f"Sending request to channel - {channel} - {request}") + await channel.send(request) async def _receive_messages( self, @@ -270,20 +287,39 @@ class ExternalMessageConsumer: latency = (await asyncio.wait_for(pong, timeout=self.ping_timeout) * 1000) logger.info(f"Connection to {channel} still alive, latency: {latency}ms") - continue + except (websockets.exceptions.ConnectionClosed): # Just eat the error and continue reconnecting logger.warning(f"Disconnection in {channel} - retrying in {self.sleep_time}s") - await asyncio.sleep(self.sleep_time) - break + except Exception as e: + # Just eat the error and continue reconnecting logger.warning(f"Ping error {channel} - {e} - retrying in {self.sleep_time}s") logger.debug(e, exc_info=e) - await asyncio.sleep(self.sleep_time) + finally: + await asyncio.sleep(self.sleep_time) break + def send_producer_request( + self, + producer_name: str, + request: Union[WSRequestSchema, Dict[str, Any]] + ): + """ + Publish a message to the producer's message stream to be + sent by the channel task. + + :param producer_name: The name of the producer to publish the message to + :param request: The request to send to the producer + """ + if isinstance(request, WSRequestSchema): + request = schema_to_dict(request) + + if channel_stream := self._channel_streams.get(producer_name): + channel_stream.publish(request) + def handle_producer_message(self, producer: Producer, message: Dict[str, Any]): """ Handles external messages from a Producer @@ -340,12 +376,44 @@ class ExternalMessageConsumer: if self._emc_config.get('remove_entry_exit_signals', False): df = remove_entry_exit_signals(df) - # Add the dataframe to the dataprovider - self._dp._add_external_df(pair, df, - last_analyzed=la, - timeframe=timeframe, - candle_type=candle_type, - producer_name=producer_name) + if len(df) >= 999: + # This is a full dataframe + # Add the dataframe to the dataprovider + self._dp._add_external_df( + pair, + df, + last_analyzed=la, + timeframe=timeframe, + candle_type=candle_type, + producer_name=producer_name + ) - logger.debug( + elif len(df) == 1: + # This is just a single candle + # Have dataprovider append it to + # the full datafame. If it can't, + # request the missing candles + if not self._dp._add_external_candle( + pair, + df, + last_analyzed=la, + timeframe=timeframe, + candle_type=candle_type, + producer_name=producer_name + ): + logger.info("Holes in data or no existing df, " + f"requesting data for {key} from `{producer_name}`") + + self.send_producer_request( + producer_name, + WSAnalyzedDFRequest( + data={ + "limit": 1000, + "pair": pair + } + ) + ) + return + + logger.info( f"Consumed message from `{producer_name}` of type `RPCMessageType.ANALYZED_DF`") diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 334e18dc7..8b23d33e7 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -1058,23 +1058,46 @@ class RPC: return self._convert_dataframe_to_dict(self._freqtrade.config['strategy'], pair, timeframe, _data, last_analyzed) - def __rpc_analysed_dataframe_raw(self, pair: str, timeframe: str, - limit: Optional[int]) -> Tuple[DataFrame, datetime]: - """ Get the dataframe and last analyze from the dataprovider """ + def __rpc_analysed_dataframe_raw( + self, + pair: str, + timeframe: str, + limit: Optional[Union[int, List[str]]] = None + ) -> Tuple[DataFrame, datetime]: + """ + Get the dataframe and last analyze from the dataprovider + + :param pair: The pair to get + :param timeframe: The timeframe of data to get + :param limit: If an integer, limits the size of dataframe + If a list of string date times, only returns those candles + """ _data, last_analyzed = self._freqtrade.dataprovider.get_analyzed_dataframe( pair, timeframe) _data = _data.copy() - if limit: + if limit and isinstance(limit, int): _data = _data.iloc[-limit:] + elif limit and isinstance(limit, str): + _data = _data.iloc[_data['date'].isin(limit)] + return _data, last_analyzed def _ws_all_analysed_dataframes( self, pairlist: List[str], - limit: Optional[int] + limit: Optional[Union[int, List[str]]] = None ) -> Generator[Dict[str, Any], None, None]: - """ Get the analysed dataframes of each pair in the pairlist """ + """ + Get the analysed dataframes of each pair in the pairlist. + Limit size of dataframe if specified. + If candles, only return the candles specified. + + :param pairlist: A list of pairs to get + :param limit: If an integer, limits the size of dataframe + If a list of string date times, only returns those candles + :returns: A generator of dictionaries with the key, dataframe, and last analyzed timestamp + """ timeframe = self._freqtrade.config['timeframe'] candle_type = self._freqtrade.config.get('candle_type_def', CandleType.SPOT) @@ -1087,10 +1110,15 @@ class RPC: "la": last_analyzed } - def _ws_request_analyzed_df(self, limit: Optional[int]): + def _ws_request_analyzed_df( + self, + pair: Optional[str], + limit: Optional[Union[int, List[str]]] = None, + ): """ Historical Analyzed Dataframes for WebSocket """ - whitelist = self._freqtrade.active_pair_whitelist - return self._ws_all_analysed_dataframes(whitelist, limit) + pairlist = [pair] if pair else self._freqtrade.active_pair_whitelist + + return self._ws_all_analysed_dataframes(pairlist, limit) def _ws_request_whitelist(self): """ Whitelist data for WebSocket """ From 4cbb3341d7160e21a55b86738100a1f49bfc7a6b Mon Sep 17 00:00:00 2001 From: Timothy Pogue Date: Fri, 25 Nov 2022 19:04:51 -0700 Subject: [PATCH 053/795] change how missing candles will be handled --- freqtrade/data/dataprovider.py | 35 +++++----------------- freqtrade/rpc/external_message_consumer.py | 4 +-- freqtrade/rpc/rpc.py | 13 ++++---- 3 files changed, 15 insertions(+), 37 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index d6eb217a8..07999fc90 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -7,9 +7,9 @@ Common Interface for bot and strategy to access data. import logging from collections import deque from datetime import datetime, timezone -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple, Union -from pandas import DataFrame, concat, date_range +from pandas import DataFrame, concat from freqtrade.configuration import TimeRange from freqtrade.constants import Config, ListPairsWithTimeframes, PairWithTimeframe @@ -165,40 +165,36 @@ class DataProvider: timeframe: str, candle_type: CandleType, producer_name: str = "default" - ) -> Tuple[bool, Optional[List[str]]]: + ) -> Union[bool, int]: """ Append a candle to the existing external dataframe :param pair: pair to get the data for :param timeframe: Timeframe to get data for :param candle_type: Any of the enum CandleType (must match trading mode!) - :returns: A tuple with a boolean value signifying if the candle was correctly appended, - and a list of datetimes missing from the candle if it finds some. - Will return false if has no data for `producer_name`. - Will return false if no existing data for (pair, timeframe, candle_type). - Will return false if there's missing candles, and a list of datetimes of - the missing candles. + :returns: False if the candle could not be appended, or the int number of missing candles. """ pair_key = (pair, timeframe, candle_type) if producer_name not in self.__producer_pairs_df: # We don't have data from this producer yet, # so we can't append a candle - return (False, None) + return False if pair_key not in self.__producer_pairs_df[producer_name]: # We don't have data for this pair_key, # so we can't append a candle - return (False, None) + return False # CHECK FOR MISSING CANDLES + # return int existing_df, _ = self.__producer_pairs_df[producer_name][pair_key] appended_df = self._append_candle_to_dataframe(existing_df, dataframe) # Everything is good, we appended self.__producer_pairs_df[producer_name][pair_key] = appended_df, last_analyzed - return (True, None) + return True def _append_candle_to_dataframe(self, existing: DataFrame, new: DataFrame) -> DataFrame: """ @@ -212,25 +208,10 @@ class DataProvider: existing = concat([existing, new]) # Only keep the last 1000 candles in memory - # TODO: Do this better existing = existing[-1000:] if len(existing) > 1000 else existing return existing - def _is_missing_candles(self, dataframe: DataFrame) -> bool: - """ - Check if the dataframe is missing any candles - - :param dataframe: The DataFrame to check - """ - logger.info(dataframe.index) - return len( - date_range( - dataframe.index.min(), - dataframe.index.max() - ).difference(dataframe.index) - ) > 0 - def get_producer_df( self, pair: str, diff --git a/freqtrade/rpc/external_message_consumer.py b/freqtrade/rpc/external_message_consumer.py index 24731ef4f..231642142 100644 --- a/freqtrade/rpc/external_message_consumer.py +++ b/freqtrade/rpc/external_message_consumer.py @@ -388,8 +388,8 @@ class ExternalMessageConsumer: producer_name=producer_name ) - elif len(df) == 1: - # This is just a single candle + elif len(df) < 999: + # This is n single candles # Have dataprovider append it to # the full datafame. If it can't, # request the missing candles diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 8b23d33e7..2452a61b8 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -1062,31 +1062,28 @@ class RPC: self, pair: str, timeframe: str, - limit: Optional[Union[int, List[str]]] = None + limit: Optional[int] = None ) -> Tuple[DataFrame, datetime]: """ Get the dataframe and last analyze from the dataprovider :param pair: The pair to get :param timeframe: The timeframe of data to get - :param limit: If an integer, limits the size of dataframe - If a list of string date times, only returns those candles + :param limit: The amount of candles in the dataframe """ _data, last_analyzed = self._freqtrade.dataprovider.get_analyzed_dataframe( pair, timeframe) _data = _data.copy() - if limit and isinstance(limit, int): + if limit: _data = _data.iloc[-limit:] - elif limit and isinstance(limit, str): - _data = _data.iloc[_data['date'].isin(limit)] return _data, last_analyzed def _ws_all_analysed_dataframes( self, pairlist: List[str], - limit: Optional[Union[int, List[str]]] = None + limit: Optional[int] = None ) -> Generator[Dict[str, Any], None, None]: """ Get the analysed dataframes of each pair in the pairlist. @@ -1113,7 +1110,7 @@ class RPC: def _ws_request_analyzed_df( self, pair: Optional[str], - limit: Optional[Union[int, List[str]]] = None, + limit: Optional[int] = None, ): """ Historical Analyzed Dataframes for WebSocket """ pairlist = [pair] if pair else self._freqtrade.active_pair_whitelist From 36a00e8de08b47900c5dbaea70c035e51f036571 Mon Sep 17 00:00:00 2001 From: Timothy Pogue Date: Sun, 27 Nov 2022 12:17:26 -0700 Subject: [PATCH 054/795] update add_external_candle returns --- freqtrade/data/dataprovider.py | 12 ++++++------ freqtrade/rpc/external_message_consumer.py | 8 +++++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 07999fc90..19b5df652 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -7,7 +7,7 @@ Common Interface for bot and strategy to access data. import logging from collections import deque from datetime import datetime, timezone -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, Dict, List, Optional, Tuple from pandas import DataFrame, concat @@ -165,7 +165,7 @@ class DataProvider: timeframe: str, candle_type: CandleType, producer_name: str = "default" - ) -> Union[bool, int]: + ) -> Tuple[bool, int]: """ Append a candle to the existing external dataframe @@ -179,22 +179,22 @@ class DataProvider: if producer_name not in self.__producer_pairs_df: # We don't have data from this producer yet, # so we can't append a candle - return False + return (False, 0) if pair_key not in self.__producer_pairs_df[producer_name]: # We don't have data for this pair_key, # so we can't append a candle - return False + return (False, 0) # CHECK FOR MISSING CANDLES - # return int + # return (False, int > 0) existing_df, _ = self.__producer_pairs_df[producer_name][pair_key] appended_df = self._append_candle_to_dataframe(existing_df, dataframe) # Everything is good, we appended self.__producer_pairs_df[producer_name][pair_key] = appended_df, last_analyzed - return True + return (True, 0) def _append_candle_to_dataframe(self, existing: DataFrame, new: DataFrame) -> DataFrame: """ diff --git a/freqtrade/rpc/external_message_consumer.py b/freqtrade/rpc/external_message_consumer.py index 231642142..17c4e1aa0 100644 --- a/freqtrade/rpc/external_message_consumer.py +++ b/freqtrade/rpc/external_message_consumer.py @@ -393,14 +393,16 @@ class ExternalMessageConsumer: # Have dataprovider append it to # the full datafame. If it can't, # request the missing candles - if not self._dp._add_external_candle( + did_append, n_missing = self._dp._add_external_candle( pair, df, last_analyzed=la, timeframe=timeframe, candle_type=candle_type, producer_name=producer_name - ): + ) + + if not did_append: logger.info("Holes in data or no existing df, " f"requesting data for {key} from `{producer_name}`") @@ -408,7 +410,7 @@ class ExternalMessageConsumer: producer_name, WSAnalyzedDFRequest( data={ - "limit": 1000, + "limit": n_missing if n_missing > 0 else 1000, "pair": pair } ) From fce1e9d6d0636c42d1ce19fdc6ebc8acce75e147 Mon Sep 17 00:00:00 2001 From: Timothy Pogue Date: Sun, 27 Nov 2022 12:18:41 -0700 Subject: [PATCH 055/795] update analyzed df request to allow specifying a single pair --- freqtrade/rpc/api_server/api_ws.py | 3 ++- freqtrade/rpc/rpc.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/rpc/api_server/api_ws.py b/freqtrade/rpc/api_server/api_ws.py index e183cd7e7..18714f15f 100644 --- a/freqtrade/rpc/api_server/api_ws.py +++ b/freqtrade/rpc/api_server/api_ws.py @@ -91,9 +91,10 @@ async def _process_consumer_request( elif type == RPCRequestType.ANALYZED_DF: # Limit the amount of candles per dataframe to 'limit' or 1500 limit = min(data.get('limit', 1500), 1500) if data else None + pair = data.get('pair', None) if data else None # For every pair in the generator, send a separate message - for message in rpc._ws_request_analyzed_df(limit): + for message in rpc._ws_request_analyzed_df(limit, pair): # Format response response = WSAnalyzedDFMessage(data=message) await channel.send(response.dict(exclude_none=True)) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 2452a61b8..4ebedd6c4 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -1109,8 +1109,8 @@ class RPC: def _ws_request_analyzed_df( self, - pair: Optional[str], limit: Optional[int] = None, + pair: Optional[str] = None ): """ Historical Analyzed Dataframes for WebSocket """ pairlist = [pair] if pair else self._freqtrade.active_pair_whitelist From d2c8487ecf01b90fab34dd55cc8d76bdd9bf5c2d Mon Sep 17 00:00:00 2001 From: Timothy Pogue Date: Sun, 27 Nov 2022 13:11:43 -0700 Subject: [PATCH 056/795] update add_external_candle, fix breaking on ping error, handle empty dataframes --- freqtrade/data/dataprovider.py | 14 +++++++++----- freqtrade/rpc/external_message_consumer.py | 20 ++++++++++++++------ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 19b5df652..42fe2f603 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -179,15 +179,19 @@ class DataProvider: if producer_name not in self.__producer_pairs_df: # We don't have data from this producer yet, # so we can't append a candle - return (False, 0) + return (False, 999) if pair_key not in self.__producer_pairs_df[producer_name]: # We don't have data for this pair_key, # so we can't append a candle - return (False, 0) + return (False, 999) # CHECK FOR MISSING CANDLES - # return (False, int > 0) + # Calculate difference between last candle in local dataframe + # and first candle in incoming dataframe. Take difference and divide + # by timeframe to find out how many candles we still need. If 1 + # then the incoming candle is the right candle. If more than 1, + # return (False, missing candles - 1) existing_df, _ = self.__producer_pairs_df[producer_name][pair_key] appended_df = self._append_candle_to_dataframe(existing_df, dataframe) @@ -207,8 +211,8 @@ class DataProvider: if existing.iloc[-1]['date'] != new.iloc[-1]['date']: existing = concat([existing, new]) - # Only keep the last 1000 candles in memory - existing = existing[-1000:] if len(existing) > 1000 else existing + # Only keep the last 1500 candles in memory + existing = existing[-1500:] if len(existing) > 1000 else existing return existing diff --git a/freqtrade/rpc/external_message_consumer.py b/freqtrade/rpc/external_message_consumer.py index 17c4e1aa0..13c2e5fb3 100644 --- a/freqtrade/rpc/external_message_consumer.py +++ b/freqtrade/rpc/external_message_consumer.py @@ -248,7 +248,7 @@ class ExternalMessageConsumer: # Now send any subsequent requests published to # this channel's stream - async for request in channel_stream: + async for request, _ in channel_stream: logger.info(f"Sending request to channel - {channel} - {request}") await channel.send(request) @@ -292,13 +292,13 @@ class ExternalMessageConsumer: except (websockets.exceptions.ConnectionClosed): # Just eat the error and continue reconnecting logger.warning(f"Disconnection in {channel} - retrying in {self.sleep_time}s") + await asyncio.sleep(self.sleep_time) + break except Exception as e: # Just eat the error and continue reconnecting logger.warning(f"Ping error {channel} - {e} - retrying in {self.sleep_time}s") logger.debug(e, exc_info=e) - - finally: await asyncio.sleep(self.sleep_time) break @@ -372,10 +372,16 @@ class ExternalMessageConsumer: pair, timeframe, candle_type = key + if df.empty: + logger.info(f"Received Empty Dataframe for {key}") + return + # If set, remove the Entry and Exit signals from the Producer if self._emc_config.get('remove_entry_exit_signals', False): df = remove_entry_exit_signals(df) + logger.info(f"Received {len(df)} candle(s) for {key}") + if len(df) >= 999: # This is a full dataframe # Add the dataframe to the dataprovider @@ -404,13 +410,14 @@ class ExternalMessageConsumer: if not did_append: logger.info("Holes in data or no existing df, " - f"requesting data for {key} from `{producer_name}`") + f"requesting {n_missing} candles " + f"for {key} from `{producer_name}`") self.send_producer_request( producer_name, WSAnalyzedDFRequest( data={ - "limit": n_missing if n_missing > 0 else 1000, + "limit": n_missing, "pair": pair } ) @@ -418,4 +425,5 @@ class ExternalMessageConsumer: return logger.info( - f"Consumed message from `{producer_name}` of type `RPCMessageType.ANALYZED_DF`") + f"Consumed message from `{producer_name}` " + f"of type `RPCMessageType.ANALYZED_DF` for {key}") From 89338fa677185b70528d2f74609ced74f84f7274 Mon Sep 17 00:00:00 2001 From: Timothy Pogue Date: Sun, 27 Nov 2022 13:14:49 -0700 Subject: [PATCH 057/795] allow specifying channel send throttle --- freqtrade/rpc/api_server/ws/channel.py | 7 +++++-- freqtrade/rpc/external_message_consumer.py | 6 +++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/freqtrade/rpc/api_server/ws/channel.py b/freqtrade/rpc/api_server/ws/channel.py index c50aff8be..3c0a833d8 100644 --- a/freqtrade/rpc/api_server/ws/channel.py +++ b/freqtrade/rpc/api_server/ws/channel.py @@ -27,7 +27,8 @@ class WebSocketChannel: self, websocket: WebSocketType, channel_id: Optional[str] = None, - serializer_cls: Type[WebSocketSerializer] = HybridJSONWebSocketSerializer + serializer_cls: Type[WebSocketSerializer] = HybridJSONWebSocketSerializer, + send_throttle: float = 0.01 ): self.channel_id = channel_id if channel_id else uuid4().hex[:8] self._websocket = WebSocketProxy(websocket) @@ -41,6 +42,7 @@ class WebSocketChannel: self._send_times: Deque[float] = deque([], maxlen=10) # High limit defaults to 3 to start self._send_high_limit = 3 + self._send_throttle = send_throttle # The subscribed message types self._subscriptions: List[str] = [] @@ -106,7 +108,8 @@ class WebSocketChannel: # Explicitly give control back to event loop as # websockets.send does not - await asyncio.sleep(0.01) + # Also throttles how fast we send + await asyncio.sleep(self._send_throttle) async def recv(self): """ diff --git a/freqtrade/rpc/external_message_consumer.py b/freqtrade/rpc/external_message_consumer.py index 13c2e5fb3..aed5d9fb9 100644 --- a/freqtrade/rpc/external_message_consumer.py +++ b/freqtrade/rpc/external_message_consumer.py @@ -202,7 +202,11 @@ class ExternalMessageConsumer: max_size=self.message_size_limit, ping_interval=None ) as ws: - async with create_channel(ws, channel_id=name) as channel: + async with create_channel( + ws, + channel_id=name, + send_throttle=0.5 + ) as channel: # Create the message stream for this channel self._channel_streams[name] = MessageStream() From c050eb8b8b372c280b43ea0c2eecbe683ef083d9 Mon Sep 17 00:00:00 2001 From: Timothy Pogue Date: Mon, 28 Nov 2022 11:02:03 -0700 Subject: [PATCH 058/795] add candle difference calculation to dataprovider --- freqtrade/data/dataprovider.py | 40 +++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 42fe2f603..e34a428eb 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -9,7 +9,7 @@ from collections import deque from datetime import datetime, timezone from typing import Any, Dict, List, Optional, Tuple -from pandas import DataFrame, concat +from pandas import DataFrame, concat, to_timedelta from freqtrade.configuration import TimeRange from freqtrade.constants import Config, ListPairsWithTimeframes, PairWithTimeframe @@ -176,24 +176,30 @@ class DataProvider: """ pair_key = (pair, timeframe, candle_type) - if producer_name not in self.__producer_pairs_df: + if (producer_name not in self.__producer_pairs_df) \ + or (pair_key not in self.__producer_pairs_df[producer_name]): # We don't have data from this producer yet, - # so we can't append a candle - return (False, 999) - - if pair_key not in self.__producer_pairs_df[producer_name]: - # We don't have data for this pair_key, - # so we can't append a candle - return (False, 999) - - # CHECK FOR MISSING CANDLES - # Calculate difference between last candle in local dataframe - # and first candle in incoming dataframe. Take difference and divide - # by timeframe to find out how many candles we still need. If 1 - # then the incoming candle is the right candle. If more than 1, - # return (False, missing candles - 1) + # sor we don't have data for this pair_key + # return False and 1000 for the full df + return (False, 1000) existing_df, _ = self.__producer_pairs_df[producer_name][pair_key] + + # CHECK FOR MISSING CANDLES + timeframe_delta = to_timedelta(timeframe) # Convert the timeframe to a timedelta for pandas + local_last = existing_df.iloc[-1]['date'] # We want the last date from our copy of data + incoming_first = dataframe.iloc[0]['date'] # We want the first date from the incoming data + + candle_difference = (incoming_first - local_last) / timeframe_delta + + # If the difference divided by the timeframe is 1, then this + # is the candle we want and the incoming data isn't missing any. + # If the candle_difference is more than 1, that means + # we missed some candles between our data and the incoming + # so return False and candle_difference. + if candle_difference > 1: + return (False, candle_difference) + appended_df = self._append_candle_to_dataframe(existing_df, dataframe) # Everything is good, we appended @@ -212,7 +218,7 @@ class DataProvider: existing = concat([existing, new]) # Only keep the last 1500 candles in memory - existing = existing[-1500:] if len(existing) > 1000 else existing + existing = existing[-1500:] if len(existing) > 1500 else existing return existing From ccd1aa70a2f5b1ecfcc202e20250b2d79a11a6cc Mon Sep 17 00:00:00 2001 From: Timothy Pogue Date: Tue, 29 Nov 2022 11:21:36 -0700 Subject: [PATCH 059/795] change log calls to debug, handle already received candle --- freqtrade/data/dataprovider.py | 9 ++++++++- freqtrade/rpc/external_message_consumer.py | 14 +++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index e34a428eb..657d96df1 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -179,7 +179,7 @@ class DataProvider: if (producer_name not in self.__producer_pairs_df) \ or (pair_key not in self.__producer_pairs_df[producer_name]): # We don't have data from this producer yet, - # sor we don't have data for this pair_key + # or we don't have data for this pair_key # return False and 1000 for the full df return (False, 1000) @@ -190,6 +190,13 @@ class DataProvider: local_last = existing_df.iloc[-1]['date'] # We want the last date from our copy of data incoming_first = dataframe.iloc[0]['date'] # We want the first date from the incoming data + # We have received this candle before, update our copy + # and return True, 0 + if local_last == incoming_first: + existing_df.iloc[-1] = dataframe.iloc[0] + existing_df = existing_df.reset_index(drop=True) + return (True, 0) + candle_difference = (incoming_first - local_last) / timeframe_delta # If the difference divided by the timeframe is 1, then this diff --git a/freqtrade/rpc/external_message_consumer.py b/freqtrade/rpc/external_message_consumer.py index aed5d9fb9..d028bc006 100644 --- a/freqtrade/rpc/external_message_consumer.py +++ b/freqtrade/rpc/external_message_consumer.py @@ -253,7 +253,7 @@ class ExternalMessageConsumer: # Now send any subsequent requests published to # this channel's stream async for request, _ in channel_stream: - logger.info(f"Sending request to channel - {channel} - {request}") + logger.debug(f"Sending request to channel - {channel} - {request}") await channel.send(request) async def _receive_messages( @@ -377,14 +377,14 @@ class ExternalMessageConsumer: pair, timeframe, candle_type = key if df.empty: - logger.info(f"Received Empty Dataframe for {key}") + logger.debug(f"Received Empty Dataframe for {key}") return # If set, remove the Entry and Exit signals from the Producer if self._emc_config.get('remove_entry_exit_signals', False): df = remove_entry_exit_signals(df) - logger.info(f"Received {len(df)} candle(s) for {key}") + logger.debug(f"Received {len(df)} candle(s) for {key}") if len(df) >= 999: # This is a full dataframe @@ -413,9 +413,9 @@ class ExternalMessageConsumer: ) if not did_append: - logger.info("Holes in data or no existing df, " - f"requesting {n_missing} candles " - f"for {key} from `{producer_name}`") + logger.debug("Holes in data or no existing df, " + f"requesting {n_missing} candles " + f"for {key} from `{producer_name}`") self.send_producer_request( producer_name, @@ -428,6 +428,6 @@ class ExternalMessageConsumer: ) return - logger.info( + logger.debug( f"Consumed message from `{producer_name}` " f"of type `RPCMessageType.ANALYZED_DF` for {key}") From d376bf4052f56dcceedf2d30121a1419a7369702 Mon Sep 17 00:00:00 2001 From: Timothy Pogue Date: Tue, 29 Nov 2022 12:22:06 -0700 Subject: [PATCH 060/795] fix indefinite reconnecting --- freqtrade/rpc/external_message_consumer.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/freqtrade/rpc/external_message_consumer.py b/freqtrade/rpc/external_message_consumer.py index d028bc006..05effb783 100644 --- a/freqtrade/rpc/external_message_consumer.py +++ b/freqtrade/rpc/external_message_consumer.py @@ -293,18 +293,11 @@ class ExternalMessageConsumer: logger.info(f"Connection to {channel} still alive, latency: {latency}ms") continue - except (websockets.exceptions.ConnectionClosed): - # Just eat the error and continue reconnecting - logger.warning(f"Disconnection in {channel} - retrying in {self.sleep_time}s") - await asyncio.sleep(self.sleep_time) - break - except Exception as e: # Just eat the error and continue reconnecting logger.warning(f"Ping error {channel} - {e} - retrying in {self.sleep_time}s") logger.debug(e, exc_info=e) - await asyncio.sleep(self.sleep_time) - break + raise def send_producer_request( self, From 0d5b2eed942922bffae0676d7870f2487f18ccec Mon Sep 17 00:00:00 2001 From: Timothy Pogue Date: Fri, 2 Dec 2022 12:07:48 -0700 Subject: [PATCH 061/795] fix same candle handling --- freqtrade/data/dataprovider.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 657d96df1..78d73b07d 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -194,7 +194,9 @@ class DataProvider: # and return True, 0 if local_last == incoming_first: existing_df.iloc[-1] = dataframe.iloc[0] - existing_df = existing_df.reset_index(drop=True) + existing_data = (existing_df.reset_index(drop=True), _) + + self.__producer_pairs_df[producer_name][pair_key] = existing_data return (True, 0) candle_difference = (incoming_first - local_last) / timeframe_delta From 49f6f40662d46bdfc2ca5006c96577df3db593b1 Mon Sep 17 00:00:00 2001 From: Timothy Pogue Date: Fri, 2 Dec 2022 12:08:42 -0700 Subject: [PATCH 062/795] remove comment --- freqtrade/rpc/external_message_consumer.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/freqtrade/rpc/external_message_consumer.py b/freqtrade/rpc/external_message_consumer.py index 05effb783..15312ba10 100644 --- a/freqtrade/rpc/external_message_consumer.py +++ b/freqtrade/rpc/external_message_consumer.py @@ -43,10 +43,6 @@ def schema_to_dict(schema: Union[WSMessageSchema, WSRequestSchema]): return schema.dict(exclude_none=True) -# def parse_message(message: Dict[str, Any], message_schema: Type[WSMessageSchema]): -# return message_schema.parse_obj(message) - - class ExternalMessageConsumer: """ The main controller class for consuming external messages from From f1ebaf4730606498d928f3f02ab5fcddfe87310d Mon Sep 17 00:00:00 2001 From: Timothy Pogue Date: Fri, 2 Dec 2022 12:28:27 -0700 Subject: [PATCH 063/795] fix tests --- freqtrade/rpc/external_message_consumer.py | 7 ++++--- tests/rpc/test_rpc_emc.py | 14 ++++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/freqtrade/rpc/external_message_consumer.py b/freqtrade/rpc/external_message_consumer.py index 15312ba10..743698b24 100644 --- a/freqtrade/rpc/external_message_consumer.py +++ b/freqtrade/rpc/external_message_consumer.py @@ -224,20 +224,21 @@ class ExternalMessageConsumer: websockets.exceptions.InvalidMessage ) as e: logger.error(f"Connection Refused - {e} retrying in {self.sleep_time}s") + await asyncio.sleep(self.sleep_time) + continue except ( websockets.exceptions.ConnectionClosedError, websockets.exceptions.ConnectionClosedOK ): # Just keep trying to connect again indefinitely - pass + await asyncio.sleep(self.sleep_time) + continue except Exception as e: # An unforseen error has occurred, log and continue logger.error("Unexpected error has occurred:") logger.exception(e) - - finally: await asyncio.sleep(self.sleep_time) continue diff --git a/tests/rpc/test_rpc_emc.py b/tests/rpc/test_rpc_emc.py index 93ae829d5..155239e94 100644 --- a/tests/rpc/test_rpc_emc.py +++ b/tests/rpc/test_rpc_emc.py @@ -94,7 +94,7 @@ def test_emc_handle_producer_message(patched_emc, caplog, ohlcv_history): assert log_has( f"Consumed message from `{producer_name}` of type `RPCMessageType.WHITELIST`", caplog) - # Test handle analyzed_df message + # Test handle analyzed_df single candle message df_message = { "type": "analyzed_df", "data": { @@ -106,8 +106,7 @@ def test_emc_handle_producer_message(patched_emc, caplog, ohlcv_history): patched_emc.handle_producer_message(test_producer, df_message) assert log_has(f"Received message of type `analyzed_df` from `{producer_name}`", caplog) - assert log_has( - f"Consumed message from `{producer_name}` of type `RPCMessageType.ANALYZED_DF`", caplog) + assert log_has_re(r"Holes in data or no existing df,.+", caplog) # Test unhandled message unhandled_message = {"type": "status", "data": "RUNNING"} @@ -183,7 +182,7 @@ async def test_emc_create_connection_success(default_conf, caplog, mocker): async with websockets.serve(eat, _TEST_WS_HOST, _TEST_WS_PORT): await emc._create_connection(test_producer, lock) - assert log_has_re(r"Producer connection success.+", caplog) + assert log_has_re(r"Connected to channel.+", caplog) finally: emc.shutdown() @@ -212,7 +211,8 @@ async def test_emc_create_connection_invalid_url(default_conf, caplog, mocker, h dp = DataProvider(default_conf, None, None, None) # Handle start explicitly to avoid messing with threading in tests - mocker.patch("freqtrade.rpc.external_message_consumer.ExternalMessageConsumer.start",) + mocker.patch("freqtrade.rpc.external_message_consumer.ExternalMessageConsumer.start") + mocker.patch("freqtrade.rpc.api_server.ws.channel.create_channel") emc = ExternalMessageConsumer(default_conf, dp) try: @@ -390,7 +390,9 @@ async def test_emc_receive_messages_timeout(default_conf, caplog, mocker): try: change_running(emc) loop.call_soon(functools.partial(change_running, emc=emc)) - await emc._receive_messages(TestChannel(), test_producer, lock) + + with pytest.raises(asyncio.TimeoutError): + await emc._receive_messages(TestChannel(), test_producer, lock) assert log_has_re(r"Ping error.+", caplog) finally: From 0602479f7d328094401ebe454fd4d33962b09a19 Mon Sep 17 00:00:00 2001 From: Timothy Pogue Date: Mon, 5 Dec 2022 13:11:07 -0700 Subject: [PATCH 064/795] minor changes, update candle appending to support overlaps --- freqtrade/data/dataprovider.py | 31 +++++++++++++--------- freqtrade/rpc/external_message_consumer.py | 22 ++++++++++----- freqtrade/rpc/rpc.py | 4 +-- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 78d73b07d..b889da17f 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -167,7 +167,8 @@ class DataProvider: producer_name: str = "default" ) -> Tuple[bool, int]: """ - Append a candle to the existing external dataframe + Append a candle to the existing external dataframe. The incoming dataframe + must have at least 1 candle. :param pair: pair to get the data for :param timeframe: Timeframe to get data for @@ -176,29 +177,32 @@ class DataProvider: """ pair_key = (pair, timeframe, candle_type) - if (producer_name not in self.__producer_pairs_df) \ - or (pair_key not in self.__producer_pairs_df[producer_name]): + if dataframe.empty: + # The incoming dataframe must have at least 1 candle + return (False, 0) + + if (producer_name not in self.__producer_pairs_df + or pair_key not in self.__producer_pairs_df[producer_name]): # We don't have data from this producer yet, # or we don't have data for this pair_key # return False and 1000 for the full df return (False, 1000) - existing_df, _ = self.__producer_pairs_df[producer_name][pair_key] + existing_df, la = self.__producer_pairs_df[producer_name][pair_key] + + # Iterate over any overlapping candles and update the values + for idx, candle in dataframe.iterrows(): + existing_df.iloc[ + existing_df['date'] == candle['date'] + ] = candle + + existing_df.reset_index(drop=True, inplace=True) # CHECK FOR MISSING CANDLES timeframe_delta = to_timedelta(timeframe) # Convert the timeframe to a timedelta for pandas local_last = existing_df.iloc[-1]['date'] # We want the last date from our copy of data incoming_first = dataframe.iloc[0]['date'] # We want the first date from the incoming data - # We have received this candle before, update our copy - # and return True, 0 - if local_last == incoming_first: - existing_df.iloc[-1] = dataframe.iloc[0] - existing_data = (existing_df.reset_index(drop=True), _) - - self.__producer_pairs_df[producer_name][pair_key] = existing_data - return (True, 0) - candle_difference = (incoming_first - local_last) / timeframe_delta # If the difference divided by the timeframe is 1, then this @@ -228,6 +232,7 @@ class DataProvider: # Only keep the last 1500 candles in memory existing = existing[-1500:] if len(existing) > 1500 else existing + existing.reset_index(drop=True, inplace=True) return existing diff --git a/freqtrade/rpc/external_message_consumer.py b/freqtrade/rpc/external_message_consumer.py index 743698b24..278f04a8e 100644 --- a/freqtrade/rpc/external_message_consumer.py +++ b/freqtrade/rpc/external_message_consumer.py @@ -36,6 +36,9 @@ class Producer(TypedDict): ws_token: str +FULL_DATAFRAME_THRESHOLD = 100 + + logger = logging.getLogger(__name__) @@ -376,8 +379,8 @@ class ExternalMessageConsumer: logger.debug(f"Received {len(df)} candle(s) for {key}") - if len(df) >= 999: - # This is a full dataframe + if len(df) >= FULL_DATAFRAME_THRESHOLD: + # This is likely a full dataframe # Add the dataframe to the dataprovider self._dp._add_external_df( pair, @@ -388,8 +391,8 @@ class ExternalMessageConsumer: producer_name=producer_name ) - elif len(df) < 999: - # This is n single candles + elif len(df) < FULL_DATAFRAME_THRESHOLD: + # This is likely n single candles # Have dataprovider append it to # the full datafame. If it can't, # request the missing candles @@ -403,9 +406,14 @@ class ExternalMessageConsumer: ) if not did_append: - logger.debug("Holes in data or no existing df, " - f"requesting {n_missing} candles " - f"for {key} from `{producer_name}`") + # We want an overlap in candles incase some data has changed + n_missing += 1 + # Set to None for all candles if we missed a full df's worth of candles + n_missing = n_missing if n_missing < FULL_DATAFRAME_THRESHOLD else 1500 + + logger.warning("Holes in data or no existing df, " + f"requesting {n_missing} candles " + f"for {key} from `{producer_name}`") self.send_producer_request( producer_name, diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 4ebedd6c4..331569de3 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -1062,7 +1062,7 @@ class RPC: self, pair: str, timeframe: str, - limit: Optional[int] = None + limit: Optional[int] ) -> Tuple[DataFrame, datetime]: """ Get the dataframe and last analyze from the dataprovider @@ -1083,7 +1083,7 @@ class RPC: def _ws_all_analysed_dataframes( self, pairlist: List[str], - limit: Optional[int] = None + limit: Optional[int] ) -> Generator[Dict[str, Any], None, None]: """ Get the analysed dataframes of each pair in the pairlist. From 6717dff19bb75015ff8ad8624fa0a82d3a961952 Mon Sep 17 00:00:00 2001 From: Timothy Pogue Date: Tue, 6 Dec 2022 16:00:28 -0700 Subject: [PATCH 065/795] update overlapping candle handling, move append to misc --- freqtrade/data/dataprovider.py | 48 ++++++++++++++++------------------ freqtrade/misc.py | 18 +++++++++++++ 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index b889da17f..8d81221b6 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -17,6 +17,7 @@ from freqtrade.data.history import load_pair_history from freqtrade.enums import CandleType, RPCMessageType, RunMode from freqtrade.exceptions import ExchangeError, OperationalException from freqtrade.exchange import Exchange, timeframe_to_seconds +from freqtrade.misc import append_candles_to_dataframe from freqtrade.rpc import RPCManager from freqtrade.util import PeriodicCache @@ -190,18 +191,30 @@ class DataProvider: existing_df, la = self.__producer_pairs_df[producer_name][pair_key] - # Iterate over any overlapping candles and update the values - for idx, candle in dataframe.iterrows(): - existing_df.iloc[ - existing_df['date'] == candle['date'] - ] = candle + # Handle overlapping candles + old_candles = existing_df[ + ~existing_df['date'].isin( + dataframe['date'] + ) + ] + overlapping_candles = existing_df[ + existing_df['date'].isin( + dataframe['date'] + ) + ] + new_candles = dataframe[ + ~dataframe['date'].isin( + existing_df['date'] + ) + ] - existing_df.reset_index(drop=True, inplace=True) + if overlapping_candles: + existing_df = concat([old_candles, overlapping_candles], axis=0) # CHECK FOR MISSING CANDLES timeframe_delta = to_timedelta(timeframe) # Convert the timeframe to a timedelta for pandas - local_last = existing_df.iloc[-1]['date'] # We want the last date from our copy of data - incoming_first = dataframe.iloc[0]['date'] # We want the first date from the incoming data + local_last = existing_df.iloc[-1]['date'] # We want the last date from our copy + incoming_first = new_candles.iloc[0]['date'] # We want the first date from the incoming candle_difference = (incoming_first - local_last) / timeframe_delta @@ -213,29 +226,12 @@ class DataProvider: if candle_difference > 1: return (False, candle_difference) - appended_df = self._append_candle_to_dataframe(existing_df, dataframe) + appended_df = append_candles_to_dataframe(existing_df, dataframe) # Everything is good, we appended self.__producer_pairs_df[producer_name][pair_key] = appended_df, last_analyzed return (True, 0) - def _append_candle_to_dataframe(self, existing: DataFrame, new: DataFrame) -> DataFrame: - """ - Append the `new` dataframe to the `existing` dataframe - - :param existing: The full dataframe you want appended to - :param new: The new dataframe containing the data you want appended - :returns: The dataframe with the new data in it - """ - if existing.iloc[-1]['date'] != new.iloc[-1]['date']: - existing = concat([existing, new]) - - # Only keep the last 1500 candles in memory - existing = existing[-1500:] if len(existing) > 1500 else existing - existing.reset_index(drop=True, inplace=True) - - return existing - def get_producer_df( self, pair: str, diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 2d2c7513a..93e8da6dd 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -301,3 +301,21 @@ def remove_entry_exit_signals(dataframe: pd.DataFrame): dataframe[SignalTagType.EXIT_TAG.value] = None return dataframe + + +def append_candles_to_dataframe(left: pd.DataFrame, right: pd.DataFrame) -> pd.DataFrame: + """ + Append the `right` dataframe to the `left` dataframe + + :param left: The full dataframe you want appended to + :param right: The new dataframe containing the data you want appended + :returns: The dataframe with the right data in it + """ + if left.iloc[-1]['date'] != right.iloc[-1]['date']: + left = pd.concat([left, right]) + + # Only keep the last 1500 candles in memory + left = left[-1500:] if len(left) > 1500 else left + left.reset_index(drop=True, inplace=True) + + return left From 414c0ce050e520855a6440176b89e4c76797a6e1 Mon Sep 17 00:00:00 2001 From: Timothy Pogue Date: Tue, 6 Dec 2022 16:02:28 -0700 Subject: [PATCH 066/795] change unused var --- freqtrade/data/dataprovider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 8d81221b6..3a6f74b97 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -189,7 +189,7 @@ class DataProvider: # return False and 1000 for the full df return (False, 1000) - existing_df, la = self.__producer_pairs_df[producer_name][pair_key] + existing_df, _ = self.__producer_pairs_df[producer_name][pair_key] # Handle overlapping candles old_candles = existing_df[ From 96edd31458e20237d65f98642c198b1cb13f8c4b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 10 Dec 2022 20:03:42 +0100 Subject: [PATCH 067/795] Test add_external_candle --- tests/data/test_dataprovider.py | 68 ++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py index 025e6d08a..862abfa0b 100644 --- a/tests/data/test_dataprovider.py +++ b/tests/data/test_dataprovider.py @@ -2,13 +2,13 @@ from datetime import datetime, timezone from unittest.mock import MagicMock import pytest -from pandas import DataFrame +from pandas import DataFrame, Timestamp from freqtrade.data.dataprovider import DataProvider from freqtrade.enums import CandleType, RunMode from freqtrade.exceptions import ExchangeError, OperationalException from freqtrade.plugins.pairlistmanager import PairListManager -from tests.conftest import get_patched_exchange +from tests.conftest import generate_test_data, get_patched_exchange @pytest.mark.parametrize('candle_type', [ @@ -412,3 +412,67 @@ def test_dp_send_msg(default_conf): dp = DataProvider(default_conf, None) dp.send_msg(msg, always_send=True) assert msg not in dp._msg_queue + + +def test_dp__add_external_candle(default_conf_usdt): + timeframe = '1h' + default_conf_usdt["timeframe"] = timeframe + dp = DataProvider(default_conf_usdt, None) + df = generate_test_data(timeframe, 24, '2022-01-01 00:00:00+00:00') + last_analyzed = datetime.now(timezone.utc) + + res = dp._add_external_candle('ETH/USDT', df, last_analyzed, timeframe, CandleType.SPOT) + assert res[0] is False + # Why 1000 ?? + assert res[1] == 1000 + + dp._add_external_df('ETH/USDT', df, last_analyzed, timeframe, CandleType.SPOT) + # BTC is not stored yet + res = dp._add_external_candle('BTC/USDT', df, last_analyzed, timeframe, CandleType.SPOT) + assert res[0] is False + df, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) + assert len(df) == 24 + + # Add the same dataframe again - dataframe size shall not change. + res = dp._add_external_candle('ETH/USDT', df, last_analyzed, timeframe, CandleType.SPOT) + assert res[0] is True + assert res[1] == 0 + df, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) + assert len(df) == 24 + + # Add a new day. + df2 = generate_test_data(timeframe, 24, '2022-01-02 00:00:00+00:00') + + res = dp._add_external_candle('ETH/USDT', df2, last_analyzed, timeframe, CandleType.SPOT) + assert res[0] is True + assert res[1] == 0 + df, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) + assert len(df) == 48 + + # Add a dataframe with a 12 hour offset - so 12 candles are overlapping, and 12 valid. + df3 = generate_test_data(timeframe, 24, '2022-01-02 12:00:00+00:00') + + res = dp._add_external_candle('ETH/USDT', df3, last_analyzed, timeframe, CandleType.SPOT) + assert res[0] is True + assert res[1] == 0 + df, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) + # New length = 48 + 12 (since we have a 12 hour offset). + assert len(df) == 60 + assert df.iloc[-1]['date'] == df3.iloc[-1]['date'] + assert df.iloc[-1]['date'] == Timestamp('2022-01-03 11:00:00+00:00') + + # Generate 1 new candle + df4 = generate_test_data(timeframe, 1, '2022-01-03 12:00:00+00:00') + res = dp._add_external_candle('ETH/USDT', df4, last_analyzed, timeframe, CandleType.SPOT) + # assert res[0] is True + # assert res[1] == 0 + df, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) + # New length = 61 + 1 + assert len(df) == 61 + + # Gap in the data ... + df4 = generate_test_data(timeframe, 1, '2022-01-05 00:00:00+00:00') + res = dp._add_external_candle('ETH/USDT', df4, last_analyzed, timeframe, CandleType.SPOT) + assert res[0] is False + # 36 hours - from 2022-01-03 12:00:00+00:00 to 2022-01-05 00:00:00+00:00 + assert res[1] == 36 From a693495a6d599fd7bdbec75337db3c44dc39c5b7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 11 Dec 2022 08:42:13 +0100 Subject: [PATCH 068/795] Improve external_candle aggregation --- freqtrade/data/dataprovider.py | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 3a6f74b97..10569e7c7 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -9,7 +9,7 @@ from collections import deque from datetime import datetime, timezone from typing import Any, Dict, List, Optional, Tuple -from pandas import DataFrame, concat, to_timedelta +from pandas import DataFrame, to_timedelta from freqtrade.configuration import TimeRange from freqtrade.constants import Config, ListPairsWithTimeframes, PairWithTimeframe @@ -191,30 +191,13 @@ class DataProvider: existing_df, _ = self.__producer_pairs_df[producer_name][pair_key] - # Handle overlapping candles - old_candles = existing_df[ - ~existing_df['date'].isin( - dataframe['date'] - ) - ] - overlapping_candles = existing_df[ - existing_df['date'].isin( - dataframe['date'] - ) - ] - new_candles = dataframe[ - ~dataframe['date'].isin( - existing_df['date'] - ) - ] - - if overlapping_candles: - existing_df = concat([old_candles, overlapping_candles], axis=0) - # CHECK FOR MISSING CANDLES timeframe_delta = to_timedelta(timeframe) # Convert the timeframe to a timedelta for pandas local_last = existing_df.iloc[-1]['date'] # We want the last date from our copy - incoming_first = new_candles.iloc[0]['date'] # We want the first date from the incoming + incoming_first = dataframe.iloc[0]['date'] # We want the first date from the incoming + + # Remove existing candles that are newer than the incoming first candle + existing_df1 = existing_df[existing_df['date'] < incoming_first] candle_difference = (incoming_first - local_last) / timeframe_delta @@ -225,8 +208,10 @@ class DataProvider: # so return False and candle_difference. if candle_difference > 1: return (False, candle_difference) - - appended_df = append_candles_to_dataframe(existing_df, dataframe) + if existing_df1.empty: + appended_df = dataframe + else: + appended_df = append_candles_to_dataframe(existing_df1, dataframe) # Everything is good, we appended self.__producer_pairs_df[producer_name][pair_key] = appended_df, last_analyzed From 1c0c4fd4206bcafc59ad70a5bb5890cf657a928d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 11 Dec 2022 08:49:35 +0100 Subject: [PATCH 069/795] Improve test --- tests/data/test_dataprovider.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py index 862abfa0b..cce483c07 100644 --- a/tests/data/test_dataprovider.py +++ b/tests/data/test_dataprovider.py @@ -469,6 +469,8 @@ def test_dp__add_external_candle(default_conf_usdt): df, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) # New length = 61 + 1 assert len(df) == 61 + assert df.iloc[-2]['date'] == Timestamp('2022-01-03 11:00:00+00:00') + assert df.iloc[-1]['date'] == Timestamp('2022-01-03 12:00:00+00:00') # Gap in the data ... df4 = generate_test_data(timeframe, 1, '2022-01-05 00:00:00+00:00') @@ -476,3 +478,13 @@ def test_dp__add_external_candle(default_conf_usdt): assert res[0] is False # 36 hours - from 2022-01-03 12:00:00+00:00 to 2022-01-05 00:00:00+00:00 assert res[1] == 36 + df, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) + # New length = 61 + 1 + assert len(df) == 61 + + # Empty dataframe + df4 = generate_test_data(timeframe, 0, '2022-01-05 00:00:00+00:00') + res = dp._add_external_candle('ETH/USDT', df4, last_analyzed, timeframe, CandleType.SPOT) + assert res[0] is False + # 36 hours - from 2022-01-03 12:00:00+00:00 to 2022-01-05 00:00:00+00:00 + assert res[1] == 0 From 0dd3836cc7a6c3ac8b5863b8267db889c7666d14 Mon Sep 17 00:00:00 2001 From: Timothy Pogue Date: Mon, 12 Dec 2022 22:46:19 -0700 Subject: [PATCH 070/795] fix rpc method docstring --- freqtrade/rpc/rpc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 331569de3..ceb791b46 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -1087,8 +1087,8 @@ class RPC: ) -> Generator[Dict[str, Any], None, None]: """ Get the analysed dataframes of each pair in the pairlist. - Limit size of dataframe if specified. - If candles, only return the candles specified. + If specified, only return the most recent `limit` candles for + each dataframe. :param pairlist: A list of pairs to get :param limit: If an integer, limits the size of dataframe From c042d0146e29baee22b42487b9bdded223754b88 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 13 Dec 2022 17:14:11 +0000 Subject: [PATCH 071/795] Don't run gc_setup during tests --- tests/conftest.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index f3fc908e7..c9af5a171 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -408,6 +408,11 @@ def create_mock_trades_usdt(fee, is_short: Optional[bool] = False, use_db: bool Trade.commit() +@pytest.fixture(autouse=True) +def patch_gc(mocker) -> None: + mocker.patch("freqtrade.main.gc_set_threshold") + + @pytest.fixture(autouse=True) def patch_coingekko(mocker) -> None: """ From fed46d330ff4b8c2ed9aff97b311148f746bb99d Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 13 Dec 2022 18:14:56 +0100 Subject: [PATCH 072/795] Revert "Bump scikit-learn from 1.1.3 to 1.2.0" --- requirements-freqai.txt | 2 +- requirements-hyperopt.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements-freqai.txt b/requirements-freqai.txt index 57dd8dbb4..215a312bf 100644 --- a/requirements-freqai.txt +++ b/requirements-freqai.txt @@ -3,7 +3,7 @@ -r requirements-plot.txt # Required for freqai -scikit-learn==1.2.0 +scikit-learn==1.1.3 joblib==1.2.0 catboost==1.1.1; platform_machine != 'aarch64' lightgbm==3.3.3 diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 8fc58812b..fcae2cbdd 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -3,7 +3,7 @@ # Required for hyperopt scipy==1.9.3 -scikit-learn==1.2.0 +scikit-learn==1.1.3 scikit-optimize==0.9.0 filelock==3.8.2 progressbar2==4.2.0 From 1d92db7805c1f13bafd61177a9f451e1b612751f Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 13 Dec 2022 19:23:37 +0100 Subject: [PATCH 073/795] Change CI to actually run one 2 randomized point. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b15451a64..0a787bc47 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,7 +88,7 @@ jobs: run: | cp config_examples/config_bittrex.example.json config.json freqtrade create-userdir --userdir user_data - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily --print-all + freqtrade hyperopt --datadir tests/testdata -e 6 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily --print-all - name: Flake8 run: | From d52c1c75544aee98f06be81cbce74d2fb45500b5 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Tue, 13 Dec 2022 20:21:06 +0100 Subject: [PATCH 074/795] Add unit tests --- docs/includes/pairlists.md | 2 +- freqtrade/plugins/pairlist/RemotePairList.py | 29 ++--- tests/plugins/test_remotepairlist.py | 123 +++++++++++++++++++ 3 files changed, 139 insertions(+), 15 deletions(-) create mode 100644 tests/plugins/test_remotepairlist.py diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index c12683e75..3a6ab7a3c 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -2,7 +2,7 @@ Pairlist Handlers define the list of pairs (pairlist) that the bot should trade. They are configured in the `pairlists` section of the configuration settings. -In your configuration, you can use Static Pairlist (defined by the [`StaticPairList`](#static-pair-list) Pairlist Handler), Dynamic Pairlist (defined by the [`VolumePairList`](#volume-pair-list) Pairlist Handler). +In your configuration, you can use Static Pairlist (defined by the [`StaticPairList`](#static-pair-list) Pairlist Handler) and Dynamic Pairlist (defined by the [`VolumePairList`](#volume-pair-list) Pairlist Handler). Additionally, [`AgeFilter`](#agefilter), [`PrecisionFilter`](#precisionfilter), [`PriceFilter`](#pricefilter), [`ShuffleFilter`](#shufflefilter), [`SpreadFilter`](#spreadfilter) and [`VolatilityFilter`](#volatilityfilter) act as Pairlist Filters, removing certain pairs and/or moving their positions in the pairlist. diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index 7ef038da7..418ac5b0b 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -74,21 +74,22 @@ class RemotePairList(IPairList): info = "Pairlist" try: - with requests.get(self._pairlist_url, headers=headers, - timeout=self._read_timeout) as response: - content_type = response.headers.get('content-type') - time_elapsed = response.elapsed.total_seconds() + response = requests.get(self._pairlist_url, headers=headers, + timeout=self._read_timeout) + content_type = response.headers.get('content-type') + time_elapsed = response.elapsed.total_seconds() - if "application/json" in str(content_type): - jsonparse = response.json() - pairlist = jsonparse['pairs'] - info = jsonparse.get('info', '')[:1000] - else: - raise OperationalException( - 'Remotepairlist is not of type JSON abort') + print(response) - self._refresh_period = jsonparse.get('refresh_period', self._refresh_period) - self._pair_cache = TTLCache(maxsize=1, ttl=self._refresh_period) + if "application/json" in str(content_type): + jsonparse = response.json() + pairlist = jsonparse['pairs'] + info = jsonparse.get('info', '') + else: + raise OperationalException('RemotePairList is not of type JSON abort ') + + self._refresh_period = jsonparse.get('refresh_period', self._refresh_period) + self._pair_cache = TTLCache(maxsize=1, ttl=self._refresh_period) except requests.exceptions.RequestException: self.log_once(f'Was not able to fetch pairlist from:' @@ -127,7 +128,7 @@ class RemotePairList(IPairList): # Load the JSON data into a dictionary jsonparse = json.load(json_file) pairlist = jsonparse['pairs'] - info = jsonparse.get('info', '')[:1000] + info = jsonparse.get('info', '') self._refresh_period = jsonparse.get('refresh_period', self._refresh_period) self._pair_cache = TTLCache(maxsize=1, ttl=self._refresh_period) diff --git a/tests/plugins/test_remotepairlist.py b/tests/plugins/test_remotepairlist.py new file mode 100644 index 000000000..743534bc3 --- /dev/null +++ b/tests/plugins/test_remotepairlist.py @@ -0,0 +1,123 @@ +from unittest.mock import MagicMock + +import pytest + +from freqtrade.exceptions import OperationalException +from freqtrade.plugins.pairlist.RemotePairList import RemotePairList +from freqtrade.plugins.pairlistmanager import PairListManager +from tests.conftest import get_patched_exchange, get_patched_freqtradebot + + +@pytest.fixture(scope="function") +def rpl_config(default_conf): + default_conf['stake_currency'] = 'USDT' + + default_conf['exchange']['pair_whitelist'] = [ + 'ETH/USDT', + 'BTC/USDT', + ] + default_conf['exchange']['pair_blacklist'] = [ + 'BLK/USDT' + ] + return default_conf + + +def test_fetch_pairlist_mock_response_html(mocker, rpl_config): + mock_response = MagicMock() + mock_response.headers = {'content-type': 'text/html'} + mocker.patch('requests.get', return_value=mock_response) + + rpl_config['pairlists'] = [ + { + "method": "RemotePairList", + "pairlist_url": "http://example.com/pairlist", + "number_assets": 10, + "read_timeout": 10, + "keep_pairlist_on_failure": True, + } + ] + + exchange = get_patched_exchange(mocker, rpl_config) + pairlistmanager = PairListManager(exchange, rpl_config) + + mocker.patch("freqtrade.plugins.pairlist.RemotePairList.requests.get", + return_value=mock_response) + remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config, + rpl_config['pairlists'][0], 0) + + with pytest.raises(OperationalException, match='RemotePairList is not of type JSON abort'): + remote_pairlist.fetch_pairlist() + + +def test_remote_pairlist_init_no_pairlist_url(mocker, rpl_config): + + rpl_config['pairlists'] = [ + { + "method": "RemotePairList", + "number_assets": 10, + "keep_pairlist_on_failure": True, + } + ] + + get_patched_exchange(mocker, rpl_config) + with pytest.raises(OperationalException, match=r'`pairlist_url` not specified.' + r' Please check your configuration for "pairlist.config.pairlist_url"'): + get_patched_freqtradebot(mocker, rpl_config) + + +def test_remote_pairlist_init_no_number_assets(mocker, rpl_config): + + rpl_config['pairlists'] = [ + { + "method": "RemotePairList", + "pairlist_url": "http://example.com/pairlist", + "keep_pairlist_on_failure": True, + } + ] + + get_patched_exchange(mocker, rpl_config) + + with pytest.raises(OperationalException, match=r'`number_assets` not specified. ' + 'Please check your configuration for "pairlist.config.number_assets"'): + get_patched_freqtradebot(mocker, rpl_config) + + +def test_fetch_pairlist_mock_response_valid(mocker, rpl_config): + + rpl_config['pairlists'] = [ + { + "method": "RemotePairList", + "pairlist_url": "http://example.com/pairlist", + "number_assets": 10, + "refresh_period": 10, + "read_timeout": 10, + "keep_pairlist_on_failure": True, + } + ] + + mock_response = MagicMock() + + mock_response.json.return_value = { + "pairs": ["ETH/BTC", "XRP/BTC", "LTC/BTC", "EOS/BTC"], + "info": "Mock pairlist response", + "refresh_period": 60 + } + + mock_response.headers = { + "content-type": "application/json" + } + + mock_response.elapsed.total_seconds.return_value = 0.4 + mocker.patch("freqtrade.plugins.pairlist.RemotePairList.requests.get", + return_value=mock_response) + + exchange = get_patched_exchange(mocker, rpl_config) + pairlistmanager = PairListManager(exchange, rpl_config) + remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config, + rpl_config['pairlists'][0], 0) + pairs, time_elapsed, info = remote_pairlist.fetch_pairlist() + + assert pairs == ["ETH/BTC", "XRP/BTC", "LTC/BTC", "EOS/BTC"] + assert time_elapsed == 0.4 + assert info == "Mock pairlist response" + assert remote_pairlist._refresh_period == 60 From 7f3524949c17afa87f52d8023770d8a974884b72 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Tue, 13 Dec 2022 21:00:23 +0100 Subject: [PATCH 075/795] - print --- freqtrade/plugins/pairlist/RemotePairList.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index 418ac5b0b..e46ac0419 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -79,8 +79,6 @@ class RemotePairList(IPairList): content_type = response.headers.get('content-type') time_elapsed = response.elapsed.total_seconds() - print(response) - if "application/json" in str(content_type): jsonparse = response.json() pairlist = jsonparse['pairs'] From 97fee37072dd28a8981131523711cfc7cbb9a3b6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 14 Dec 2022 07:22:41 +0100 Subject: [PATCH 076/795] Improve emc test --- freqtrade/rpc/external_message_consumer.py | 3 +-- tests/rpc/test_rpc_emc.py | 25 +++++++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/freqtrade/rpc/external_message_consumer.py b/freqtrade/rpc/external_message_consumer.py index 278f04a8e..67b323fb2 100644 --- a/freqtrade/rpc/external_message_consumer.py +++ b/freqtrade/rpc/external_message_consumer.py @@ -411,8 +411,7 @@ class ExternalMessageConsumer: # Set to None for all candles if we missed a full df's worth of candles n_missing = n_missing if n_missing < FULL_DATAFRAME_THRESHOLD else 1500 - logger.warning("Holes in data or no existing df, " - f"requesting {n_missing} candles " + logger.warning("Holes in data or no existing df, requesting {n_missing} candles " f"for {key} from `{producer_name}`") self.send_producer_request( diff --git a/tests/rpc/test_rpc_emc.py b/tests/rpc/test_rpc_emc.py index 155239e94..e1537ec9e 100644 --- a/tests/rpc/test_rpc_emc.py +++ b/tests/rpc/test_rpc_emc.py @@ -83,6 +83,7 @@ def test_emc_init(patched_emc): def test_emc_handle_producer_message(patched_emc, caplog, ohlcv_history): test_producer = {"name": "test", "url": "ws://test", "ws_token": "test"} producer_name = test_producer['name'] + invalid_msg = r"Invalid message .+" caplog.set_level(logging.DEBUG) @@ -119,7 +120,8 @@ def test_emc_handle_producer_message(patched_emc, caplog, ohlcv_history): malformed_message = {"type": "whitelist", "data": {"pair": "BTC/USDT"}} patched_emc.handle_producer_message(test_producer, malformed_message) - assert log_has_re(r"Invalid message .+", caplog) + assert log_has_re(invalid_msg, caplog) + caplog.clear() malformed_message = { "type": "analyzed_df", @@ -132,13 +134,30 @@ def test_emc_handle_producer_message(patched_emc, caplog, ohlcv_history): patched_emc.handle_producer_message(test_producer, malformed_message) assert log_has(f"Received message of type `analyzed_df` from `{producer_name}`", caplog) - assert log_has_re(r"Invalid message .+", caplog) + assert log_has_re(invalid_msg, caplog) + caplog.clear() + + # Empty dataframe + malformed_message = { + "type": "analyzed_df", + "data": { + "key": ("BTC/USDT", "5m", "spot"), + "df": ohlcv_history.loc[ohlcv_history['open'] < 0], + "la": datetime.now(timezone.utc) + } + } + patched_emc.handle_producer_message(test_producer, malformed_message) + + assert log_has(f"Received message of type `analyzed_df` from `{producer_name}`", caplog) + assert not log_has_re(invalid_msg, caplog) + assert log_has_re(r"Received Empty Dataframe for.+", caplog) caplog.clear() malformed_message = {"some": "stuff"} patched_emc.handle_producer_message(test_producer, malformed_message) - assert log_has_re(r"Invalid message .+", caplog) + assert log_has_re(invalid_msg, caplog) + caplog.clear() caplog.clear() malformed_message = {"type": "whitelist", "data": None} From de19d1cfbba4f7ca0c356ed840093950c74f6434 Mon Sep 17 00:00:00 2001 From: initrv <37817561+initrv@users.noreply.github.com> Date: Wed, 14 Dec 2022 13:36:07 +0300 Subject: [PATCH 077/795] fix doc minimal_roi --- docs/strategy-customization.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index c006bf12c..0fb35ce89 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -363,9 +363,9 @@ class AwesomeStrategy(IStrategy): timeframe = "1d" timeframe_mins = timeframe_to_minutes(timeframe) minimal_roi = { - "0": 0.05, # 5% for the first 3 candles - str(timeframe_mins * 3)): 0.02, # 2% after 3 candles - str(timeframe_mins * 6)): 0.01, # 1% After 6 candles + "0": 0.05, # 5% for the first 3 candles + str(timeframe_mins * 3): 0.02, # 2% after 3 candles + str(timeframe_mins * 6): 0.01, # 1% After 6 candles } ``` From 2285ca7d2a214c811c17e371e4780216d70760dc Mon Sep 17 00:00:00 2001 From: robcaulk Date: Wed, 14 Dec 2022 18:22:20 +0100 Subject: [PATCH 078/795] add dp to multiproc --- freqtrade/freqai/RL/BaseReinforcementLearningModel.py | 6 ++++-- .../prediction_models/ReinforcementLearner_multiproc.py | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/freqtrade/freqai/RL/BaseReinforcementLearningModel.py b/freqtrade/freqai/RL/BaseReinforcementLearningModel.py index 5e9b81108..b77f21d58 100644 --- a/freqtrade/freqai/RL/BaseReinforcementLearningModel.py +++ b/freqtrade/freqai/RL/BaseReinforcementLearningModel.py @@ -24,6 +24,7 @@ from freqtrade.freqai.RL.Base5ActionRLEnv import Actions, Base5ActionRLEnv from freqtrade.freqai.RL.BaseEnvironment import BaseActions, Positions from freqtrade.freqai.RL.TensorboardCallback import TensorboardCallback from freqtrade.persistence import Trade +from freqtrade.data.dataprovider import DataProvider logger = logging.getLogger(__name__) @@ -384,7 +385,7 @@ class BaseReinforcementLearningModel(IFreqaiModel): def make_env(MyRLEnv: Type[gym.Env], env_id: str, rank: int, seed: int, train_df: DataFrame, price: DataFrame, reward_params: Dict[str, int], window_size: int, monitor: bool = False, - config: Dict[str, Any] = {}) -> Callable: + config: Dict[str, Any] = {}, dp: DataProvider = None) -> Callable: """ Utility function for multiprocessed env. @@ -398,7 +399,8 @@ def make_env(MyRLEnv: Type[gym.Env], env_id: str, rank: int, def _init() -> gym.Env: env = MyRLEnv(df=train_df, prices=price, window_size=window_size, - reward_kwargs=reward_params, id=env_id, seed=seed + rank, config=config) + reward_kwargs=reward_params, id=env_id, seed=seed + rank, + config=config, dp=dp) if monitor: env = Monitor(env) return env diff --git a/freqtrade/freqai/prediction_models/ReinforcementLearner_multiproc.py b/freqtrade/freqai/prediction_models/ReinforcementLearner_multiproc.py index 32a2a2076..c9b824978 100644 --- a/freqtrade/freqai/prediction_models/ReinforcementLearner_multiproc.py +++ b/freqtrade/freqai/prediction_models/ReinforcementLearner_multiproc.py @@ -37,14 +37,14 @@ class ReinforcementLearner_multiproc(ReinforcementLearner): env_id = "train_env" self.train_env = SubprocVecEnv([make_env(self.MyRLEnv, env_id, i, 1, train_df, prices_train, self.reward_params, self.CONV_WIDTH, monitor=True, - config=self.config) for i + config=self.config, dp=self.data_provider) for i in range(self.max_threads)]) eval_env_id = 'eval_env' self.eval_env = SubprocVecEnv([make_env(self.MyRLEnv, eval_env_id, i, 1, test_df, prices_test, self.reward_params, self.CONV_WIDTH, monitor=True, - config=self.config) for i + config=self.config, dp=self.data_provider) for i in range(self.max_threads)]) self.eval_callback = EvalCallback(self.eval_env, deterministic=True, render=False, eval_freq=len(train_df), From dac1c8ab894c345649e158a14105bac8f76e2c35 Mon Sep 17 00:00:00 2001 From: robcaulk Date: Wed, 14 Dec 2022 18:28:52 +0100 Subject: [PATCH 079/795] fix isort --- freqtrade/freqai/RL/BaseReinforcementLearningModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqai/RL/BaseReinforcementLearningModel.py b/freqtrade/freqai/RL/BaseReinforcementLearningModel.py index b77f21d58..0231124ff 100644 --- a/freqtrade/freqai/RL/BaseReinforcementLearningModel.py +++ b/freqtrade/freqai/RL/BaseReinforcementLearningModel.py @@ -17,6 +17,7 @@ from stable_baselines3.common.monitor import Monitor from stable_baselines3.common.utils import set_random_seed from stable_baselines3.common.vec_env import SubprocVecEnv +from freqtrade.data.dataprovider import DataProvider from freqtrade.exceptions import OperationalException from freqtrade.freqai.data_kitchen import FreqaiDataKitchen from freqtrade.freqai.freqai_interface import IFreqaiModel @@ -24,7 +25,6 @@ from freqtrade.freqai.RL.Base5ActionRLEnv import Actions, Base5ActionRLEnv from freqtrade.freqai.RL.BaseEnvironment import BaseActions, Positions from freqtrade.freqai.RL.TensorboardCallback import TensorboardCallback from freqtrade.persistence import Trade -from freqtrade.data.dataprovider import DataProvider logger = logging.getLogger(__name__) From fa260e6560591d848189197362e69806396eb1bb Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 14 Dec 2022 19:56:54 +0100 Subject: [PATCH 080/795] Move "replace or append" decision to dataprovider --- freqtrade/constants.py | 1 + freqtrade/data/dataprovider.py | 29 ++++++++-- freqtrade/rpc/external_message_consumer.py | 67 ++++++++-------------- 3 files changed, 50 insertions(+), 47 deletions(-) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index ca1be1d6a..ff6cc7c67 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -61,6 +61,7 @@ USERPATH_FREQAIMODELS = 'freqaimodels' TELEGRAM_SETTING_OPTIONS = ['on', 'off', 'silent'] WEBHOOK_FORMAT_OPTIONS = ['form', 'json', 'raw'] +FULL_DATAFRAME_THRESHOLD = 100 ENV_VAR_PREFIX = 'FREQTRADE__' diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 10569e7c7..b46f4e881 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -12,7 +12,8 @@ from typing import Any, Dict, List, Optional, Tuple from pandas import DataFrame, to_timedelta from freqtrade.configuration import TimeRange -from freqtrade.constants import Config, ListPairsWithTimeframes, PairWithTimeframe +from freqtrade.constants import (FULL_DATAFRAME_THRESHOLD, Config, ListPairsWithTimeframes, + PairWithTimeframe) from freqtrade.data.history import load_pair_history from freqtrade.enums import CandleType, RPCMessageType, RunMode from freqtrade.exceptions import ExchangeError, OperationalException @@ -132,7 +133,7 @@ class DataProvider: 'data': pair_key, }) - def _add_external_df( + def _replace_external_df( self, pair: str, dataframe: DataFrame, @@ -158,7 +159,7 @@ class DataProvider: self.__producer_pairs_df[producer_name][pair_key] = (dataframe, _last_analyzed) logger.debug(f"External DataFrame for {pair_key} from {producer_name} added.") - def _add_external_candle( + def _add_external_df( self, pair: str, dataframe: DataFrame, @@ -182,6 +183,19 @@ class DataProvider: # The incoming dataframe must have at least 1 candle return (False, 0) + if len(dataframe) >= FULL_DATAFRAME_THRESHOLD: + # This is likely a full dataframe + # Add the dataframe to the dataprovider + self._add_external_df( + pair, + dataframe, + last_analyzed=last_analyzed, + timeframe=timeframe, + candle_type=candle_type, + producer_name=producer_name + ) + return (True, 0) + if (producer_name not in self.__producer_pairs_df or pair_key not in self.__producer_pairs_df[producer_name]): # We don't have data from this producer yet, @@ -214,7 +228,14 @@ class DataProvider: appended_df = append_candles_to_dataframe(existing_df1, dataframe) # Everything is good, we appended - self.__producer_pairs_df[producer_name][pair_key] = appended_df, last_analyzed + self._add_external_df( + pair, + appended_df, + last_analyzed=last_analyzed, + timeframe=timeframe, + candle_type=candle_type, + producer_name=producer_name + ) return (True, 0) def get_producer_df( diff --git a/freqtrade/rpc/external_message_consumer.py b/freqtrade/rpc/external_message_consumer.py index 67b323fb2..e888191ea 100644 --- a/freqtrade/rpc/external_message_consumer.py +++ b/freqtrade/rpc/external_message_consumer.py @@ -13,6 +13,7 @@ from typing import TYPE_CHECKING, Any, Callable, Dict, List, TypedDict, Union import websockets from pydantic import ValidationError +from freqtrade.constants import FULL_DATAFRAME_THRESHOLD from freqtrade.data.dataprovider import DataProvider from freqtrade.enums import RPCMessageType from freqtrade.misc import remove_entry_exit_signals @@ -36,9 +37,6 @@ class Producer(TypedDict): ws_token: str -FULL_DATAFRAME_THRESHOLD = 100 - - logger = logging.getLogger(__name__) @@ -379,51 +377,34 @@ class ExternalMessageConsumer: logger.debug(f"Received {len(df)} candle(s) for {key}") - if len(df) >= FULL_DATAFRAME_THRESHOLD: - # This is likely a full dataframe - # Add the dataframe to the dataprovider - self._dp._add_external_df( - pair, - df, - last_analyzed=la, - timeframe=timeframe, - candle_type=candle_type, - producer_name=producer_name + did_append, n_missing = self._dp._add_external_df( + pair, + df, + last_analyzed=la, + timeframe=timeframe, + candle_type=candle_type, + producer_name=producer_name ) - elif len(df) < FULL_DATAFRAME_THRESHOLD: - # This is likely n single candles - # Have dataprovider append it to - # the full datafame. If it can't, - # request the missing candles - did_append, n_missing = self._dp._add_external_candle( - pair, - df, - last_analyzed=la, - timeframe=timeframe, - candle_type=candle_type, - producer_name=producer_name - ) + if not did_append: + # We want an overlap in candles incase some data has changed + n_missing += 1 + # Set to None for all candles if we missed a full df's worth of candles + n_missing = n_missing if n_missing < FULL_DATAFRAME_THRESHOLD else 1500 - if not did_append: - # We want an overlap in candles incase some data has changed - n_missing += 1 - # Set to None for all candles if we missed a full df's worth of candles - n_missing = n_missing if n_missing < FULL_DATAFRAME_THRESHOLD else 1500 + logger.warning(f"Holes in data or no existing df, requesting {n_missing} candles " + f"for {key} from `{producer_name}`") - logger.warning("Holes in data or no existing df, requesting {n_missing} candles " - f"for {key} from `{producer_name}`") - - self.send_producer_request( - producer_name, - WSAnalyzedDFRequest( - data={ - "limit": n_missing, - "pair": pair - } - ) + self.send_producer_request( + producer_name, + WSAnalyzedDFRequest( + data={ + "limit": n_missing, + "pair": pair + } ) - return + ) + return logger.debug( f"Consumed message from `{producer_name}` " From 2018da07677f6343bef7a28eb8c4782032fbb508 Mon Sep 17 00:00:00 2001 From: Emre Date: Wed, 14 Dec 2022 22:03:05 +0300 Subject: [PATCH 081/795] Add env_info dict to base environment --- freqtrade/freqai/RL/BaseEnvironment.py | 17 +++++------------ .../freqai/RL/BaseReinforcementLearningModel.py | 16 +++++++++++----- .../ReinforcementLearner_multiproc.py | 11 +++++++++-- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/freqtrade/freqai/RL/BaseEnvironment.py b/freqtrade/freqai/RL/BaseEnvironment.py index 5a5a950e7..887910006 100644 --- a/freqtrade/freqai/RL/BaseEnvironment.py +++ b/freqtrade/freqai/RL/BaseEnvironment.py @@ -11,9 +11,6 @@ from gym import spaces from gym.utils import seeding from pandas import DataFrame -from freqtrade.data.dataprovider import DataProvider -from freqtrade.enums import RunMode - logger = logging.getLogger(__name__) @@ -48,7 +45,7 @@ class BaseEnvironment(gym.Env): def __init__(self, df: DataFrame = DataFrame(), prices: DataFrame = DataFrame(), reward_kwargs: dict = {}, window_size=10, starting_point=True, id: str = 'baseenv-1', seed: int = 1, config: dict = {}, - dp: Optional[DataProvider] = None): + env_info: dict = {}): """ Initializes the training/eval environment. :param df: dataframe of features @@ -59,7 +56,7 @@ class BaseEnvironment(gym.Env): :param id: string id of the environment (used in backend for multiprocessed env) :param seed: Sets the seed of the environment higher in the gym.Env object :param config: Typical user configuration file - :param dp: dataprovider from freqtrade + :param env_info: Environment info dictionary, used to pass live status, fee, etc. """ self.config = config self.rl_config = config['freqai']['rl_config'] @@ -71,17 +68,13 @@ class BaseEnvironment(gym.Env): self.compound_trades = config['stake_amount'] == 'unlimited' if self.config.get('fee', None) is not None: self.fee = self.config['fee'] - elif dp is not None: - self.fee = dp._exchange.get_fee(symbol=dp.current_whitelist()[0]) # type: ignore else: - self.fee = 0.0015 + self.fee = env_info.get('fee', 0.0015) # set here to default 5Ac, but all children envs can override this self.actions: Type[Enum] = BaseActions self.tensorboard_metrics: dict = {} - self.live: bool = False - if dp: - self.live = dp.runmode in (RunMode.DRY_RUN, RunMode.LIVE) + self.live = env_info.get('live', False) if not self.live and self.add_state_info: self.add_state_info = False logger.warning("add_state_info is not available in backtesting. Deactivating.") @@ -213,7 +206,7 @@ class BaseEnvironment(gym.Env): """ features_window = self.signal_features[( self._current_tick - self.window_size):self._current_tick] - if self.add_state_info and self.live: + if self.add_state_info: features_and_state = DataFrame(np.zeros((len(features_window), 3)), columns=['current_profit_pct', 'position', diff --git a/freqtrade/freqai/RL/BaseReinforcementLearningModel.py b/freqtrade/freqai/RL/BaseReinforcementLearningModel.py index b77f21d58..a41f02cba 100644 --- a/freqtrade/freqai/RL/BaseReinforcementLearningModel.py +++ b/freqtrade/freqai/RL/BaseReinforcementLearningModel.py @@ -17,6 +17,7 @@ from stable_baselines3.common.monitor import Monitor from stable_baselines3.common.utils import set_random_seed from stable_baselines3.common.vec_env import SubprocVecEnv +from freqtrade.enums import RunMode from freqtrade.exceptions import OperationalException from freqtrade.freqai.data_kitchen import FreqaiDataKitchen from freqtrade.freqai.freqai_interface import IFreqaiModel @@ -24,7 +25,6 @@ from freqtrade.freqai.RL.Base5ActionRLEnv import Actions, Base5ActionRLEnv from freqtrade.freqai.RL.BaseEnvironment import BaseActions, Positions from freqtrade.freqai.RL.TensorboardCallback import TensorboardCallback from freqtrade.persistence import Trade -from freqtrade.data.dataprovider import DataProvider logger = logging.getLogger(__name__) @@ -144,18 +144,24 @@ class BaseReinforcementLearningModel(IFreqaiModel): train_df = data_dictionary["train_features"] test_df = data_dictionary["test_features"] + env_info = {"live": False} + if self.data_provider: + env_info["live"] = self.data_provider.runmode in (RunMode.DRY_RUN, RunMode.LIVE) + env_info["fee"] = self.data_provider._exchange \ + .get_fee(symbol=self.data_provider.current_whitelist()[0]) # type: ignore + self.train_env = self.MyRLEnv(df=train_df, prices=prices_train, window_size=self.CONV_WIDTH, reward_kwargs=self.reward_params, config=self.config, - dp=self.data_provider) + env_info=env_info) self.eval_env = Monitor(self.MyRLEnv(df=test_df, prices=prices_test, window_size=self.CONV_WIDTH, reward_kwargs=self.reward_params, config=self.config, - dp=self.data_provider)) + env_info=env_info)) self.eval_callback = EvalCallback(self.eval_env, deterministic=True, render=False, eval_freq=len(train_df), best_model_save_path=str(dk.data_path)) @@ -385,7 +391,7 @@ class BaseReinforcementLearningModel(IFreqaiModel): def make_env(MyRLEnv: Type[gym.Env], env_id: str, rank: int, seed: int, train_df: DataFrame, price: DataFrame, reward_params: Dict[str, int], window_size: int, monitor: bool = False, - config: Dict[str, Any] = {}, dp: DataProvider = None) -> Callable: + config: Dict[str, Any] = {}, env_info: Dict[str, Any] = {}) -> Callable: """ Utility function for multiprocessed env. @@ -400,7 +406,7 @@ def make_env(MyRLEnv: Type[gym.Env], env_id: str, rank: int, env = MyRLEnv(df=train_df, prices=price, window_size=window_size, reward_kwargs=reward_params, id=env_id, seed=seed + rank, - config=config, dp=dp) + config=config, env_info=env_info) if monitor: env = Monitor(env) return env diff --git a/freqtrade/freqai/prediction_models/ReinforcementLearner_multiproc.py b/freqtrade/freqai/prediction_models/ReinforcementLearner_multiproc.py index c9b824978..58735e78f 100644 --- a/freqtrade/freqai/prediction_models/ReinforcementLearner_multiproc.py +++ b/freqtrade/freqai/prediction_models/ReinforcementLearner_multiproc.py @@ -5,6 +5,7 @@ from pandas import DataFrame from stable_baselines3.common.callbacks import EvalCallback from stable_baselines3.common.vec_env import SubprocVecEnv +from freqtrade.enums import RunMode from freqtrade.freqai.data_kitchen import FreqaiDataKitchen from freqtrade.freqai.prediction_models.ReinforcementLearner import ReinforcementLearner from freqtrade.freqai.RL.BaseReinforcementLearningModel import make_env @@ -34,17 +35,23 @@ class ReinforcementLearner_multiproc(ReinforcementLearner): train_df = data_dictionary["train_features"] test_df = data_dictionary["test_features"] + env_info = {"live": False} + if self.data_provider: + env_info["live"] = self.data_provider.runmode in (RunMode.DRY_RUN, RunMode.LIVE) + env_info["fee"] = self.data_provider._exchange \ + .get_fee(symbol=self.data_provider.current_whitelist()[0]) # type: ignore + env_id = "train_env" self.train_env = SubprocVecEnv([make_env(self.MyRLEnv, env_id, i, 1, train_df, prices_train, self.reward_params, self.CONV_WIDTH, monitor=True, - config=self.config, dp=self.data_provider) for i + config=self.config, env_info=env_info) for i in range(self.max_threads)]) eval_env_id = 'eval_env' self.eval_env = SubprocVecEnv([make_env(self.MyRLEnv, eval_env_id, i, 1, test_df, prices_test, self.reward_params, self.CONV_WIDTH, monitor=True, - config=self.config, dp=self.data_provider) for i + config=self.config, env_info=env_info) for i in range(self.max_threads)]) self.eval_callback = EvalCallback(self.eval_env, deterministic=True, render=False, eval_freq=len(train_df), From 3af2251ce86aee7a72fe659c6964338c412fadf7 Mon Sep 17 00:00:00 2001 From: Emre Date: Wed, 14 Dec 2022 22:03:23 +0300 Subject: [PATCH 082/795] Fix add_state_info backtesting bug --- freqtrade/freqai/RL/BaseEnvironment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqai/RL/BaseEnvironment.py b/freqtrade/freqai/RL/BaseEnvironment.py index 887910006..49361cbde 100644 --- a/freqtrade/freqai/RL/BaseEnvironment.py +++ b/freqtrade/freqai/RL/BaseEnvironment.py @@ -62,8 +62,6 @@ class BaseEnvironment(gym.Env): self.rl_config = config['freqai']['rl_config'] self.add_state_info = self.rl_config.get('add_state_info', False) self.id = id - self.seed(seed) - self.reset_env(df, prices, window_size, reward_kwargs, starting_point) self.max_drawdown = 1 - self.rl_config.get('max_training_drawdown_pct', 0.8) self.compound_trades = config['stake_amount'] == 'unlimited' if self.config.get('fee', None) is not None: @@ -78,6 +76,8 @@ class BaseEnvironment(gym.Env): if not self.live and self.add_state_info: self.add_state_info = False logger.warning("add_state_info is not available in backtesting. Deactivating.") + self.seed(seed) + self.reset_env(df, prices, window_size, reward_kwargs, starting_point) def reset_env(self, df: DataFrame, prices: DataFrame, window_size: int, reward_kwargs: dict, starting_point=True): From ca2a878b86b32d5c81abd4276c7de7c907f25a69 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 14 Dec 2022 19:58:45 +0100 Subject: [PATCH 083/795] Update test naming --- freqtrade/data/dataprovider.py | 4 ++-- tests/data/test_dataprovider.py | 29 +++++++++++++++-------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index b46f4e881..df4a4c898 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -186,7 +186,7 @@ class DataProvider: if len(dataframe) >= FULL_DATAFRAME_THRESHOLD: # This is likely a full dataframe # Add the dataframe to the dataprovider - self._add_external_df( + self._replace_external_df( pair, dataframe, last_analyzed=last_analyzed, @@ -228,7 +228,7 @@ class DataProvider: appended_df = append_candles_to_dataframe(existing_df1, dataframe) # Everything is good, we appended - self._add_external_df( + self._replace_external_df( pair, appended_df, last_analyzed=last_analyzed, diff --git a/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py index cce483c07..7d61a22be 100644 --- a/tests/data/test_dataprovider.py +++ b/tests/data/test_dataprovider.py @@ -161,9 +161,9 @@ def test_producer_pairs(mocker, default_conf, ohlcv_history): assert dataprovider.get_producer_pairs("bad") == [] -def test_get_producer_df(mocker, default_conf, ohlcv_history): +def test_get_producer_df(mocker, default_conf): dataprovider = DataProvider(default_conf, None) - + ohlcv_history = generate_test_data('5m', 150) pair = 'BTC/USDT' timeframe = default_conf['timeframe'] candle_type = CandleType.SPOT @@ -414,27 +414,28 @@ def test_dp_send_msg(default_conf): assert msg not in dp._msg_queue -def test_dp__add_external_candle(default_conf_usdt): +def test_dp__add_external_df(default_conf_usdt): timeframe = '1h' default_conf_usdt["timeframe"] = timeframe dp = DataProvider(default_conf_usdt, None) df = generate_test_data(timeframe, 24, '2022-01-01 00:00:00+00:00') last_analyzed = datetime.now(timezone.utc) - res = dp._add_external_candle('ETH/USDT', df, last_analyzed, timeframe, CandleType.SPOT) + res = dp._add_external_df('ETH/USDT', df, last_analyzed, timeframe, CandleType.SPOT) assert res[0] is False # Why 1000 ?? assert res[1] == 1000 - dp._add_external_df('ETH/USDT', df, last_analyzed, timeframe, CandleType.SPOT) + # Hard add dataframe + dp._replace_external_df('ETH/USDT', df, last_analyzed, timeframe, CandleType.SPOT) # BTC is not stored yet - res = dp._add_external_candle('BTC/USDT', df, last_analyzed, timeframe, CandleType.SPOT) + res = dp._add_external_df('BTC/USDT', df, last_analyzed, timeframe, CandleType.SPOT) assert res[0] is False - df, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) - assert len(df) == 24 + df_res, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) + assert len(df_res) == 24 # Add the same dataframe again - dataframe size shall not change. - res = dp._add_external_candle('ETH/USDT', df, last_analyzed, timeframe, CandleType.SPOT) + res = dp._add_external_df('ETH/USDT', df, last_analyzed, timeframe, CandleType.SPOT) assert res[0] is True assert res[1] == 0 df, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) @@ -443,7 +444,7 @@ def test_dp__add_external_candle(default_conf_usdt): # Add a new day. df2 = generate_test_data(timeframe, 24, '2022-01-02 00:00:00+00:00') - res = dp._add_external_candle('ETH/USDT', df2, last_analyzed, timeframe, CandleType.SPOT) + res = dp._add_external_df('ETH/USDT', df2, last_analyzed, timeframe, CandleType.SPOT) assert res[0] is True assert res[1] == 0 df, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) @@ -452,7 +453,7 @@ def test_dp__add_external_candle(default_conf_usdt): # Add a dataframe with a 12 hour offset - so 12 candles are overlapping, and 12 valid. df3 = generate_test_data(timeframe, 24, '2022-01-02 12:00:00+00:00') - res = dp._add_external_candle('ETH/USDT', df3, last_analyzed, timeframe, CandleType.SPOT) + res = dp._add_external_df('ETH/USDT', df3, last_analyzed, timeframe, CandleType.SPOT) assert res[0] is True assert res[1] == 0 df, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) @@ -463,7 +464,7 @@ def test_dp__add_external_candle(default_conf_usdt): # Generate 1 new candle df4 = generate_test_data(timeframe, 1, '2022-01-03 12:00:00+00:00') - res = dp._add_external_candle('ETH/USDT', df4, last_analyzed, timeframe, CandleType.SPOT) + res = dp._add_external_df('ETH/USDT', df4, last_analyzed, timeframe, CandleType.SPOT) # assert res[0] is True # assert res[1] == 0 df, _ = dp.get_producer_df('ETH/USDT', timeframe, CandleType.SPOT) @@ -474,7 +475,7 @@ def test_dp__add_external_candle(default_conf_usdt): # Gap in the data ... df4 = generate_test_data(timeframe, 1, '2022-01-05 00:00:00+00:00') - res = dp._add_external_candle('ETH/USDT', df4, last_analyzed, timeframe, CandleType.SPOT) + res = dp._add_external_df('ETH/USDT', df4, last_analyzed, timeframe, CandleType.SPOT) assert res[0] is False # 36 hours - from 2022-01-03 12:00:00+00:00 to 2022-01-05 00:00:00+00:00 assert res[1] == 36 @@ -484,7 +485,7 @@ def test_dp__add_external_candle(default_conf_usdt): # Empty dataframe df4 = generate_test_data(timeframe, 0, '2022-01-05 00:00:00+00:00') - res = dp._add_external_candle('ETH/USDT', df4, last_analyzed, timeframe, CandleType.SPOT) + res = dp._add_external_df('ETH/USDT', df4, last_analyzed, timeframe, CandleType.SPOT) assert res[0] is False # 36 hours - from 2022-01-03 12:00:00+00:00 to 2022-01-05 00:00:00+00:00 assert res[1] == 0 From 33dce5cf1024aa506a0e57d8226136b0db434d81 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 15 Dec 2022 06:51:15 +0100 Subject: [PATCH 084/795] Clarify partial exit calculation messaging --- docs/strategy-callbacks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index 230968fb0..19bd26a04 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -773,7 +773,7 @@ class DigDeeperStrategy(IStrategy): * Sell 100@10\$ -> Avg price: 8.5\$, realized profit 150\$, 17.65% * Buy 150@11\$ -> Avg price: 10\$, realized profit 150\$, 17.65% * Sell 100@12\$ -> Avg price: 10\$, total realized profit 350\$, 20% - * Sell 150@14\$ -> Avg price: 10\$, total realized profit 950\$, 40% + * Sell 150@14\$ -> Avg price: 10\$, total realized profit 950\$, 40% <- *This will be the last "Exit" message* The total profit for this trade was 950$ on a 3350$ investment (`100@8$ + 100@9$ + 150@11$`). As such - the final relative profit is 28.35% (`950 / 3350`). From 7a0eadbdf5013c967d45c185da510c231e11dbe9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 15 Dec 2022 07:04:59 +0100 Subject: [PATCH 085/795] Don't recalc profit on closed trades --- freqtrade/rpc/rpc.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 334e18dc7..dae23d388 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -167,6 +167,7 @@ class RPC: results = [] for trade in trades: order: Optional[Order] = None + current_profit_fiat: Optional[float] = None if trade.open_order_id: order = trade.select_order_by_order_id(trade.open_order_id) # calculate profit and send message to user @@ -176,23 +177,26 @@ class RPC: trade.pair, side='exit', is_short=trade.is_short, refresh=False) except (ExchangeError, PricingError): current_rate = NAN + if len(trade.select_filled_orders(trade.entry_side)) > 0: + current_profit = trade.calc_profit_ratio( + current_rate) if not isnan(current_rate) else NAN + current_profit_abs = trade.calc_profit( + current_rate) if not isnan(current_rate) else NAN + else: + current_profit = current_profit_abs = current_profit_fiat = 0.0 else: + # Closed trade ... current_rate = trade.close_rate - if len(trade.select_filled_orders(trade.entry_side)) > 0: - current_profit = trade.calc_profit_ratio( - current_rate) if not isnan(current_rate) else NAN - current_profit_abs = trade.calc_profit( - current_rate) if not isnan(current_rate) else NAN - current_profit_fiat: Optional[float] = None - # Calculate fiat profit - if self._fiat_converter: - current_profit_fiat = self._fiat_converter.convert_amount( - current_profit_abs, - self._freqtrade.config['stake_currency'], - self._freqtrade.config['fiat_display_currency'] - ) - else: - current_profit = current_profit_abs = current_profit_fiat = 0.0 + current_profit = trade.close_profit + current_profit_abs = trade.close_profit_abs + + # Calculate fiat profit + if not isnan(current_profit_abs) and self._fiat_converter: + current_profit_fiat = self._fiat_converter.convert_amount( + current_profit_abs, + self._freqtrade.config['stake_currency'], + self._freqtrade.config['fiat_display_currency'] + ) # Calculate guaranteed profit (in case of trailing stop) stoploss_entry_dist = trade.calc_profit(trade.stop_loss) From 7b4abd5ef50f3c6f84c6604fc1f79ff4b92c2575 Mon Sep 17 00:00:00 2001 From: robcaulk Date: Thu, 15 Dec 2022 12:25:33 +0100 Subject: [PATCH 086/795] use a dictionary to make code more readable --- freqtrade/freqai/RL/BaseEnvironment.py | 8 ++-- .../RL/BaseReinforcementLearningModel.py | 40 ++++++++++--------- .../ReinforcementLearner_multiproc.py | 18 ++++----- 3 files changed, 32 insertions(+), 34 deletions(-) diff --git a/freqtrade/freqai/RL/BaseEnvironment.py b/freqtrade/freqai/RL/BaseEnvironment.py index 49361cbde..39e8609f5 100644 --- a/freqtrade/freqai/RL/BaseEnvironment.py +++ b/freqtrade/freqai/RL/BaseEnvironment.py @@ -44,8 +44,8 @@ class BaseEnvironment(gym.Env): def __init__(self, df: DataFrame = DataFrame(), prices: DataFrame = DataFrame(), reward_kwargs: dict = {}, window_size=10, starting_point=True, - id: str = 'baseenv-1', seed: int = 1, config: dict = {}, - env_info: dict = {}): + id: str = 'baseenv-1', seed: int = 1, config: dict = {}, live: bool = False, + fee: float = 0.0015): """ Initializes the training/eval environment. :param df: dataframe of features @@ -67,12 +67,12 @@ class BaseEnvironment(gym.Env): if self.config.get('fee', None) is not None: self.fee = self.config['fee'] else: - self.fee = env_info.get('fee', 0.0015) + self.fee = fee # set here to default 5Ac, but all children envs can override this self.actions: Type[Enum] = BaseActions self.tensorboard_metrics: dict = {} - self.live = env_info.get('live', False) + self.live = live if not self.live and self.add_state_info: self.add_state_info = False logger.warning("add_state_info is not available in backtesting. Deactivating.") diff --git a/freqtrade/freqai/RL/BaseReinforcementLearningModel.py b/freqtrade/freqai/RL/BaseReinforcementLearningModel.py index a41f02cba..62963f194 100644 --- a/freqtrade/freqai/RL/BaseReinforcementLearningModel.py +++ b/freqtrade/freqai/RL/BaseReinforcementLearningModel.py @@ -17,7 +17,6 @@ from stable_baselines3.common.monitor import Monitor from stable_baselines3.common.utils import set_random_seed from stable_baselines3.common.vec_env import SubprocVecEnv -from freqtrade.enums import RunMode from freqtrade.exceptions import OperationalException from freqtrade.freqai.data_kitchen import FreqaiDataKitchen from freqtrade.freqai.freqai_interface import IFreqaiModel @@ -144,24 +143,14 @@ class BaseReinforcementLearningModel(IFreqaiModel): train_df = data_dictionary["train_features"] test_df = data_dictionary["test_features"] - env_info = {"live": False} - if self.data_provider: - env_info["live"] = self.data_provider.runmode in (RunMode.DRY_RUN, RunMode.LIVE) - env_info["fee"] = self.data_provider._exchange \ - .get_fee(symbol=self.data_provider.current_whitelist()[0]) # type: ignore + env_info = self.pack_env_dict() self.train_env = self.MyRLEnv(df=train_df, prices=prices_train, - window_size=self.CONV_WIDTH, - reward_kwargs=self.reward_params, - config=self.config, - env_info=env_info) + **env_info) self.eval_env = Monitor(self.MyRLEnv(df=test_df, prices=prices_test, - window_size=self.CONV_WIDTH, - reward_kwargs=self.reward_params, - config=self.config, - env_info=env_info)) + **env_info)) self.eval_callback = EvalCallback(self.eval_env, deterministic=True, render=False, eval_freq=len(train_df), best_model_save_path=str(dk.data_path)) @@ -169,6 +158,20 @@ class BaseReinforcementLearningModel(IFreqaiModel): actions = self.train_env.get_actions() self.tensorboard_callback = TensorboardCallback(verbose=1, actions=actions) + def pack_env_dict(self) -> Dict[str, Any]: + """ + Create dictionary of environment arguments + """ + env_info = {"window_size": self.CONV_WIDTH, + "reward_kwargs": self.reward_params, + "config": self.config, + "live": self.live} + if self.data_provider: + env_info["fee"] = self.data_provider._exchange \ + .get_fee(symbol=self.data_provider.current_whitelist()[0]) # type: ignore + + return env_info + @abstractmethod def fit(self, data_dictionary: Dict[str, Any], dk: FreqaiDataKitchen, **kwargs): """ @@ -390,8 +393,8 @@ class BaseReinforcementLearningModel(IFreqaiModel): def make_env(MyRLEnv: Type[gym.Env], env_id: str, rank: int, seed: int, train_df: DataFrame, price: DataFrame, - reward_params: Dict[str, int], window_size: int, monitor: bool = False, - config: Dict[str, Any] = {}, env_info: Dict[str, Any] = {}) -> Callable: + monitor: bool = False, + env_info: Dict[str, Any] = {}) -> Callable: """ Utility function for multiprocessed env. @@ -404,9 +407,8 @@ def make_env(MyRLEnv: Type[gym.Env], env_id: str, rank: int, def _init() -> gym.Env: - env = MyRLEnv(df=train_df, prices=price, window_size=window_size, - reward_kwargs=reward_params, id=env_id, seed=seed + rank, - config=config, env_info=env_info) + env = MyRLEnv(df=train_df, prices=price, id=env_id, seed=seed + rank, + **env_info) if monitor: env = Monitor(env) return env diff --git a/freqtrade/freqai/prediction_models/ReinforcementLearner_multiproc.py b/freqtrade/freqai/prediction_models/ReinforcementLearner_multiproc.py index 58735e78f..a9be87b0b 100644 --- a/freqtrade/freqai/prediction_models/ReinforcementLearner_multiproc.py +++ b/freqtrade/freqai/prediction_models/ReinforcementLearner_multiproc.py @@ -5,7 +5,6 @@ from pandas import DataFrame from stable_baselines3.common.callbacks import EvalCallback from stable_baselines3.common.vec_env import SubprocVecEnv -from freqtrade.enums import RunMode from freqtrade.freqai.data_kitchen import FreqaiDataKitchen from freqtrade.freqai.prediction_models.ReinforcementLearner import ReinforcementLearner from freqtrade.freqai.RL.BaseReinforcementLearningModel import make_env @@ -35,23 +34,20 @@ class ReinforcementLearner_multiproc(ReinforcementLearner): train_df = data_dictionary["train_features"] test_df = data_dictionary["test_features"] - env_info = {"live": False} - if self.data_provider: - env_info["live"] = self.data_provider.runmode in (RunMode.DRY_RUN, RunMode.LIVE) - env_info["fee"] = self.data_provider._exchange \ - .get_fee(symbol=self.data_provider.current_whitelist()[0]) # type: ignore + env_info = self.pack_env_dict() env_id = "train_env" - self.train_env = SubprocVecEnv([make_env(self.MyRLEnv, env_id, i, 1, train_df, prices_train, - self.reward_params, self.CONV_WIDTH, monitor=True, - config=self.config, env_info=env_info) for i + self.train_env = SubprocVecEnv([make_env(self.MyRLEnv, env_id, i, 1, + train_df, prices_train, + monitor=True, + env_info=env_info) for i in range(self.max_threads)]) eval_env_id = 'eval_env' self.eval_env = SubprocVecEnv([make_env(self.MyRLEnv, eval_env_id, i, 1, test_df, prices_test, - self.reward_params, self.CONV_WIDTH, monitor=True, - config=self.config, env_info=env_info) for i + monitor=True, + env_info=env_info) for i in range(self.max_threads)]) self.eval_callback = EvalCallback(self.eval_env, deterministic=True, render=False, eval_freq=len(train_df), From 581a5296cc7a76ea927eec9157559e426f170daa Mon Sep 17 00:00:00 2001 From: robcaulk Date: Thu, 15 Dec 2022 16:50:08 +0100 Subject: [PATCH 087/795] fix docstrings to reflect new env_info changes --- freqtrade/freqai/RL/BaseEnvironment.py | 3 ++- freqtrade/freqai/RL/BaseReinforcementLearningModel.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqai/RL/BaseEnvironment.py b/freqtrade/freqai/RL/BaseEnvironment.py index 39e8609f5..17d82a3ba 100644 --- a/freqtrade/freqai/RL/BaseEnvironment.py +++ b/freqtrade/freqai/RL/BaseEnvironment.py @@ -56,7 +56,8 @@ class BaseEnvironment(gym.Env): :param id: string id of the environment (used in backend for multiprocessed env) :param seed: Sets the seed of the environment higher in the gym.Env object :param config: Typical user configuration file - :param env_info: Environment info dictionary, used to pass live status, fee, etc. + :param live: Whether or not this environment is active in dry/live/backtesting + :param fee: The fee to use for environmental interactions. """ self.config = config self.rl_config = config['freqai']['rl_config'] diff --git a/freqtrade/freqai/RL/BaseReinforcementLearningModel.py b/freqtrade/freqai/RL/BaseReinforcementLearningModel.py index 62963f194..d7e3a3cad 100644 --- a/freqtrade/freqai/RL/BaseReinforcementLearningModel.py +++ b/freqtrade/freqai/RL/BaseReinforcementLearningModel.py @@ -402,6 +402,7 @@ def make_env(MyRLEnv: Type[gym.Env], env_id: str, rank: int, :param num_env: (int) the number of environment you wish to have in subprocesses :param seed: (int) the inital seed for RNG :param rank: (int) index of the subprocess + :param env_info: (dict) all required arguments to instantiate the environment. :return: (Callable) """ From 1d5c66da3bcb212732df322efb74d54eca069ca0 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Thu, 15 Dec 2022 17:38:21 +0100 Subject: [PATCH 088/795] + Unit Tests --- tests/plugins/test_remotepairlist.py | 72 ++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/tests/plugins/test_remotepairlist.py b/tests/plugins/test_remotepairlist.py index 743534bc3..bc4adb616 100644 --- a/tests/plugins/test_remotepairlist.py +++ b/tests/plugins/test_remotepairlist.py @@ -1,11 +1,13 @@ +import json from unittest.mock import MagicMock import pytest +import requests from freqtrade.exceptions import OperationalException from freqtrade.plugins.pairlist.RemotePairList import RemotePairList from freqtrade.plugins.pairlistmanager import PairListManager -from tests.conftest import get_patched_exchange, get_patched_freqtradebot +from tests.conftest import get_patched_exchange, get_patched_freqtradebot, log_has @pytest.fixture(scope="function") @@ -22,10 +24,44 @@ def rpl_config(default_conf): return default_conf +def test_gen_pairlist_with_local_file(mocker, rpl_config): + + mock_file = MagicMock() + mock_file.read.return_value = '{"pairs": ["TKN/USDT","ETH/USDT","NANO/USDT"]}' + mocker.patch('freqtrade.plugins.pairlist.RemotePairList.open', return_value=mock_file) + + mock_file_path = mocker.patch('freqtrade.plugins.pairlist.RemotePairList.Path') + mock_file_path.exists.return_value = True + + jsonparse = json.loads(mock_file.read.return_value) + mocker.patch('freqtrade.plugins.pairlist.RemotePairList.json.load', return_value=jsonparse) + + rpl_config['pairlists'] = [ + { + "method": "RemotePairList", + 'number_assets': 2, + 'refresh_period': 1800, + 'keep_pairlist_on_failure': True, + 'pairlist_url': 'file:///pairlist.json', + 'bearer_token': '', + 'read_timeout': 60 + } + ] + + exchange = get_patched_exchange(mocker, rpl_config) + pairlistmanager = PairListManager(exchange, rpl_config) + + remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config, + rpl_config['pairlists'][0], 0) + + result = remote_pairlist.gen_pairlist([]) + + assert result == ['TKN/USDT', 'ETH/USDT'] + + def test_fetch_pairlist_mock_response_html(mocker, rpl_config): mock_response = MagicMock() mock_response.headers = {'content-type': 'text/html'} - mocker.patch('requests.get', return_value=mock_response) rpl_config['pairlists'] = [ { @@ -49,6 +85,34 @@ def test_fetch_pairlist_mock_response_html(mocker, rpl_config): remote_pairlist.fetch_pairlist() +def test_fetch_pairlist_timeout_keep_last_pairlist(mocker, rpl_config, caplog): + rpl_config['pairlists'] = [ + { + "method": "RemotePairList", + "pairlist_url": "http://example.com/pairlist", + "number_assets": 10, + "read_timeout": 10, + "keep_pairlist_on_failure": True, + } + ] + + exchange = get_patched_exchange(mocker, rpl_config) + pairlistmanager = PairListManager(exchange, rpl_config) + + mocker.patch("freqtrade.plugins.pairlist.RemotePairList.requests.get", + side_effect=requests.exceptions.RequestException) + + remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config, + rpl_config['pairlists'][0], 0) + + remote_pairlist._last_pairlist = ["BTC/USDT", "ETH/USDT", "LTC/USDT"] + + pairs, time_elapsed, info = remote_pairlist.fetch_pairlist() + assert log_has(f"Was not able to fetch pairlist from: {remote_pairlist._pairlist_url} ", caplog) + assert log_has("Keeping last fetched pairlist", caplog) + assert pairs == ["BTC/USDT", "ETH/USDT", "LTC/USDT"] + + def test_remote_pairlist_init_no_pairlist_url(mocker, rpl_config): rpl_config['pairlists'] = [ @@ -98,7 +162,7 @@ def test_fetch_pairlist_mock_response_valid(mocker, rpl_config): mock_response = MagicMock() mock_response.json.return_value = { - "pairs": ["ETH/BTC", "XRP/BTC", "LTC/BTC", "EOS/BTC"], + "pairs": ["ETH/USDT", "XRP/USDT", "LTC/USDT", "EOS/USDT"], "info": "Mock pairlist response", "refresh_period": 60 } @@ -117,7 +181,7 @@ def test_fetch_pairlist_mock_response_valid(mocker, rpl_config): rpl_config['pairlists'][0], 0) pairs, time_elapsed, info = remote_pairlist.fetch_pairlist() - assert pairs == ["ETH/BTC", "XRP/BTC", "LTC/BTC", "EOS/BTC"] + assert pairs == ["ETH/USDT", "XRP/USDT", "LTC/USDT", "EOS/USDT"] assert time_elapsed == 0.4 assert info == "Mock pairlist response" assert remote_pairlist._refresh_period == 60 From cd1b8b9cee37a0ac412a57af51f02348b40d9565 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Thu, 15 Dec 2022 18:14:37 +0100 Subject: [PATCH 089/795] single space removed for the unit test to pass.. --- tests/plugins/test_remotepairlist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/plugins/test_remotepairlist.py b/tests/plugins/test_remotepairlist.py index bc4adb616..fc91d3f06 100644 --- a/tests/plugins/test_remotepairlist.py +++ b/tests/plugins/test_remotepairlist.py @@ -108,7 +108,7 @@ def test_fetch_pairlist_timeout_keep_last_pairlist(mocker, rpl_config, caplog): remote_pairlist._last_pairlist = ["BTC/USDT", "ETH/USDT", "LTC/USDT"] pairs, time_elapsed, info = remote_pairlist.fetch_pairlist() - assert log_has(f"Was not able to fetch pairlist from: {remote_pairlist._pairlist_url} ", caplog) + assert log_has(f"Was not able to fetch pairlist from: {remote_pairlist._pairlist_url}", caplog) assert log_has("Keeping last fetched pairlist", caplog) assert pairs == ["BTC/USDT", "ETH/USDT", "LTC/USDT"] From 6fa3db3a1dc79dfd36b121c5b3f41cc8811ad487 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 15 Dec 2022 19:36:21 +0100 Subject: [PATCH 090/795] Fix failing tests --- tests/plugins/test_pairlist.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index ecc1da3e3..739c3a7ac 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -22,6 +22,11 @@ from tests.conftest import (create_mock_trades_usdt, get_patched_exchange, get_p log_has, log_has_re, num_log_has) +# Exclude RemotePairList from tests. +# It has a mandatory parameter, and requires special handling, which happens in test_remotepairlist. +TESTABLE_PAIRLISTS = [p for p in AVAILABLE_PAIRLISTS if p not in ['RemotePairList']] + + @pytest.fixture(scope="function") def whitelist_conf(default_conf): default_conf['stake_currency'] = 'BTC' @@ -824,7 +829,7 @@ def test_pair_whitelist_not_supported_Spread(mocker, default_conf, tickers) -> N get_patched_freqtradebot(mocker, default_conf) -@pytest.mark.parametrize("pairlist", AVAILABLE_PAIRLISTS) +@pytest.mark.parametrize("pairlist", TESTABLE_PAIRLISTS) def test_pairlist_class(mocker, whitelist_conf, markets, pairlist): whitelist_conf['pairlists'][0]['method'] = pairlist mocker.patch.multiple('freqtrade.exchange.Exchange', @@ -839,7 +844,7 @@ def test_pairlist_class(mocker, whitelist_conf, markets, pairlist): assert isinstance(freqtrade.pairlists.blacklist, list) -@pytest.mark.parametrize("pairlist", AVAILABLE_PAIRLISTS) +@pytest.mark.parametrize("pairlist", TESTABLE_PAIRLISTS) @pytest.mark.parametrize("whitelist,log_message", [ (['ETH/BTC', 'TKN/BTC'], ""), # TRX/ETH not in markets @@ -872,7 +877,7 @@ def test__whitelist_for_active_markets(mocker, whitelist_conf, markets, pairlist assert log_message in caplog.text -@pytest.mark.parametrize("pairlist", AVAILABLE_PAIRLISTS) +@pytest.mark.parametrize("pairlist", TESTABLE_PAIRLISTS) def test__whitelist_for_active_markets_empty(mocker, whitelist_conf, pairlist, tickers): whitelist_conf['pairlists'][0]['method'] = pairlist From 32d57f624e06790e4a4ddc4bba493a72ce64ab3c Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Thu, 15 Dec 2022 15:00:27 -0500 Subject: [PATCH 091/795] delisted bibox following ccxt PR https://github.com/ccxt/ccxt/pull/16067 --- freqtrade/exchange/__init__.py | 1 - freqtrade/exchange/bibox.py | 28 ---------------------------- tests/exchange/test_exchange.py | 3 --- 3 files changed, 32 deletions(-) delete mode 100644 freqtrade/exchange/bibox.py diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 9aed5c507..973ed499b 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -3,7 +3,6 @@ from freqtrade.exchange.common import remove_credentials, MAP_EXCHANGE_CHILDCLASS from freqtrade.exchange.exchange import Exchange # isort: on -from freqtrade.exchange.bibox import Bibox from freqtrade.exchange.binance import Binance from freqtrade.exchange.bitpanda import Bitpanda from freqtrade.exchange.bittrex import Bittrex diff --git a/freqtrade/exchange/bibox.py b/freqtrade/exchange/bibox.py deleted file mode 100644 index da1effbfe..000000000 --- a/freqtrade/exchange/bibox.py +++ /dev/null @@ -1,28 +0,0 @@ -""" Bibox exchange subclass """ -import logging -from typing import Dict - -from freqtrade.exchange import Exchange - - -logger = logging.getLogger(__name__) - - -class Bibox(Exchange): - """ - Bibox exchange class. Contains adjustments needed for Freqtrade to work - with this exchange. - - Please note that this exchange is not included in the list of exchanges - officially supported by the Freqtrade development team. So some features - may still not work as expected. - """ - - # fetchCurrencies API point requires authentication for Bibox, - # so switch it off for Freqtrade load_markets() - @property - def _ccxt_config(self) -> Dict: - # Parameters to add directly to ccxt sync/async initialization. - config = {"has": {"fetchCurrencies": False}} - config.update(super()._ccxt_config) - return config diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index e61ad8532..280e20ff0 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -4014,9 +4014,6 @@ def test_validate_trading_mode_and_margin_mode( ("binance", "spot", {}), ("binance", "margin", {"options": {"defaultType": "margin"}}), ("binance", "futures", {"options": {"defaultType": "future"}}), - ("bibox", "spot", {"has": {"fetchCurrencies": False}}), - ("bibox", "margin", {"has": {"fetchCurrencies": False}, "options": {"defaultType": "margin"}}), - ("bibox", "futures", {"has": {"fetchCurrencies": False}, "options": {"defaultType": "swap"}}), ("bybit", "spot", {"options": {"defaultType": "spot"}}), ("bybit", "futures", {"options": {"defaultType": "linear"}}), ("gateio", "futures", {"options": {"defaultType": "swap"}}), From 935275010f37738efc4667bff608762d89db0559 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 16 Dec 2022 06:46:44 +0100 Subject: [PATCH 092/795] Remove some unused fixtures --- tests/data/test_dataprovider.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py index 7d61a22be..e0c79d52a 100644 --- a/tests/data/test_dataprovider.py +++ b/tests/data/test_dataprovider.py @@ -144,7 +144,7 @@ def test_available_pairs(mocker, default_conf, ohlcv_history): assert dp.available_pairs == [("XRP/BTC", timeframe), ("UNITTEST/BTC", timeframe), ] -def test_producer_pairs(mocker, default_conf, ohlcv_history): +def test_producer_pairs(default_conf): dataprovider = DataProvider(default_conf, None) producer = "default" @@ -161,7 +161,7 @@ def test_producer_pairs(mocker, default_conf, ohlcv_history): assert dataprovider.get_producer_pairs("bad") == [] -def test_get_producer_df(mocker, default_conf): +def test_get_producer_df(default_conf): dataprovider = DataProvider(default_conf, None) ohlcv_history = generate_test_data('5m', 150) pair = 'BTC/USDT' @@ -221,7 +221,7 @@ def test_emit_df(mocker, default_conf, ohlcv_history): assert send_mock.call_count == 0 -def test_refresh(mocker, default_conf, ohlcv_history): +def test_refresh(mocker, default_conf): refresh_mock = MagicMock() mocker.patch("freqtrade.exchange.Exchange.refresh_latest_ohlcv", refresh_mock) From c9bc91c75b8414d70bbe6291497d068f5b9d355e Mon Sep 17 00:00:00 2001 From: robcaulk Date: Fri, 16 Dec 2022 11:20:37 +0100 Subject: [PATCH 093/795] add shuffle_after_split option --- freqtrade/freqai/data_kitchen.py | 14 ++++++++++++++ tests/freqai/test_freqai_interface.py | 20 +++++++++++--------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/freqtrade/freqai/data_kitchen.py b/freqtrade/freqai/data_kitchen.py index 9c8158c8a..de6b74b21 100644 --- a/freqtrade/freqai/data_kitchen.py +++ b/freqtrade/freqai/data_kitchen.py @@ -1,5 +1,6 @@ import copy import logging +import random import shutil from datetime import datetime, timezone from math import cos, sin @@ -168,6 +169,19 @@ class FreqaiDataKitchen: train_labels = labels train_weights = weights + if feat_dict.get("shuffle_after_split", False): + rint1 = random.randint(0, 100) + rint2 = random.randint(0, 100) + train_features = train_features.sample( + frac=1, random_state=rint1).reset_index(drop=True) + train_labels = train_labels.sample(frac=1, random_state=rint1).reset_index(drop=True) + train_weights = pd.DataFrame(train_weights).sample( + frac=1, random_state=rint1).reset_index(drop=True).to_numpy()[:, 0] + test_features = test_features.sample(frac=1, random_state=rint2).reset_index(drop=True) + test_labels = test_labels.sample(frac=1, random_state=rint2).reset_index(drop=True) + test_weights = pd.DataFrame(test_weights).sample( + frac=1, random_state=rint2).reset_index(drop=True).to_numpy()[:, 0] + # Simplest way to reverse the order of training and test data: if self.freqai_config['feature_parameters'].get('reverse_train_test_order', False): return self.build_data_dictionary( diff --git a/tests/freqai/test_freqai_interface.py b/tests/freqai/test_freqai_interface.py index f19acb018..fde167823 100644 --- a/tests/freqai/test_freqai_interface.py +++ b/tests/freqai/test_freqai_interface.py @@ -27,16 +27,17 @@ def is_mac() -> bool: return "Darwin" in machine -@pytest.mark.parametrize('model, pca, dbscan, float32', [ - ('LightGBMRegressor', True, False, True), - ('XGBoostRegressor', False, True, False), - ('XGBoostRFRegressor', False, False, False), - ('CatboostRegressor', False, False, False), - ('ReinforcementLearner', False, True, False), - ('ReinforcementLearner_multiproc', False, False, False), - ('ReinforcementLearner_test_4ac', False, False, False) +@pytest.mark.parametrize('model, pca, dbscan, float32, shuffle', [ + ('LightGBMRegressor', True, False, True, False), + ('XGBoostRegressor', False, True, False, False), + ('XGBoostRFRegressor', False, False, False, False), + ('CatboostRegressor', False, False, False, True), + ('ReinforcementLearner', False, True, False, False), + ('ReinforcementLearner_multiproc', False, False, False, False), + ('ReinforcementLearner_test_4ac', False, False, False, False) ]) -def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca, dbscan, float32): +def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca, + dbscan, float32, shuffle): if is_arm() and model == 'CatboostRegressor': pytest.skip("CatBoost is not supported on ARM") @@ -50,6 +51,7 @@ def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca, freqai_conf['freqai']['feature_parameters'].update({"principal_component_analysis": pca}) freqai_conf['freqai']['feature_parameters'].update({"use_DBSCAN_to_remove_outliers": dbscan}) freqai_conf.update({"reduce_df_footprint": float32}) + freqai_conf['freqai']['feature_parameters'].update({"shuffle_after_split": shuffle}) if 'ReinforcementLearner' in model: model_save_ext = 'zip' From 36948e2a7480326a4bd786bc6ff72c2eb4c52fd5 Mon Sep 17 00:00:00 2001 From: initrv Date: Fri, 16 Dec 2022 14:14:05 +0300 Subject: [PATCH 094/795] fix base4 env done condition --- freqtrade/freqai/RL/Base4ActionRLEnv.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqai/RL/Base4ActionRLEnv.py b/freqtrade/freqai/RL/Base4ActionRLEnv.py index a3ebfdbfa..8f45028b1 100644 --- a/freqtrade/freqai/RL/Base4ActionRLEnv.py +++ b/freqtrade/freqai/RL/Base4ActionRLEnv.py @@ -88,7 +88,8 @@ class Base4ActionRLEnv(BaseEnvironment): {'price': self.current_price(), 'index': self._current_tick, 'type': trade_type}) - if self._total_profit < 1 - self.rl_config.get('max_training_drawdown_pct', 0.8): + if (self._total_profit < self.max_drawdown or + self._total_unrealized_profit < self.max_drawdown): self._done = True self._position_history.append(self._position) From e4284f4e7b1d7f96cb61d113b9496166b732f28c Mon Sep 17 00:00:00 2001 From: robcaulk Date: Fri, 16 Dec 2022 15:20:46 +0100 Subject: [PATCH 095/795] add citation to freqai doc. Update credits --- docs/freqai.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/freqai.md b/docs/freqai.md index efa279704..80096996d 100644 --- a/docs/freqai.md +++ b/docs/freqai.md @@ -72,11 +72,25 @@ pip install -r requirements-freqai.txt If you are using docker, a dedicated tag with FreqAI dependencies is available as `:freqai`. As such - you can replace the image line in your docker-compose file with `image: freqtradeorg/freqtrade:develop_freqai`. This image contains the regular FreqAI dependencies. Similar to native installs, Catboost will not be available on ARM based devices. - ### FreqAI position in open-source machine learning landscape Forecasting chaotic time-series based systems, such as equity/cryptocurrency markets, requires a broad set of tools geared toward testing a wide range of hypotheses. Fortunately, a recent maturation of robust machine learning libraries (e.g. `scikit-learn`) has opened up a wide range of research possibilities. Scientists from a diverse range of fields can now easily prototype their studies on an abundance of established machine learning algorithms. Similarly, these user-friendly libraries enable "citzen scientists" to use their basic Python skills for data-exploration. However, leveraging these machine learning libraries on historical and live chaotic data sources can be logistically difficult and expensive. Additionally, robust data-collection, storage, and handling presents a disparate challenge. [`FreqAI`](#freqai) aims to provide a generalized and extensible open-sourced framework geared toward live deployments of adaptive modeling for market forecasting. The `FreqAI` framework is effectively a sandbox for the rich world of open-source machine learning libraries. Inside the `FreqAI` sandbox, users find they can combine a wide variety of third-party libraries to test creative hypotheses on a free live 24/7 chaotic data source - cryptocurrency exchange data. +### Citing FreqAI + +FreqAI is published in the Journal of Open Source Software [link](https://joss.theoj.org/papers/10.21105/joss.04864). If you find FreqAI useful in your research, please use the following citation: + +```bibtex +@article{Caulk2022, + doi = {10.21105/joss.04864}, + url = {https://doi.org/10.21105/joss.04864}, + year = {2022}, publisher = {The Open Journal}, + volume = {7}, number = {80}, pages = {4864}, + author = {Robert A. Caulk and Elin Törnquist and Matthias Voppichler and Andrew R. Lawless and Ryan McMullan and Wagner Costa Santos and Timothy C. Pogue and Johan van der Vlugt and Stefan P. Gehring and Pascal Schmidt}, + title = {FreqAI: generalizing adaptive modeling for chaotic time-series market forecasts}, + journal = {Journal of Open Source Software} } +``` + ## Common pitfalls FreqAI cannot be combined with dynamic `VolumePairlists` (or any pairlist filter that adds and removes pairs dynamically). @@ -99,6 +113,8 @@ Code review and software architecture brainstorming: Software development: Wagner Costa @wagnercosta +Emre Suzen @aemr3 +Timothy Pogue @wizrds Beta testing and bug reporting: -Stefan Gehring @bloodhunter4rc, @longyu, Andrew Lawless @paranoidandy, Pascal Schmidt @smidelis, Ryan McMullan @smarmau, Juha Nykänen @suikula, Johan van der Vlugt @jooopiert, Richárd Józsa @richardjosza, Timothy Pogue @wizrds +Stefan Gehring @bloodhunter4rc, @longyu, Andrew Lawless @paranoidandy, Pascal Schmidt @smidelis, Ryan McMullan @smarmau, Juha Nykänen @suikula, Johan van der Vlugt @jooopiert, Richárd Józsa @richardjosza From dde363343c4932572a013c50df9effddd47282ad Mon Sep 17 00:00:00 2001 From: Emre Date: Fri, 16 Dec 2022 22:16:19 +0300 Subject: [PATCH 096/795] Add can_short param to base env --- freqtrade/freqai/RL/BaseEnvironment.py | 4 +++- freqtrade/freqai/RL/BaseReinforcementLearningModel.py | 3 ++- freqtrade/freqai/freqai_interface.py | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqai/RL/BaseEnvironment.py b/freqtrade/freqai/RL/BaseEnvironment.py index 17d82a3ba..ef1c02a3b 100644 --- a/freqtrade/freqai/RL/BaseEnvironment.py +++ b/freqtrade/freqai/RL/BaseEnvironment.py @@ -45,7 +45,7 @@ class BaseEnvironment(gym.Env): def __init__(self, df: DataFrame = DataFrame(), prices: DataFrame = DataFrame(), reward_kwargs: dict = {}, window_size=10, starting_point=True, id: str = 'baseenv-1', seed: int = 1, config: dict = {}, live: bool = False, - fee: float = 0.0015): + fee: float = 0.0015, can_short: bool = False): """ Initializes the training/eval environment. :param df: dataframe of features @@ -58,6 +58,7 @@ class BaseEnvironment(gym.Env): :param config: Typical user configuration file :param live: Whether or not this environment is active in dry/live/backtesting :param fee: The fee to use for environmental interactions. + :param can_short: Whether or not the environment can short """ self.config = config self.rl_config = config['freqai']['rl_config'] @@ -73,6 +74,7 @@ class BaseEnvironment(gym.Env): # set here to default 5Ac, but all children envs can override this self.actions: Type[Enum] = BaseActions self.tensorboard_metrics: dict = {} + self.can_short = can_short self.live = live if not self.live and self.add_state_info: self.add_state_info = False diff --git a/freqtrade/freqai/RL/BaseReinforcementLearningModel.py b/freqtrade/freqai/RL/BaseReinforcementLearningModel.py index d7e3a3cad..af0726c0b 100644 --- a/freqtrade/freqai/RL/BaseReinforcementLearningModel.py +++ b/freqtrade/freqai/RL/BaseReinforcementLearningModel.py @@ -165,7 +165,8 @@ class BaseReinforcementLearningModel(IFreqaiModel): env_info = {"window_size": self.CONV_WIDTH, "reward_kwargs": self.reward_params, "config": self.config, - "live": self.live} + "live": self.live, + "can_short": self.can_short} if self.data_provider: env_info["fee"] = self.data_provider._exchange \ .get_fee(symbol=self.data_provider.current_whitelist()[0]) # type: ignore diff --git a/freqtrade/freqai/freqai_interface.py b/freqtrade/freqai/freqai_interface.py index 34780f930..bbae7453f 100644 --- a/freqtrade/freqai/freqai_interface.py +++ b/freqtrade/freqai/freqai_interface.py @@ -133,6 +133,7 @@ class IFreqaiModel(ABC): self.live = strategy.dp.runmode in (RunMode.DRY_RUN, RunMode.LIVE) self.dd.set_pair_dict_info(metadata) self.data_provider = strategy.dp + self.can_short = strategy.can_short if self.live: self.inference_timer('start') From 7727f315070c471ea09bc17ad67da5d2cd06e067 Mon Sep 17 00:00:00 2001 From: Emre Date: Fri, 16 Dec 2022 22:18:49 +0300 Subject: [PATCH 097/795] Add 3 Action RL env --- freqtrade/freqai/RL/Base3ActionRLEnv.py | 125 ++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 freqtrade/freqai/RL/Base3ActionRLEnv.py diff --git a/freqtrade/freqai/RL/Base3ActionRLEnv.py b/freqtrade/freqai/RL/Base3ActionRLEnv.py new file mode 100644 index 000000000..3b5fffc58 --- /dev/null +++ b/freqtrade/freqai/RL/Base3ActionRLEnv.py @@ -0,0 +1,125 @@ +import logging +from enum import Enum + +from gym import spaces + +from freqtrade.freqai.RL.BaseEnvironment import BaseEnvironment, Positions + + +logger = logging.getLogger(__name__) + + +class Actions(Enum): + Neutral = 0 + Buy = 1 + Sell = 2 + + +class Base3ActionRLEnv(BaseEnvironment): + """ + Base class for a 3 action environment + """ + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.actions = Actions + + def set_action_space(self): + self.action_space = spaces.Discrete(len(Actions)) + + def step(self, action: int): + """ + Logic for a single step (incrementing one candle in time) + by the agent + :param: action: int = the action type that the agent plans + to take for the current step. + :returns: + observation = current state of environment + step_reward = the reward from `calculate_reward()` + _done = if the agent "died" or if the candles finished + info = dict passed back to openai gym lib + """ + self._done = False + self._current_tick += 1 + + if self._current_tick == self._end_tick: + self._done = True + + self._update_unrealized_total_profit() + step_reward = self.calculate_reward(action) + self.total_reward += step_reward + self.tensorboard_log(self.actions._member_names_[action]) + + trade_type = None + if self.is_tradesignal(action): + if action == Actions.Buy.value: + if self._position == Positions.Short: + self._update_total_profit() + self._position = Positions.Long + trade_type = "long" + self._last_trade_tick = self._current_tick + elif action == Actions.Sell.value and self.can_short: + if self._position == Positions.Long: + self._update_total_profit() + self._position = Positions.Short + trade_type = "short" + self._last_trade_tick = self._current_tick + elif action == Actions.Sell.value and not self.can_short: + self._update_total_profit() + self._position = Positions.Neutral + trade_type = "neutral" + self._last_trade_tick = None + else: + print("case not defined") + + if trade_type is not None: + self.trade_history.append( + {'price': self.current_price(), 'index': self._current_tick, + 'type': trade_type}) + + if (self._total_profit < self.max_drawdown or + self._total_unrealized_profit < self.max_drawdown): + self._done = True + + self._position_history.append(self._position) + + info = dict( + tick=self._current_tick, + action=action, + total_reward=self.total_reward, + total_profit=self._total_profit, + position=self._position.value, + trade_duration=self.get_trade_duration(), + current_profit_pct=self.get_unrealized_profit() + ) + + observation = self._get_observation() + + self._update_history(info) + + return observation, step_reward, self._done, info + + def is_tradesignal(self, action: int) -> bool: + """ + Determine if the signal is a trade signal + e.g.: agent wants a Actions.Buy while it is in a Positions.short + """ + return ( + (action == Actions.Buy.value and self._position == Positions.Neutral) + or (action == Actions.Sell.value and self._position == Positions.Long) + or (action == Actions.Sell.value and self._position == Positions.Neutral + and self.can_short) + or (action == Actions.Buy.value and self._position == Positions.Short + and self.can_short) + ) + + def _is_valid(self, action: int) -> bool: + """ + Determine if the signal is valid. + e.g.: agent wants a Actions.Sell while it is in a Positions.Long + """ + if self.can_short: + return action in [Actions.Buy.value, Actions.Sell.value, Actions.Neutral.value] + else: + if action == Actions.Sell.value and self._position != Positions.Long: + return False + return True From a8c9aa01fb3c11330618f26efa822bfe9394124e Mon Sep 17 00:00:00 2001 From: Emre Date: Fri, 16 Dec 2022 22:31:44 +0300 Subject: [PATCH 098/795] Add 3ac test --- tests/freqai/test_freqai_interface.py | 5 +- .../ReinforcementLearner_test_3ac.py | 65 +++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 tests/freqai/test_models/ReinforcementLearner_test_3ac.py diff --git a/tests/freqai/test_freqai_interface.py b/tests/freqai/test_freqai_interface.py index f19acb018..2c58d4c0a 100644 --- a/tests/freqai/test_freqai_interface.py +++ b/tests/freqai/test_freqai_interface.py @@ -34,6 +34,7 @@ def is_mac() -> bool: ('CatboostRegressor', False, False, False), ('ReinforcementLearner', False, True, False), ('ReinforcementLearner_multiproc', False, False, False), + ('ReinforcementLearner_test_3ac', False, False, False), ('ReinforcementLearner_test_4ac', False, False, False) ]) def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca, dbscan, float32): @@ -58,7 +59,7 @@ def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca, freqai_conf['freqai']['feature_parameters'].update({"use_SVM_to_remove_outliers": True}) freqai_conf['freqai']['data_split_parameters'].update({'shuffle': True}) - if 'test_4ac' in model: + if 'test_3ac' in model or 'test_4ac' in model: freqai_conf["freqaimodel_path"] = str(Path(__file__).parents[1] / "freqai" / "test_models") if 'ReinforcementLearner' in model: @@ -68,7 +69,7 @@ def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca, freqai_conf['freqai']['feature_parameters'].update({"use_SVM_to_remove_outliers": True}) freqai_conf['freqai']['data_split_parameters'].update({'shuffle': True}) - if 'test_4ac' in model: + if 'test_3ac' in model or 'test_4ac' in model: freqai_conf["freqaimodel_path"] = str(Path(__file__).parents[1] / "freqai" / "test_models") strategy = get_patched_freqai_strategy(mocker, freqai_conf) diff --git a/tests/freqai/test_models/ReinforcementLearner_test_3ac.py b/tests/freqai/test_models/ReinforcementLearner_test_3ac.py new file mode 100644 index 000000000..c267c76a8 --- /dev/null +++ b/tests/freqai/test_models/ReinforcementLearner_test_3ac.py @@ -0,0 +1,65 @@ +import logging + +import numpy as np + +from freqtrade.freqai.prediction_models.ReinforcementLearner import ReinforcementLearner +from freqtrade.freqai.RL.Base3ActionRLEnv import Actions, Base3ActionRLEnv, Positions + + +logger = logging.getLogger(__name__) + + +class ReinforcementLearner_test_3ac(ReinforcementLearner): + """ + User created Reinforcement Learning Model prediction model. + """ + + class MyRLEnv(Base3ActionRLEnv): + """ + User can override any function in BaseRLEnv and gym.Env. Here the user + sets a custom reward based on profit and trade duration. + """ + + def calculate_reward(self, action: int) -> float: + + # first, penalize if the action is not valid + if not self._is_valid(action): + return -2 + + pnl = self.get_unrealized_profit() + rew = np.sign(pnl) * (pnl + 1) + factor = 100. + + # reward agent for entering trades + if (action in (Actions.Buy.value, Actions.Sell.value) + and self._position == Positions.Neutral): + return 25 + # discourage agent from not entering trades + if action == Actions.Neutral.value and self._position == Positions.Neutral: + return -1 + + max_trade_duration = self.rl_config.get('max_trade_duration_candles', 300) + trade_duration = self._current_tick - self._last_trade_tick # type: ignore + + if trade_duration <= max_trade_duration: + factor *= 1.5 + elif trade_duration > max_trade_duration: + factor *= 0.5 + + # discourage sitting in position + if self._position in (Positions.Short, Positions.Long) and ( + action == Actions.Neutral.value + or (action == Actions.Sell.value and self._position == Positions.Short) + or (action == Actions.Buy.value and self._position == Positions.Long) + ): + return -1 * trade_duration / max_trade_duration + + # close position + if (action == Actions.Buy.value and self._position == Positions.Short) or ( + action == Actions.Sell.value and self._position == Positions.Long + ): + if pnl > self.profit_aim * self.rr: + factor *= self.rl_config["model_reward_parameters"].get("win_reward_factor", 2) + return float(rew * factor) + + return 0. From e604047158a56fe2e0185fec806b7fa1c465d3fa Mon Sep 17 00:00:00 2001 From: Emre Date: Fri, 16 Dec 2022 22:57:55 +0300 Subject: [PATCH 099/795] Enable RL tests on arm mac --- 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 2c58d4c0a..15e656776 100644 --- a/tests/freqai/test_freqai_interface.py +++ b/tests/freqai/test_freqai_interface.py @@ -41,7 +41,7 @@ def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca, if is_arm() and model == 'CatboostRegressor': pytest.skip("CatBoost is not supported on ARM") - if is_mac() and 'Reinforcement' in model: + if is_mac() and not is_arm() and 'Reinforcement' in model: pytest.skip("Reinforcement learning module not available on intel based Mac OS") model_save_ext = 'joblib' From c293401b22fe582463d14edbb7db75582c831212 Mon Sep 17 00:00:00 2001 From: Emre Date: Fri, 16 Dec 2022 23:19:08 +0300 Subject: [PATCH 100/795] Add can_short to freqai base model --- freqtrade/freqai/freqai_interface.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/freqai/freqai_interface.py b/freqtrade/freqai/freqai_interface.py index bbae7453f..9025f358a 100644 --- a/freqtrade/freqai/freqai_interface.py +++ b/freqtrade/freqai/freqai_interface.py @@ -104,6 +104,7 @@ class IFreqaiModel(ABC): self.metadata: Dict[str, Any] = self.dd.load_global_metadata_from_disk() self.data_provider: Optional[DataProvider] = None self.max_system_threads = max(int(psutil.cpu_count() * 2 - 2), 1) + self.can_short = True # overridden in start() with strategy.can_short record_params(config, self.full_path) From 329a0a3f45aa88c34fba9d605e329708a4a0f6b8 Mon Sep 17 00:00:00 2001 From: Robert Caulk Date: Sat, 17 Dec 2022 18:43:20 +0100 Subject: [PATCH 101/795] Update docs/freqai.md Co-authored-by: Matthias --- docs/freqai.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/freqai.md b/docs/freqai.md index 80096996d..d13d43f66 100644 --- a/docs/freqai.md +++ b/docs/freqai.md @@ -78,7 +78,7 @@ Forecasting chaotic time-series based systems, such as equity/cryptocurrency mar ### Citing FreqAI -FreqAI is published in the Journal of Open Source Software [link](https://joss.theoj.org/papers/10.21105/joss.04864). If you find FreqAI useful in your research, please use the following citation: +FreqAI is [published in the Journal of Open Source Software](https://joss.theoj.org/papers/10.21105/joss.04864). If you find FreqAI useful in your research, please use the following citation: ```bibtex @article{Caulk2022, From bad6fe77d3f388fb1e95b1df9bdf0dd89c6ad373 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 18 Dec 2022 17:21:50 +0100 Subject: [PATCH 102/795] Remove deprecated trade property --- freqtrade/persistence/trade_model.py | 6 ------ tests/test_freqtradebot.py | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index 19ba48fcd..186a1e584 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -397,12 +397,6 @@ class LocalTrade(): def close_date_utc(self): return self.close_date.replace(tzinfo=timezone.utc) - @property - def enter_side(self) -> str: - """ DEPRECATED, please use entry_side instead""" - # TODO: Please remove me after 2022.5 - return self.entry_side - @property def entry_side(self) -> str: if self.is_short: diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index faaefcafb..a4431358f 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2378,7 +2378,7 @@ def test_close_trade( trade.is_short = is_short assert trade - oobj = Order.parse_from_ccxt_object(enter_order, enter_order['symbol'], trade.enter_side) + oobj = Order.parse_from_ccxt_object(enter_order, enter_order['symbol'], trade.entry_side) trade.update_trade(oobj) oobj = Order.parse_from_ccxt_object(exit_order, exit_order['symbol'], trade.exit_side) trade.update_trade(oobj) From a439488b74f2351df7c70f8030af559e786078f2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 18 Dec 2022 17:42:05 +0100 Subject: [PATCH 103/795] Add initial trade_object documentation --- docs/strategy-customization.md | 36 ++------- docs/trade-object.md | 132 +++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 3 files changed, 141 insertions(+), 28 deletions(-) create mode 100644 docs/trade-object.md diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 0fb35ce89..462f20402 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -989,38 +989,18 @@ from freqtrade.persistence import Trade The following example queries for the current pair and trades from today, however other filters can easily be added. ``` python -if self.config['runmode'].value in ('live', 'dry_run'): - trades = Trade.get_trades([Trade.pair == metadata['pair'], - Trade.open_date > datetime.utcnow() - timedelta(days=1), - Trade.is_open.is_(False), - ]).order_by(Trade.close_date).all() - # Summarize profit for this pair. - curdayprofit = sum(trade.close_profit for trade in trades) +trades = Trade.get_trades_proxy(pair=metadata['pair'], + open_date=datetime.now(timezone.utc) - timedelta(days=1), + is_open=False, + ]).order_by(Trade.close_date).all() +# Summarize profit for this pair. +curdayprofit = sum(trade.close_profit for trade in trades) ``` -Get amount of stake_currency currently invested in Trades: - -``` python -if self.config['runmode'].value in ('live', 'dry_run'): - total_stakes = Trade.total_open_trades_stakes() -``` - -Retrieve performance per pair. -Returns a List of dicts per pair. - -``` python -if self.config['runmode'].value in ('live', 'dry_run'): - performance = Trade.get_overall_performance() -``` - -Sample return value: ETH/BTC had 5 trades, with a total profit of 1.5% (ratio of 0.015). - -``` json -{"pair": "ETH/BTC", "profit": 0.015, "count": 5} -``` +For a full list of available methods, please consult the [Trade object](trade-object.md) documentation. !!! Warning - Trade history is not available during backtesting or hyperopt. + Trade history is not available in `populate_*` methods during backtesting or hyperopt, and will result in empty results. ## Prevent trades from happening for a specific pair diff --git a/docs/trade-object.md b/docs/trade-object.md new file mode 100644 index 000000000..ff9571047 --- /dev/null +++ b/docs/trade-object.md @@ -0,0 +1,132 @@ +# Trade Object + +## Trade + +A position freqtrade enters is stored in a Trade object - which is persisted to the database. +It's a core concept of Freqtrade - and something you'll come across in many sections of the documentation, which will most likely point you to this location. + +It will be passed to the strategy in many [strategy callbacks](strategy-callbacks.md). The object passed to the strategy cannot be modified. + +## Available attributes + +The following attributes / properties are available for each individual trade - and can be used with `trade.` (e.g. `trade.pair`). + +| Attribute | DataType | Description | +|------------|-------------|-------------| +| `pair`| string | Pair of this trade +| `is_open`| boolean | Is the trade currently open, or has it been concluded +| `open_rate`| float | Rate this trade was entered at (Avg. entry rate in case of trade-adjustments) +| `close_rate`| float | Close rate - only set when is_open = False +| `stake_amount`| float | Amount in Stake (or Quote) currency. +| `amount`| float | Amount in Asset / Base currency that is currently owned. +| `open_date`| datetime | Timestamp when trade was opened **use `open_date_utc` instead** +| `open_date_utc`| datetime | Timestamp when trade was opened - in UTC +| `close_date`| datetime | Timestamp when trade was closed **use `close_date_utc` instead** +| `close_date_utc`| datetime | Timestamp when trade was closed - in UTC +| `close_profit`| float | Relative profit at the time of trade closure. `0.01` == 1% +| `close_profit_abs`| float | Absolute profit (in stake currency) at the time of trade closure. +| `leverage` | float | Leverage used for this trade - defaults to 1.0 in spot markets. +| `enter_tag`| string | Tag provided on entry via the `enter_tag` column in the dataframe +| `is_short` | boolean | True for short trades, False otherwise +| `orders` | Order[] | List of order objects attached to this trade. +| `date_last_filled_utc` | datetime | Time of the last filled order +| `entry_side` | "buy" / "sell" | Order Side the trade was entered +| `exit_side` | "buy" / "sell" | Order Side that will result in a trade exit / position reduction. +| `trade_direction` | "long" / "short" | Trade direction in text - long or short. +| `nr_of_successful_entries` | int | Number of successful (filled) entry orders +| `nr_of_successful_exits` | int | Number of successful (filled) exit orders + +## Class methods + +The following are class methods - which return generic information, and usually result in an explicit query against the database. +They can be used as `Trade.` - e.g. `open_trades = Trade.get_open_trade_count()` + +!!! Warning "Backtesting/hyperopt" + Most methods will work in both backtesting / hyperopt and live/dry modes. + During backtesting, it's limited to usage in [strategy callbacks](strategy-callbacks.md). Usage in `populate_*()` methods is not supported and will result in wrong results. + +### get_trades_proxy + +When your strategy needs some information on existing (open or close) trades - it's best to use `Trade.get_trades_proxy()`. + +Usage: + +``` python +from freqtrade.persistence import Trade +from datetime import timedelta + +# ... +trade_hist = Trade.get_trades_proxy(pair='ETH/USDT', is_open=False, open_date=current_date - timedelta(days=2)) + +``` + +`get_trades_proxy()` supports the following keyword arguments. All arguments are optional - calling `get_trades_proxy()` without arguments will return a list of all trades in the database. + +* `pair` e.g. `pair='ETH/USDT'` +* `is_open` e.g. `is_open=False` +* `open_date` e.g. `open_date=current_date - timedelta(days=2)` +* `close_date` e.g. `close_date=current_date - timedelta(days=5)` + +### get_open_trade_count + +Get the number of currently open trades + +``` python +from freqtrade.persistence import Trade +# ... +open_trades = Trade.get_open_trade_count() +``` + +### get_total_closed_profit + +Retrieve the total profit the bot has generated so far. +Aggregates `close_profit_abs` for all closed trades. + +``` python +from freqtrade.persistence import Trade + +# ... +profit = Trade.get_total_closed_profit() +``` + +### total_open_trades_stakes + +Retrieve the total stake_amount that's currently in trades. + +``` python +from freqtrade.persistence import Trade + +# ... +profit = Trade.total_open_trades_stakes() +``` + +### get_overall_performance + +Retrieve the overall performance - similar to the `/performance` telegram command. + +``` python +from freqtrade.persistence import Trade + +# ... +if self.config['runmode'].value in ('live', 'dry_run'): + performance = Trade.get_overall_performance() +``` + +Sample return value: ETH/BTC had 5 trades, with a total profit of 1.5% (ratio of 0.015). + +``` json +{"pair": "ETH/BTC", "profit": 0.015, "count": 5} +``` + +## Order Object + +An `Order` object represents an order on the exchange (or a simulated order in dry-run mode). +An `Order` object will always be tied to it's corresponding [`Trade`](#trade-object), and only really makes sense in the context of a trade. + +## Available information + +TODO: write me + +| Attribute | DataType | Description | +|------------|-------------|-------------| +TODO: write me diff --git a/mkdocs.yml b/mkdocs.yml index 81f2b7b0b..21fcafbed 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -13,6 +13,7 @@ nav: - Configuration: configuration.md - Strategy Customization: strategy-customization.md - Strategy Callbacks: strategy-callbacks.md + - Trade Object: trade-object.md - Stoploss: stoploss.md - Plugins: plugins.md - Start the bot: bot-usage.md From eda72ef26ce48b759ec6096a1082d393a451e353 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 18 Dec 2022 19:40:49 +0100 Subject: [PATCH 104/795] Add documentation for Order object --- docs/trade-object.md | 74 +++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/docs/trade-object.md b/docs/trade-object.md index ff9571047..7e0db1e3b 100644 --- a/docs/trade-object.md +++ b/docs/trade-object.md @@ -2,39 +2,39 @@ ## Trade -A position freqtrade enters is stored in a Trade object - which is persisted to the database. -It's a core concept of Freqtrade - and something you'll come across in many sections of the documentation, which will most likely point you to this location. +A position freqtrade enters is stored in a `Trade` object - which is persisted to the database. +It's a core concept of freqtrade - and something you'll come across in many sections of the documentation, which will most likely point you to this location. -It will be passed to the strategy in many [strategy callbacks](strategy-callbacks.md). The object passed to the strategy cannot be modified. +It will be passed to the strategy in many [strategy callbacks](strategy-callbacks.md). The object passed to the strategy cannot be modified directly. Indirect modifications may occur based on callback results. -## Available attributes +## Trade - Available attributes The following attributes / properties are available for each individual trade - and can be used with `trade.` (e.g. `trade.pair`). | Attribute | DataType | Description | |------------|-------------|-------------| -| `pair`| string | Pair of this trade -| `is_open`| boolean | Is the trade currently open, or has it been concluded -| `open_rate`| float | Rate this trade was entered at (Avg. entry rate in case of trade-adjustments) -| `close_rate`| float | Close rate - only set when is_open = False -| `stake_amount`| float | Amount in Stake (or Quote) currency. -| `amount`| float | Amount in Asset / Base currency that is currently owned. -| `open_date`| datetime | Timestamp when trade was opened **use `open_date_utc` instead** -| `open_date_utc`| datetime | Timestamp when trade was opened - in UTC -| `close_date`| datetime | Timestamp when trade was closed **use `close_date_utc` instead** -| `close_date_utc`| datetime | Timestamp when trade was closed - in UTC -| `close_profit`| float | Relative profit at the time of trade closure. `0.01` == 1% -| `close_profit_abs`| float | Absolute profit (in stake currency) at the time of trade closure. -| `leverage` | float | Leverage used for this trade - defaults to 1.0 in spot markets. -| `enter_tag`| string | Tag provided on entry via the `enter_tag` column in the dataframe -| `is_short` | boolean | True for short trades, False otherwise -| `orders` | Order[] | List of order objects attached to this trade. -| `date_last_filled_utc` | datetime | Time of the last filled order -| `entry_side` | "buy" / "sell" | Order Side the trade was entered -| `exit_side` | "buy" / "sell" | Order Side that will result in a trade exit / position reduction. -| `trade_direction` | "long" / "short" | Trade direction in text - long or short. -| `nr_of_successful_entries` | int | Number of successful (filled) entry orders -| `nr_of_successful_exits` | int | Number of successful (filled) exit orders +`pair`| string | Pair of this trade +`is_open`| boolean | Is the trade currently open, or has it been concluded +`open_rate`| float | Rate this trade was entered at (Avg. entry rate in case of trade-adjustments) +`close_rate`| float | Close rate - only set when is_open = False +`stake_amount`| float | Amount in Stake (or Quote) currency. +`amount`| float | Amount in Asset / Base currency that is currently owned. +`open_date`| datetime | Timestamp when trade was opened **use `open_date_utc` instead** +`open_date_utc`| datetime | Timestamp when trade was opened - in UTC +`close_date`| datetime | Timestamp when trade was closed **use `close_date_utc` instead** +`close_date_utc`| datetime | Timestamp when trade was closed - in UTC +`close_profit`| float | Relative profit at the time of trade closure. `0.01` == 1% +`close_profit_abs`| float | Absolute profit (in stake currency) at the time of trade closure. +`leverage` | float | Leverage used for this trade - defaults to 1.0 in spot markets. +`enter_tag`| string | Tag provided on entry via the `enter_tag` column in the dataframe +`is_short` | boolean | True for short trades, False otherwise +`orders` | Order[] | List of order objects attached to this trade (includes both filled and cancelled orders) +`date_last_filled_utc` | datetime | Time of the last filled order +`entry_side` | "buy" / "sell" | Order Side the trade was entered +`exit_side` | "buy" / "sell" | Order Side that will result in a trade exit / position reduction. +`trade_direction` | "long" / "short" | Trade direction in text - long or short. +`nr_of_successful_entries` | int | Number of successful (filled) entry orders +`nr_of_successful_exits` | int | Number of successful (filled) exit orders ## Class methods @@ -123,10 +123,26 @@ Sample return value: ETH/BTC had 5 trades, with a total profit of 1.5% (ratio of An `Order` object represents an order on the exchange (or a simulated order in dry-run mode). An `Order` object will always be tied to it's corresponding [`Trade`](#trade-object), and only really makes sense in the context of a trade. -## Available information +### Order - Available attributes -TODO: write me +an Order object is typically attached to a trade. +Most properties here can be None as they are dependant on the exchange response. | Attribute | DataType | Description | |------------|-------------|-------------| -TODO: write me +`trade` | Trade | Trade object this order is attached to +`ft_pair` | string | Pair this order is for +`ft_is_open` | boolean | is the order filled? +`order_type` | string | Order type as defined on the exchange - usually market, limit or stoploss +`status` | string | Status as defined by ccxt. Usually open, closed, expired or canceled +`side` | string | Buy or Sell +`price` | float | Price the order was placed at +`average` | float | Average price the order filled at +`amount` | float | Amount in base currency +`filled` | float | Filled amount (in base currency) +`remaining` | float | Remaining amount +`cost` | float | Cost of the order - usually average * filled +`order_date` | datetime | Order creation date **use `order_date_utc` instead** +`order_date_utc` | datetime | Order creation date (in UTC) +`order_fill_date` | datetime | Order fill date **use `order_fill_utc` instead** +`order_fill_date_utc` | datetime | Order fill date From 1f4cc145c48adf470af21b3022ab0b9c8ae88444 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 18 Dec 2022 20:02:38 +0100 Subject: [PATCH 105/795] Move trade docs to advanced section --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 21fcafbed..c44e4640e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -13,7 +13,6 @@ nav: - Configuration: configuration.md - Strategy Customization: strategy-customization.md - Strategy Callbacks: strategy-callbacks.md - - Trade Object: trade-object.md - Stoploss: stoploss.md - Plugins: plugins.md - Start the bot: bot-usage.md @@ -42,6 +41,7 @@ nav: - Backtest analysis: advanced-backtesting.md - Advanced Topics: - Advanced Post-installation Tasks: advanced-setup.md + - Trade Object: trade-object.md - Advanced Strategy: strategy-advanced.md - Advanced Hyperopt: advanced-hyperopt.md - Producer/Consumer mode: producer-consumer.md From bb33b96ba7bcf90f442251c9e7e4d44392cec9a2 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Sun, 18 Dec 2022 22:28:12 +0100 Subject: [PATCH 106/795] init cache on first iteration, init checks, limit length and charmap to info replace if invalid, move filter logic --- docs/includes/pairlists.md | 2 +- freqtrade/plugins/pairlist/RemotePairList.py | 87 +++++++++++++------- 2 files changed, 60 insertions(+), 29 deletions(-) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index 3a6ab7a3c..0bff9b29b 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -202,7 +202,7 @@ The user is responsible for providing a server or local file that returns a JSON { "pairs": ["XRP/USDT", "ETH/USDT", "LTC/USDT"], "refresh_period": 1800, - "info": "Pairlist updated on 2022-12-12 at 12:12" + "info": "Pairlist updated on 2022-12-12 at 12:12" // Maximum Length: 256 Characters, Charset: Alphanumeric + "+-.,%:" } ``` diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index e46ac0419..a0e140b42 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -6,7 +6,7 @@ Provides pair list fetched from a remote source import json import logging from pathlib import Path -from typing import Any, Dict, List, Tuple +from typing import Any, Dict, List, Optional, Tuple import requests from cachetools import TTLCache @@ -39,12 +39,13 @@ class RemotePairList(IPairList): 'for "pairlist.config.pairlist_url"') self._number_pairs = self._pairlistconfig['number_assets'] - self._refresh_period = self._pairlistconfig.get('refresh_period', 1800) + self._refresh_period: int = self._pairlistconfig.get('refresh_period', 1800) self._keep_pairlist_on_failure = self._pairlistconfig.get('keep_pairlist_on_failure', True) - self._pair_cache: TTLCache = TTLCache(maxsize=1, ttl=self._refresh_period) + self._pair_cache: Optional[TTLCache] = None self._pairlist_url = self._pairlistconfig.get('pairlist_url', '') self._read_timeout = self._pairlistconfig.get('read_timeout', 60) self._bearer_token = self._pairlistconfig.get('bearer_token', '') + self._init_done = False self._last_pairlist: List[Any] = list() @property @@ -62,6 +63,15 @@ class RemotePairList(IPairList): """ return f"{self.name} - {self._pairlistconfig['number_assets']} pairs from RemotePairlist." + def return_last_pairlist(self) -> List[str]: + if self._keep_pairlist_on_failure: + pairlist = self._last_pairlist + self.log_once('Keeping last fetched pairlist', logger.info) + else: + pairlist = [] + + return pairlist + def fetch_pairlist(self) -> Tuple[List[str], float, str]: headers = { @@ -81,23 +91,35 @@ class RemotePairList(IPairList): if "application/json" in str(content_type): jsonparse = response.json() - pairlist = jsonparse['pairs'] - info = jsonparse.get('info', '') - else: - raise OperationalException('RemotePairList is not of type JSON abort ') + pairlist = jsonparse.get('pairs', []) + remote_info = jsonparse.get('info', '')[:256].strip() + remote_refresh_period = jsonparse.get('refresh_period', self._refresh_period) - self._refresh_period = jsonparse.get('refresh_period', self._refresh_period) - self._pair_cache = TTLCache(maxsize=1, ttl=self._refresh_period) + info = "".join(char if char.isalnum() or + char in " +-.,%:" else "-" for char in remote_info) + + if not self._init_done and self._refresh_period < remote_refresh_period: + self.log_once(f'Refresh Period has been increased from {self._refresh_period}' + f' to {remote_refresh_period} from Remote.', logger.info) + + self._refresh_period = remote_refresh_period + self._pair_cache = TTLCache(maxsize=1, ttl=self._refresh_period) + + self._init_done = True + else: + if self._init_done: + self.log_once(f'Error: RemotePairList is not of type JSON: ' + f' {self._pairlist_url}', logger.info) + pairlist = self.return_last_pairlist() + + else: + raise OperationalException('RemotePairList is not of type JSON abort ') except requests.exceptions.RequestException: self.log_once(f'Was not able to fetch pairlist from:' f' {self._pairlist_url}', logger.info) - if self._keep_pairlist_on_failure: - pairlist = self._last_pairlist - self.log_once('Keeping last fetched pairlist', logger.info) - else: - pairlist = [] + pairlist = self.return_last_pairlist() time_elapsed = 0 @@ -110,12 +132,17 @@ class RemotePairList(IPairList): :return: List of pairs """ - pairlist = self._pair_cache.get('pairlist') + if self._init_done and self._pair_cache: + pairlist = self._pair_cache.get('pairlist') + else: + pairlist = [] + time_elapsed = 0.0 if pairlist: # Item found - no refresh necessary return pairlist.copy() + self._init_done = True else: if self._pairlist_url.startswith("file:///"): filename = self._pairlist_url.split("file:///", 1)[1] @@ -127,17 +154,25 @@ class RemotePairList(IPairList): jsonparse = json.load(json_file) pairlist = jsonparse['pairs'] info = jsonparse.get('info', '') - self._refresh_period = jsonparse.get('refresh_period', self._refresh_period) - self._pair_cache = TTLCache(maxsize=1, ttl=self._refresh_period) + if not self._init_done: + self._refresh_period = jsonparse.get('refresh_period', + self._refresh_period) + self._pair_cache = TTLCache(maxsize=1, ttl=self._refresh_period) + self._init_done = True else: raise ValueError(f"{self._pairlist_url} does not exist.") else: # Fetch Pairlist from Remote URL pairlist, time_elapsed, info = self.fetch_pairlist() - pairlist = self.filter_pairlist(pairlist, tickers) - self._pair_cache['pairlist'] = pairlist.copy() + self.log_once(f"Fetched pairs: {pairlist}", logger.debug) + + pairlist = self._whitelist_for_active_markets(pairlist) + pairlist = pairlist[:self._number_pairs] + + if self._pair_cache: + self._pair_cache['pairlist'] = pairlist.copy() if time_elapsed != 0.0: self.log_once(f'{info} Fetched in {time_elapsed} seconds.', logger.info) @@ -145,6 +180,7 @@ class RemotePairList(IPairList): self.log_once(f'{info} Fetched Pairlist.', logger.info) self._last_pairlist = list(pairlist) + return pairlist def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: @@ -155,12 +191,7 @@ class RemotePairList(IPairList): :param tickers: Tickers (from exchange.get_tickers). May be cached. :return: new whitelist """ - - # Validate whitelist to only have active market pairs - pairlist = self._whitelist_for_active_markets(pairlist) - pairlist = self.verify_blacklist(pairlist, logger.info) - # Limit pairlist to the requested number of pairs - pairlist = pairlist[:self._number_pairs] - self.log_once(f"Searching {self._number_pairs} pairs: {pairlist}", logger.info) - - return pairlist + rpl_pairlist = self.gen_pairlist(tickers) + merged_list = pairlist + rpl_pairlist + merged_list = sorted(set(merged_list), key=merged_list.index) + return merged_list From 6380c3d46205bdb3b29c5bff213cb7b113b93a79 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Sun, 18 Dec 2022 23:37:18 +0100 Subject: [PATCH 107/795] reduce duplicate code, fix cache check --- freqtrade/plugins/pairlist/RemotePairList.py | 54 ++++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index a0e140b42..205ee5742 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -63,6 +63,29 @@ class RemotePairList(IPairList): """ return f"{self.name} - {self._pairlistconfig['number_assets']} pairs from RemotePairlist." + def process_json(self, jsonparse) -> Tuple[List[str], str]: + + pairlist = jsonparse.get('pairs', []) + remote_info = jsonparse.get('info', '')[:256].strip() + remote_refresh_period = jsonparse.get('refresh_period', self._refresh_period) + + info = "".join(char if char.isalnum() or + char in " +-.,%:" else "-" for char in remote_info) + + if not self._init_done: + if self._refresh_period < remote_refresh_period: + self.log_once(f'Refresh Period has been increased from {self._refresh_period}' + f' to {remote_refresh_period} from Remote.', logger.info) + + self._refresh_period = remote_refresh_period + self._pair_cache = TTLCache(maxsize=1, ttl=self._refresh_period) + else: + self._pair_cache = TTLCache(maxsize=1, ttl=self._refresh_period) + + self._init_done = True + + return pairlist, info + def return_last_pairlist(self) -> List[str]: if self._keep_pairlist_on_failure: pairlist = self._last_pairlist @@ -91,27 +114,12 @@ class RemotePairList(IPairList): if "application/json" in str(content_type): jsonparse = response.json() - pairlist = jsonparse.get('pairs', []) - remote_info = jsonparse.get('info', '')[:256].strip() - remote_refresh_period = jsonparse.get('refresh_period', self._refresh_period) - - info = "".join(char if char.isalnum() or - char in " +-.,%:" else "-" for char in remote_info) - - if not self._init_done and self._refresh_period < remote_refresh_period: - self.log_once(f'Refresh Period has been increased from {self._refresh_period}' - f' to {remote_refresh_period} from Remote.', logger.info) - - self._refresh_period = remote_refresh_period - self._pair_cache = TTLCache(maxsize=1, ttl=self._refresh_period) - - self._init_done = True + pairlist, info = self.process_json(jsonparse) else: if self._init_done: self.log_once(f'Error: RemotePairList is not of type JSON: ' f' {self._pairlist_url}', logger.info) pairlist = self.return_last_pairlist() - else: raise OperationalException('RemotePairList is not of type JSON abort ') @@ -132,7 +140,7 @@ class RemotePairList(IPairList): :return: List of pairs """ - if self._init_done and self._pair_cache: + if self._init_done and self._pair_cache is not None: pairlist = self._pair_cache.get('pairlist') else: pairlist = [] @@ -142,7 +150,6 @@ class RemotePairList(IPairList): if pairlist: # Item found - no refresh necessary return pairlist.copy() - self._init_done = True else: if self._pairlist_url.startswith("file:///"): filename = self._pairlist_url.split("file:///", 1)[1] @@ -152,14 +159,7 @@ class RemotePairList(IPairList): with open(filename) as json_file: # Load the JSON data into a dictionary jsonparse = json.load(json_file) - pairlist = jsonparse['pairs'] - info = jsonparse.get('info', '') - - if not self._init_done: - self._refresh_period = jsonparse.get('refresh_period', - self._refresh_period) - self._pair_cache = TTLCache(maxsize=1, ttl=self._refresh_period) - self._init_done = True + pairlist, info = self.process_json(jsonparse) else: raise ValueError(f"{self._pairlist_url} does not exist.") else: @@ -171,7 +171,7 @@ class RemotePairList(IPairList): pairlist = self._whitelist_for_active_markets(pairlist) pairlist = pairlist[:self._number_pairs] - if self._pair_cache: + if self._pair_cache is not None: self._pair_cache['pairlist'] = pairlist.copy() if time_elapsed != 0.0: From b61fc161bfde822c1569696cfa2d5e5d68a198b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 03:00:43 +0000 Subject: [PATCH 108/795] Bump isort from 5.10.1 to 5.11.3 Bumps [isort](https://github.com/pycqa/isort) from 5.10.1 to 5.11.3. - [Release notes](https://github.com/pycqa/isort/releases) - [Changelog](https://github.com/PyCQA/isort/blob/main/CHANGELOG.md) - [Commits](https://github.com/pycqa/isort/compare/5.10.1...5.11.3) --- updated-dependencies: - dependency-name: isort 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 9de41b273..03129bf07 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -16,7 +16,7 @@ pytest-asyncio==0.20.3 pytest-cov==4.0.0 pytest-mock==3.10.0 pytest-random-order==1.1.0 -isort==5.10.1 +isort==5.11.3 # For datetime mocking time-machine==2.8.2 # fastapi testing From d86885c7f95c9d7eb1ef187cfdec012fa850aba9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 03:00:46 +0000 Subject: [PATCH 109/795] Bump blosc from 1.10.6 to 1.11.1 Bumps [blosc](https://github.com/blosc/python-blosc) from 1.10.6 to 1.11.1. - [Release notes](https://github.com/blosc/python-blosc/releases) - [Changelog](https://github.com/Blosc/python-blosc/blob/main/RELEASE_NOTES.rst) - [Commits](https://github.com/blosc/python-blosc/compare/v1.10.6...v1.11.1) --- updated-dependencies: - dependency-name: blosc 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 37f1d31e1..eae0cc7da 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,7 +21,7 @@ pycoingecko==3.1.0 jinja2==3.1.2 tables==3.7.0 blosc==1.10.6; platform_machine == 'arm64' -blosc==1.11.0; platform_machine != 'arm64' +blosc==1.11.1; platform_machine != 'arm64' joblib==1.2.0 pyarrow==10.0.1; platform_machine != 'armv7l' From 06225b9501b178c67c9cea5481d32298ebf07c4a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 03:00:54 +0000 Subject: [PATCH 110/795] Bump types-python-dateutil from 2.8.19.4 to 2.8.19.5 Bumps [types-python-dateutil](https://github.com/python/typeshed) from 2.8.19.4 to 2.8.19.5. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-python-dateutil 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 9de41b273..e750dd577 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -30,4 +30,4 @@ types-cachetools==5.2.1 types-filelock==3.2.7 types-requests==2.28.11.5 types-tabulate==0.9.0.0 -types-python-dateutil==2.8.19.4 +types-python-dateutil==2.8.19.5 From 7216d140ded9aa54d2655f9d32bc9927dfb9acd6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 03:01:09 +0000 Subject: [PATCH 111/795] Bump torch from 1.13.0 to 1.13.1 Bumps [torch](https://github.com/pytorch/pytorch) from 1.13.0 to 1.13.1. - [Release notes](https://github.com/pytorch/pytorch/releases) - [Changelog](https://github.com/pytorch/pytorch/blob/master/RELEASE.md) - [Commits](https://github.com/pytorch/pytorch/compare/v1.13.0...v1.13.1) --- updated-dependencies: - dependency-name: torch dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-freqai-rl.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-freqai-rl.txt b/requirements-freqai-rl.txt index 67bd66102..db8d8d169 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.0 +torch==1.13.1 stable-baselines3==1.6.2 sb3-contrib==1.6.2 # Gym is forced to this version by stable-baselines3. From fa87e080715bf69ce6ac8301e66262aaf8b7425e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 03:01:26 +0000 Subject: [PATCH 112/795] Bump ccxt from 2.2.92 to 2.4.27 Bumps [ccxt](https://github.com/ccxt/ccxt) from 2.2.92 to 2.4.27. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/2.2.92...2.4.27) --- updated-dependencies: - dependency-name: ccxt 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 37f1d31e1..bc3b8ad77 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ numpy==1.23.5 pandas==1.5.2 pandas-ta==0.3.14b -ccxt==2.2.92 +ccxt==2.4.27 # Pin cryptography for now due to rust build errors with piwheels cryptography==38.0.1; platform_machine == 'armv7l' cryptography==38.0.4; platform_machine != 'armv7l' From 0c8d657d9202345e3afe17d1bdb3c994337de2b0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 19 Dec 2022 06:27:38 +0100 Subject: [PATCH 113/795] update types-dateutil precommit --- .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 ccf9d5098..a7e60ce90 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,7 @@ repos: - types-filelock==3.2.7 - types-requests==2.28.11.5 - types-tabulate==0.9.0.0 - - types-python-dateutil==2.8.19.4 + - types-python-dateutil==2.8.19.5 # stages: [push] - repo: https://github.com/pycqa/isort From 86b30d2d6648971cd8a60e374bb932ac7ffabbd3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 19 Dec 2022 07:01:22 +0100 Subject: [PATCH 114/795] Improve emc test resiliancy --- tests/rpc/test_rpc_emc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rpc/test_rpc_emc.py b/tests/rpc/test_rpc_emc.py index e1537ec9e..26512e30b 100644 --- a/tests/rpc/test_rpc_emc.py +++ b/tests/rpc/test_rpc_emc.py @@ -267,7 +267,7 @@ async def test_emc_create_connection_error(default_conf, caplog, mocker): emc = ExternalMessageConsumer(default_conf, dp) try: - await asyncio.sleep(0.01) + await asyncio.sleep(0.05) assert log_has("Unexpected error has occurred:", caplog) finally: emc.shutdown() From a276ef4b061183e5209497bee9b7cb83a87d8126 Mon Sep 17 00:00:00 2001 From: robcaulk Date: Mon, 19 Dec 2022 11:49:31 +0100 Subject: [PATCH 115/795] ensure long only RL is tested --- tests/freqai/test_freqai_interface.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/freqai/test_freqai_interface.py b/tests/freqai/test_freqai_interface.py index 15e656776..af104f3d2 100644 --- a/tests/freqai/test_freqai_interface.py +++ b/tests/freqai/test_freqai_interface.py @@ -27,17 +27,19 @@ def is_mac() -> bool: return "Darwin" in machine -@pytest.mark.parametrize('model, pca, dbscan, float32', [ - ('LightGBMRegressor', True, False, True), - ('XGBoostRegressor', False, True, False), - ('XGBoostRFRegressor', False, False, False), - ('CatboostRegressor', False, False, False), - ('ReinforcementLearner', False, True, False), - ('ReinforcementLearner_multiproc', False, False, False), - ('ReinforcementLearner_test_3ac', False, False, False), - ('ReinforcementLearner_test_4ac', False, False, False) +@pytest.mark.parametrize('model, pca, dbscan, float32, can_short', [ + ('LightGBMRegressor', True, False, True, True), + ('XGBoostRegressor', False, True, False, True), + ('XGBoostRFRegressor', False, False, False, True), + ('CatboostRegressor', False, False, False, True), + ('ReinforcementLearner', False, True, False, True), + ('ReinforcementLearner_multiproc', False, False, False, True), + ('ReinforcementLearner_test_3ac', False, False, False, False), + ('ReinforcementLearner_test_3ac', False, False, False, True), + ('ReinforcementLearner_test_4ac', False, False, False, True) ]) -def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca, dbscan, float32): +def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca, + dbscan, float32, can_short): if is_arm() and model == 'CatboostRegressor': pytest.skip("CatBoost is not supported on ARM") @@ -59,9 +61,6 @@ def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca, freqai_conf['freqai']['feature_parameters'].update({"use_SVM_to_remove_outliers": True}) freqai_conf['freqai']['data_split_parameters'].update({'shuffle': True}) - if 'test_3ac' in model or 'test_4ac' in model: - freqai_conf["freqaimodel_path"] = str(Path(__file__).parents[1] / "freqai" / "test_models") - if 'ReinforcementLearner' in model: model_save_ext = 'zip' freqai_conf = make_rl_config(freqai_conf) @@ -78,6 +77,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.can_short = can_short freqai.dk = FreqaiDataKitchen(freqai_conf) freqai.dk.set_paths('ADA/BTC', 10000) timerange = TimeRange.parse_timerange("20180110-20180130") From 5405d8fa6fd425a63c4287574f8a168131772967 Mon Sep 17 00:00:00 2001 From: robcaulk Date: Mon, 19 Dec 2022 12:14:53 +0100 Subject: [PATCH 116/795] add discussion and tips for Base3ActionRLEnvironment --- docs/freqai-reinforcement-learning.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/freqai-reinforcement-learning.md b/docs/freqai-reinforcement-learning.md index f3d6c97f8..c2bcb75f8 100644 --- a/docs/freqai-reinforcement-learning.md +++ b/docs/freqai-reinforcement-learning.md @@ -275,12 +275,12 @@ FreqAI also provides a built in episodic summary logger called `self.tensorboard ### Choosing a base environment -FreqAI provides two base environments, `Base4ActionEnvironment` and `Base5ActionEnvironment`. As the names imply, the environments are customized for agents that can select from 4 or 5 actions. In the `Base4ActionEnvironment`, the agent can enter long, enter short, hold neutral, or exit position. Meanwhile, in the `Base5ActionEnvironment`, the agent has the same actions as Base4, but instead of a single exit action, it separates exit long and exit short. The main changes stemming from the environment selection include: +FreqAI provides three base environments, `Base3ActionRLEnvironment`, `Base4ActionEnvironment` and `Base5ActionEnvironment`. As the names imply, the environments are customized for agents that can select from 3, 4 or 5 actions. The `Base3ActionEnvironment` is the simplest, the agent can select from hold, long, or short. This environment can also be used for long-only bots (it automatically follows the `can_short` flag from the strategy), where long is the enter condition and short is the exit condition. Meanwhile, in the `Base4ActionEnvironment`, the agent can enter long, enter short, hold neutral, or exit position. Meanwhile, in the `Base5ActionEnvironment`, the agent has the same actions as Base4, but instead of a single exit action, it separates exit long and exit short. The main changes stemming from the environment selection include: * the actions available in the `calculate_reward` * the actions consumed by the user strategy -Both of the FreqAI provided environments inherit from an action/position agnostic environment object called the `BaseEnvironment`, which contains all shared logic. The architecture is designed to be easily customized. The simplest customization is the `calculate_reward()` (see details [here](#creating-a-custom-reward-function)). However, the customizations can be further extended into any of the functions inside the environment. You can do this by simply overriding those functions inside your `MyRLEnv` in the prediction model file. Or for more advanced customizations, it is encouraged to create an entirely new environment inherited from `BaseEnvironment`. +All of the FreqAI provided environments inherit from an action/position agnostic environment object called the `BaseEnvironment`, which contains all shared logic. The architecture is designed to be easily customized. The simplest customization is the `calculate_reward()` (see details [here](#creating-a-custom-reward-function)). However, the customizations can be further extended into any of the functions inside the environment. You can do this by simply overriding those functions inside your `MyRLEnv` in the prediction model file. Or for more advanced customizations, it is encouraged to create an entirely new environment inherited from `BaseEnvironment`. !!! Note - FreqAI does not provide by default, a long-only training environment. However, creating one should be as simple as copy-pasting one of the built in environments and removing the `short` actions (and all associated references to those). + Only the `Base3ActionRLEnv` can do long-only training/trading (set the user strategy attribute `can_short = False`). From 5b9e3af276b3a7ebc1f9a4df0742f8a0eafa4332 Mon Sep 17 00:00:00 2001 From: robcaulk Date: Mon, 19 Dec 2022 12:22:15 +0100 Subject: [PATCH 117/795] improve wording --- docs/freqai-reinforcement-learning.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/freqai-reinforcement-learning.md b/docs/freqai-reinforcement-learning.md index c2bcb75f8..22772c2ec 100644 --- a/docs/freqai-reinforcement-learning.md +++ b/docs/freqai-reinforcement-learning.md @@ -275,7 +275,7 @@ FreqAI also provides a built in episodic summary logger called `self.tensorboard ### Choosing a base environment -FreqAI provides three base environments, `Base3ActionRLEnvironment`, `Base4ActionEnvironment` and `Base5ActionEnvironment`. As the names imply, the environments are customized for agents that can select from 3, 4 or 5 actions. The `Base3ActionEnvironment` is the simplest, the agent can select from hold, long, or short. This environment can also be used for long-only bots (it automatically follows the `can_short` flag from the strategy), where long is the enter condition and short is the exit condition. Meanwhile, in the `Base4ActionEnvironment`, the agent can enter long, enter short, hold neutral, or exit position. Meanwhile, in the `Base5ActionEnvironment`, the agent has the same actions as Base4, but instead of a single exit action, it separates exit long and exit short. The main changes stemming from the environment selection include: +FreqAI provides three base environments, `Base3ActionRLEnvironment`, `Base4ActionEnvironment` and `Base5ActionEnvironment`. As the names imply, the environments are customized for agents that can select from 3, 4 or 5 actions. The `Base3ActionEnvironment` is the simplest, the agent can select from hold, long, or short. This environment can also be used for long-only bots (it automatically follows the `can_short` flag from the strategy), where long is the enter condition and short is the exit condition. Meanwhile, in the `Base4ActionEnvironment`, the agent can enter long, enter short, hold neutral, or exit position. Finally, in the `Base5ActionEnvironment`, the agent has the same actions as Base4, but instead of a single exit action, it separates exit long and exit short. The main changes stemming from the environment selection include: * the actions available in the `calculate_reward` * the actions consumed by the user strategy From 4bad2b5c042fc754b7fd911ca9e5aff7a530a912 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 19 Dec 2022 13:27:07 +0100 Subject: [PATCH 118/795] Apply suggestions from code review Co-authored-by: Emre --- requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index eae0cc7da..170389247 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,8 +20,7 @@ tabulate==0.9.0 pycoingecko==3.1.0 jinja2==3.1.2 tables==3.7.0 -blosc==1.10.6; platform_machine == 'arm64' -blosc==1.11.1; platform_machine != 'arm64' +blosc==1.11.1 joblib==1.2.0 pyarrow==10.0.1; platform_machine != 'armv7l' From 43f5a16006805d763b59c3cf1f8ff32aee47df66 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Mon, 19 Dec 2022 15:36:28 +0100 Subject: [PATCH 119/795] parse exception handling, remove info, cache change --- docs/includes/pairlists.md | 3 +- freqtrade/plugins/pairlist/RemotePairList.py | 68 +++++++++++--------- tests/plugins/test_remotepairlist.py | 6 +- 3 files changed, 41 insertions(+), 36 deletions(-) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index 0bff9b29b..5fda038bd 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -202,11 +202,10 @@ The user is responsible for providing a server or local file that returns a JSON { "pairs": ["XRP/USDT", "ETH/USDT", "LTC/USDT"], "refresh_period": 1800, - "info": "Pairlist updated on 2022-12-12 at 12:12" // Maximum Length: 256 Characters, Charset: Alphanumeric + "+-.,%:" } ``` -The `pairs` property should contain a list of strings with the trading pairs to be used by the bot. The `refresh_period` property is optional and specifies the number of seconds that the pairlist should be cached before being refreshed. The `info` property is also optional and can be used to provide any additional information about the pairlist. +The `pairs` property should contain a list of strings with the trading pairs to be used by the bot. The `refresh_period` property is optional and specifies the number of seconds that the pairlist should be cached before being refreshed. The optional `keep_pairlist_on_failure` specifies whether the previous received pairlist should be used if the remote server is not reachable or returns an error. The default value is true. diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index 205ee5742..25530457a 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -6,7 +6,7 @@ Provides pair list fetched from a remote source import json import logging from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict, List, Tuple import requests from cachetools import TTLCache @@ -41,7 +41,7 @@ class RemotePairList(IPairList): self._number_pairs = self._pairlistconfig['number_assets'] self._refresh_period: int = self._pairlistconfig.get('refresh_period', 1800) self._keep_pairlist_on_failure = self._pairlistconfig.get('keep_pairlist_on_failure', True) - self._pair_cache: Optional[TTLCache] = None + self._pair_cache: TTLCache = TTLCache(maxsize=1, ttl=self._refresh_period) self._pairlist_url = self._pairlistconfig.get('pairlist_url', '') self._read_timeout = self._pairlistconfig.get('read_timeout', 60) self._bearer_token = self._pairlistconfig.get('bearer_token', '') @@ -63,28 +63,20 @@ class RemotePairList(IPairList): """ return f"{self.name} - {self._pairlistconfig['number_assets']} pairs from RemotePairlist." - def process_json(self, jsonparse) -> Tuple[List[str], str]: + def process_json(self, jsonparse) -> List[str]: pairlist = jsonparse.get('pairs', []) - remote_info = jsonparse.get('info', '')[:256].strip() - remote_refresh_period = jsonparse.get('refresh_period', self._refresh_period) + remote_refresh_period = int(jsonparse.get('refresh_period', self._refresh_period)) - info = "".join(char if char.isalnum() or - char in " +-.,%:" else "-" for char in remote_info) - - if not self._init_done: - if self._refresh_period < remote_refresh_period: - self.log_once(f'Refresh Period has been increased from {self._refresh_period}' - f' to {remote_refresh_period} from Remote.', logger.info) - - self._refresh_period = remote_refresh_period - self._pair_cache = TTLCache(maxsize=1, ttl=self._refresh_period) - else: - self._pair_cache = TTLCache(maxsize=1, ttl=self._refresh_period) + if self._refresh_period < remote_refresh_period: + self.log_once(f'Refresh Period has been increased from {self._refresh_period}' + f' to minimum allowed: {remote_refresh_period} from Remote.', logger.info) + self._refresh_period = remote_refresh_period + self._pair_cache = TTLCache(maxsize=1, ttl=remote_refresh_period) self._init_done = True - return pairlist, info + return pairlist def return_last_pairlist(self) -> List[str]: if self._keep_pairlist_on_failure: @@ -95,7 +87,7 @@ class RemotePairList(IPairList): return pairlist - def fetch_pairlist(self) -> Tuple[List[str], float, str]: + def fetch_pairlist(self) -> Tuple[List[str], float]: headers = { 'User-Agent': 'Freqtrade/' + __version__ + ' Remotepairlist' @@ -104,8 +96,6 @@ class RemotePairList(IPairList): if self._bearer_token: headers['Authorization'] = f'Bearer {self._bearer_token}' - info = "Pairlist" - try: response = requests.get(self._pairlist_url, headers=headers, timeout=self._read_timeout) @@ -114,7 +104,17 @@ class RemotePairList(IPairList): if "application/json" in str(content_type): jsonparse = response.json() - pairlist, info = self.process_json(jsonparse) + + try: + pairlist = self.process_json(jsonparse) + except Exception as e: + + if self._init_done: + pairlist = self.return_last_pairlist() + logger.warning(f'Error while processing JSON data: {type(e)}') + else: + raise OperationalException(f'Error while processing JSON data: {type(e)}') + else: if self._init_done: self.log_once(f'Error: RemotePairList is not of type JSON: ' @@ -131,7 +131,7 @@ class RemotePairList(IPairList): time_elapsed = 0 - return pairlist, time_elapsed, info + return pairlist, time_elapsed def gen_pairlist(self, tickers: Tickers) -> List[str]: """ @@ -140,7 +140,7 @@ class RemotePairList(IPairList): :return: List of pairs """ - if self._init_done and self._pair_cache is not None: + if self._init_done: pairlist = self._pair_cache.get('pairlist') else: pairlist = [] @@ -159,25 +159,33 @@ class RemotePairList(IPairList): with open(filename) as json_file: # Load the JSON data into a dictionary jsonparse = json.load(json_file) - pairlist, info = self.process_json(jsonparse) + + try: + pairlist = self.process_json(jsonparse) + except Exception as e: + if self._init_done: + pairlist = self.return_last_pairlist() + logger.warning(f'Error while processing JSON data: {type(e)}') + else: + raise OperationalException('Error while processing' + f'JSON data: {type(e)}') else: raise ValueError(f"{self._pairlist_url} does not exist.") else: # Fetch Pairlist from Remote URL - pairlist, time_elapsed, info = self.fetch_pairlist() + pairlist, time_elapsed = self.fetch_pairlist() self.log_once(f"Fetched pairs: {pairlist}", logger.debug) pairlist = self._whitelist_for_active_markets(pairlist) pairlist = pairlist[:self._number_pairs] - if self._pair_cache is not None: - self._pair_cache['pairlist'] = pairlist.copy() + self._pair_cache['pairlist'] = pairlist.copy() if time_elapsed != 0.0: - self.log_once(f'{info} Fetched in {time_elapsed} seconds.', logger.info) + self.log_once(f'Pairlist Fetched in {time_elapsed} seconds.', logger.info) else: - self.log_once(f'{info} Fetched Pairlist.', logger.info) + self.log_once('Fetched Pairlist.', logger.info) self._last_pairlist = list(pairlist) diff --git a/tests/plugins/test_remotepairlist.py b/tests/plugins/test_remotepairlist.py index fc91d3f06..b7a484c92 100644 --- a/tests/plugins/test_remotepairlist.py +++ b/tests/plugins/test_remotepairlist.py @@ -107,7 +107,7 @@ def test_fetch_pairlist_timeout_keep_last_pairlist(mocker, rpl_config, caplog): remote_pairlist._last_pairlist = ["BTC/USDT", "ETH/USDT", "LTC/USDT"] - pairs, time_elapsed, info = remote_pairlist.fetch_pairlist() + pairs, time_elapsed = remote_pairlist.fetch_pairlist() assert log_has(f"Was not able to fetch pairlist from: {remote_pairlist._pairlist_url}", caplog) assert log_has("Keeping last fetched pairlist", caplog) assert pairs == ["BTC/USDT", "ETH/USDT", "LTC/USDT"] @@ -163,7 +163,6 @@ def test_fetch_pairlist_mock_response_valid(mocker, rpl_config): mock_response.json.return_value = { "pairs": ["ETH/USDT", "XRP/USDT", "LTC/USDT", "EOS/USDT"], - "info": "Mock pairlist response", "refresh_period": 60 } @@ -179,9 +178,8 @@ def test_fetch_pairlist_mock_response_valid(mocker, rpl_config): pairlistmanager = PairListManager(exchange, rpl_config) remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config, rpl_config['pairlists'][0], 0) - pairs, time_elapsed, info = remote_pairlist.fetch_pairlist() + pairs, time_elapsed = remote_pairlist.fetch_pairlist() assert pairs == ["ETH/USDT", "XRP/USDT", "LTC/USDT", "EOS/USDT"] assert time_elapsed == 0.4 - assert info == "Mock pairlist response" assert remote_pairlist._refresh_period == 60 From ebf60d85da374a24601c8cff3ecd49fc5931fe02 Mon Sep 17 00:00:00 2001 From: Bloodhunter4rc Date: Mon, 19 Dec 2022 16:25:22 +0100 Subject: [PATCH 120/795] self._init_done placed wrong. fixed --- freqtrade/plugins/pairlist/RemotePairList.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index 25530457a..0746f7e6f 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -74,7 +74,8 @@ class RemotePairList(IPairList): self._refresh_period = remote_refresh_period self._pair_cache = TTLCache(maxsize=1, ttl=remote_refresh_period) - self._init_done = True + + self._init_done = True return pairlist From a119fbd895f6a6ef5c33abd128b8d96f0321c520 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 19 Dec 2022 18:19:55 +0100 Subject: [PATCH 121/795] Small error-message finetuning --- freqtrade/plugins/pairlist/RemotePairList.py | 2 +- tests/plugins/test_remotepairlist.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index 0746f7e6f..b54be1fa7 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -122,7 +122,7 @@ class RemotePairList(IPairList): f' {self._pairlist_url}', logger.info) pairlist = self.return_last_pairlist() else: - raise OperationalException('RemotePairList is not of type JSON abort ') + raise OperationalException('RemotePairList is not of type JSON, abort.') except requests.exceptions.RequestException: self.log_once(f'Was not able to fetch pairlist from:' diff --git a/tests/plugins/test_remotepairlist.py b/tests/plugins/test_remotepairlist.py index b7a484c92..ac1d1f5ed 100644 --- a/tests/plugins/test_remotepairlist.py +++ b/tests/plugins/test_remotepairlist.py @@ -81,7 +81,7 @@ def test_fetch_pairlist_mock_response_html(mocker, rpl_config): remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config, rpl_config['pairlists'][0], 0) - with pytest.raises(OperationalException, match='RemotePairList is not of type JSON abort'): + with pytest.raises(OperationalException, match='RemotePairList is not of type JSON, abort.'): remote_pairlist.fetch_pairlist() From 5dbd5c235af3fae7b06a6fd1accac099f9fe007a Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 20 Dec 2022 07:21:52 +0100 Subject: [PATCH 122/795] Add endpoint for freqAI models --- freqtrade/rpc/api_server/api_schemas.py | 5 +++++ freqtrade/rpc/api_server/api_v1.py | 26 ++++++++++++++++++------- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index ada20230a..2100a6fe2 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -372,6 +372,10 @@ class StrategyListResponse(BaseModel): strategies: List[str] +class FreqAIModelListResponse(BaseModel): + freqaimodels: List[str] + + class StrategyResponse(BaseModel): strategy: str code: str @@ -419,6 +423,7 @@ class BacktestRequest(BaseModel): stake_amount: Optional[str] enable_protections: bool dry_run_wallet: Optional[float] + freqaimodel: Optional[str] class BacktestResponse(BaseModel): diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 9e4b140e4..e26df6eea 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -13,12 +13,13 @@ from freqtrade.rpc import RPC from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, BlacklistPayload, BlacklistResponse, Count, Daily, DeleteLockRequest, DeleteTrade, ForceEnterPayload, - ForceEnterResponse, ForceExitPayload, Health, - Locks, Logs, OpenTradeSchema, PairHistory, - PerformanceEntry, Ping, PlotConfig, Profit, - ResultMsg, ShowConfig, Stats, StatusMsg, - StrategyListResponse, StrategyResponse, SysInfo, - Version, WhitelistResponse) + ForceEnterResponse, ForceExitPayload, + FreqAIModelListResponse, Health, Locks, Logs, + OpenTradeSchema, PairHistory, PerformanceEntry, + Ping, PlotConfig, Profit, ResultMsg, ShowConfig, + Stats, StatusMsg, StrategyListResponse, + StrategyResponse, SysInfo, Version, + WhitelistResponse) from freqtrade.rpc.api_server.deps import get_config, get_exchange, get_rpc, get_rpc_optional from freqtrade.rpc.rpc import RPCException @@ -38,7 +39,8 @@ logger = logging.getLogger(__name__) # 2.17: Forceentry - leverage, partial force_exit # 2.20: Add websocket endpoints # 2.21: Add new_candle messagetype -API_VERSION = 2.21 +# 2.22: Add FreqAI to backtesting +API_VERSION = 2.22 # Public API, requires no auth. router_public = APIRouter() @@ -279,6 +281,16 @@ def get_strategy(strategy: str, config=Depends(get_config)): } +@router.get('/freqaimodels', response_model=FreqAIModelListResponse, tags=['freqai']) +def list_freqaimodels(config=Depends(get_config)): + from freqtrade.resolvers.freqaimodel_resolver import FreqaiModelResolver + strategies = FreqaiModelResolver.search_all_objects( + config, False) + strategies = sorted(strategies, key=lambda x: x['name']) + + return {'freqaimodels': [x['name'] for x in strategies]} + + @router.get('/available_pairs', response_model=AvailablePairs, tags=['candle data']) def list_available_pairs(timeframe: Optional[str] = None, stake_currency: Optional[str] = None, candletype: Optional[CandleType] = None, config=Depends(get_config)): From 256fac2a2b56b7c384fd330eb29828b6661f1702 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 20 Dec 2022 07:23:41 +0100 Subject: [PATCH 123/795] Add test for freqaimodels endpoint --- tests/rpc/test_rpc_apiserver.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index ee067f911..74e620def 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1488,6 +1488,33 @@ def test_api_strategy(botclient): assert_response(rc, 500) +def test_api_freqaimodels(botclient, tmpdir): + ftbot, client = botclient + ftbot.config['user_data_dir'] = Path(tmpdir) + + rc = client_get(client, f"{BASE_URI}/freqaimodels") + + assert_response(rc) + + assert rc.json() == {'freqaimodels': [ + 'CatboostClassifier', + 'CatboostClassifierMultiTarget', + 'CatboostRegressor', + 'CatboostRegressorMultiTarget', + 'LightGBMClassifier', + 'LightGBMClassifierMultiTarget', + 'LightGBMRegressor', + 'LightGBMRegressorMultiTarget', + 'ReinforcementLearner', + 'ReinforcementLearner_multiproc', + 'XGBoostClassifier', + 'XGBoostRFClassifier', + 'XGBoostRFRegressor', + 'XGBoostRegressor', + 'XGBoostRegressorMultiTarget' + ]} + + def test_list_available_pairs(botclient): ftbot, client = botclient From 6d9f1fafb7b3105c96d02858adc0d30ef41d62d8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 20 Dec 2022 19:20:39 +0100 Subject: [PATCH 124/795] allow backtest_cache to be provided via backtest API --- freqtrade/rpc/api_server/api_schemas.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 2100a6fe2..6c423c959 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -423,6 +423,7 @@ class BacktestRequest(BaseModel): stake_amount: Optional[str] enable_protections: bool dry_run_wallet: Optional[float] + backtest_cache: Optional[str] freqaimodel: Optional[str] From 07606a9e2380cc3b86ddf7753c5b05f7abeb37a3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 20 Dec 2022 19:32:29 +0100 Subject: [PATCH 125/795] Simplify APi backtest config merging --- freqtrade/rpc/api_server/api_backtest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index b17636a7d..2d3da6d20 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -11,6 +11,7 @@ from freqtrade.configuration.config_validation import validate_config_consistenc from freqtrade.data.btanalysis import get_backtest_resultlist, load_and_merge_backtest_result from freqtrade.enums import BacktestState from freqtrade.exceptions import DependencyException +from freqtrade.misc import deep_merge_dicts from freqtrade.rpc.api_server.api_schemas import (BacktestHistoryEntry, BacktestRequest, BacktestResponse) from freqtrade.rpc.api_server.deps import get_config, is_webserver_mode @@ -38,9 +39,8 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac btconfig = deepcopy(config) settings = dict(bt_settings) # Pydantic models will contain all keys, but non-provided ones are None - for setting in settings.keys(): - if settings[setting] is not None: - btconfig[setting] = settings[setting] + + btconfig = deep_merge_dicts(settings, btconfig, allow_null_overrides=False) try: btconfig['stake_amount'] = float(btconfig['stake_amount']) except ValueError: From 70531224e61641587a22d1816530f79a824139e7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 20 Dec 2022 19:44:01 +0100 Subject: [PATCH 126/795] Allow setting identifier via UI --- freqtrade/rpc/api_server/api_backtest.py | 2 ++ freqtrade/rpc/api_server/api_schemas.py | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index 2d3da6d20..4e43a63b1 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -38,6 +38,8 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac btconfig = deepcopy(config) settings = dict(bt_settings) + if 'freqai' in settings: + settings['freqai'] = dict(settings['freqai']) # Pydantic models will contain all keys, but non-provided ones are None btconfig = deep_merge_dicts(settings, btconfig, allow_null_overrides=False) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 6c423c959..17dff222d 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -414,6 +414,10 @@ class PairHistory(BaseModel): } +class BacktestFreqAIInputs(BaseModel): + identifier: str + + class BacktestRequest(BaseModel): strategy: str timeframe: Optional[str] @@ -425,6 +429,7 @@ class BacktestRequest(BaseModel): dry_run_wallet: Optional[float] backtest_cache: Optional[str] freqaimodel: Optional[str] + freqai: Optional[BacktestFreqAIInputs] class BacktestResponse(BaseModel): From 73792fd6ce81f004dfa0fbe3129f86a6e9e2c697 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 21 Dec 2022 06:28:55 +0100 Subject: [PATCH 127/795] Don't attempt to convert None to dict --- freqtrade/rpc/api_server/api_backtest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index 4e43a63b1..bc2a40d91 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -38,7 +38,7 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac btconfig = deepcopy(config) settings = dict(bt_settings) - if 'freqai' in settings: + if settings.get('freqai', None) is not None: settings['freqai'] = dict(settings['freqai']) # Pydantic models will contain all keys, but non-provided ones are None From 2a7369b56a7f6063835a65c3d7e00b0f12889da4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 23 Dec 2022 07:38:33 +0100 Subject: [PATCH 128/795] fix macos CI --- .github/workflows/ci.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0a787bc47..77432cc9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -147,7 +147,15 @@ jobs: - name: Installation - macOS if: runner.os == 'macOS' run: | - brew update + # homebrew fails to update python 3.9.1 to 3.9.1.1 due to unlinking failure + rm /usr/local/bin/2to3 || true + # homebrew fails to update python from 3.9 to 3.10 due to another unlinking failure + rm /usr/local/bin/idle3 || true + rm /usr/local/bin/pydoc3 || true + rm /usr/local/bin/python3 || true + rm /usr/local/bin/python3-config || true + # Ignore brew update failures - https://github.com/actions/runner-images/issues/6817 + brew update || true brew install hdf5 c-blosc python -m pip install --upgrade pip wheel export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH From ad0d7c9a9ee0e2bdcbff02d487752bb1ffdcb57d Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 23 Dec 2022 16:09:35 +0100 Subject: [PATCH 129/795] Don't allow DCA trades to go beyond max order size closes #7924 --- freqtrade/freqtradebot.py | 1 + freqtrade/optimize/backtesting.py | 1 + freqtrade/wallets.py | 7 ++++++- tests/test_wallets.py | 26 ++++++++++++++------------ 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index f6c4a52bb..258a45008 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -912,6 +912,7 @@ class FreqtradeBot(LoggingMixin): stake_amount=stake_amount, min_stake_amount=min_stake_amount, max_stake_amount=max_stake_amount, + trade_amount=trade.stake_amount if trade else None, ) return enter_limit_requested, stake_amount, leverage diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 82aa2b3e9..2b8b96cba 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -769,6 +769,7 @@ class Backtesting: stake_amount=stake_amount, min_stake_amount=min_stake_amount, max_stake_amount=max_stake_amount, + trade_amount=trade.stake_amount if trade else None ) return propose_rate, stake_amount_val, leverage, min_stake_amount diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py index 0a9ecc638..97db3fba5 100644 --- a/freqtrade/wallets.py +++ b/freqtrade/wallets.py @@ -291,12 +291,17 @@ class Wallets: return self._check_available_stake_amount(stake_amount, available_amount) def validate_stake_amount(self, pair: str, stake_amount: Optional[float], - min_stake_amount: Optional[float], max_stake_amount: float): + min_stake_amount: Optional[float], max_stake_amount: float, + trade_amount: Optional[float]): if not stake_amount: logger.debug(f"Stake amount is {stake_amount}, ignoring possible trade for {pair}.") return 0 max_stake_amount = min(max_stake_amount, self.get_available_stake_amount()) + if trade_amount: + # if in a trade, then the resulting trade size cannot go beyond the max stake + # Otherwise we could no longer exit. + max_stake_amount = min(max_stake_amount, max_stake_amount - trade_amount) if min_stake_amount is not None and min_stake_amount > max_stake_amount: if self._log: diff --git a/tests/test_wallets.py b/tests/test_wallets.py index 73a34bbae..0117f7427 100644 --- a/tests/test_wallets.py +++ b/tests/test_wallets.py @@ -180,17 +180,17 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_r assert result == 0 -@pytest.mark.parametrize('stake_amount,min_stake,stake_available,max_stake,expected', [ - (22, 11, 50, 10000, 22), - (100, 11, 500, 10000, 100), - (1000, 11, 500, 10000, 500), # Above stake_available - (700, 11, 1000, 400, 400), # Above max_stake, below stake available - (20, 15, 10, 10000, 0), # Minimum stake > stake_available - (9, 11, 100, 10000, 11), # Below min stake - (1, 15, 10, 10000, 0), # Below min stake and min_stake > stake_available - (20, 50, 100, 10000, 0), # Below min stake and stake * 1.3 > min_stake - (1000, None, 1000, 10000, 1000), # No min-stake-amount could be determined - +@pytest.mark.parametrize('stake_amount,min_stake,stake_available,max_stake,trade_amount,expected', [ + (22, 11, 50, 10000, None, 22), + (100, 11, 500, 10000, None, 100), + (1000, 11, 500, 10000, None, 500), # Above stake_available + (700, 11, 1000, 400, None, 400), # Above max_stake, below stake available + (20, 15, 10, 10000, None, 0), # Minimum stake > stake_available + (9, 11, 100, 10000, None, 11), # Below min stake + (1, 15, 10, 10000, None, 0), # Below min stake and min_stake > stake_available + (20, 50, 100, 10000, None, 0), # Below min stake and stake * 1.3 > min_stake + (1000, None, 1000, 10000, None, 1000), # No min-stake-amount could be determined + (2000, 15, 2000, 3000, 1500, 500), # Rebuy - resulting in too high stake amount. Adjusting. ]) def test_validate_stake_amount( mocker, @@ -199,13 +199,15 @@ def test_validate_stake_amount( min_stake, stake_available, max_stake, + trade_amount, expected, ): freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch("freqtrade.wallets.Wallets.get_available_stake_amount", return_value=stake_available) - res = freqtrade.wallets.validate_stake_amount('XRP/USDT', stake_amount, min_stake, max_stake) + res = freqtrade.wallets.validate_stake_amount( + 'XRP/USDT', stake_amount, min_stake, max_stake, trade_amount) assert res == expected From 524da3c7ab1e4ab77d6eb0d0a87226f278e5b870 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 23 Dec 2022 16:19:12 +0100 Subject: [PATCH 130/795] Don't actually load models to avoid random failures --- tests/rpc/test_rpc_apiserver.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 74e620def..aea8ea059 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1488,19 +1488,30 @@ def test_api_strategy(botclient): assert_response(rc, 500) -def test_api_freqaimodels(botclient, tmpdir): +def test_api_freqaimodels(botclient, tmpdir, mocker): ftbot, client = botclient ftbot.config['user_data_dir'] = Path(tmpdir) + mocker.patch( + "freqtrade.resolvers.freqaimodel_resolver.FreqaiModelResolver.search_all_objects", + return_value=[ + {'name': 'LightGBMClassifier'}, + {'name': 'LightGBMClassifierMultiTarget'}, + {'name': 'LightGBMRegressor'}, + {'name': 'LightGBMRegressorMultiTarget'}, + {'name': 'ReinforcementLearner'}, + {'name': 'ReinforcementLearner_multiproc'}, + {'name': 'XGBoostClassifier'}, + {'name': 'XGBoostRFClassifier'}, + {'name': 'XGBoostRFRegressor'}, + {'name': 'XGBoostRegressor'}, + {'name': 'XGBoostRegressorMultiTarget'}, + ]) rc = client_get(client, f"{BASE_URI}/freqaimodels") assert_response(rc) assert rc.json() == {'freqaimodels': [ - 'CatboostClassifier', - 'CatboostClassifierMultiTarget', - 'CatboostRegressor', - 'CatboostRegressorMultiTarget', 'LightGBMClassifier', 'LightGBMClassifierMultiTarget', 'LightGBMRegressor', From ce13ce4b10ec8887b4fb21ccddd942aaca3eee1f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 25 Dec 2022 09:05:53 +0100 Subject: [PATCH 131/795] Update binance stoploss order types closes #7927 an update to the most recent ccxt version (>2.4.55) would have the same effect. --- freqtrade/exchange/binance.py | 2 +- tests/exchange/test_binance.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index b21e64eb2..7462e4f81 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -31,7 +31,7 @@ class Binance(Exchange): "ccxt_futures_name": "future" } _ft_has_futures: Dict = { - "stoploss_order_types": {"limit": "limit", "market": "market"}, + "stoploss_order_types": {"limit": "stop", "market": "stop_market"}, "tickers_have_price": False, } diff --git a/tests/exchange/test_binance.py b/tests/exchange/test_binance.py index 1fc8b4153..306a30985 100644 --- a/tests/exchange/test_binance.py +++ b/tests/exchange/test_binance.py @@ -23,7 +23,7 @@ from tests.exchange.test_exchange import ccxt_exceptionhandlers def test_stoploss_order_binance(default_conf, mocker, limitratio, expected, side, trademode): api_mock = MagicMock() order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) - order_type = 'stop_loss_limit' if trademode == TradingMode.SPOT else 'limit' + order_type = 'stop_loss_limit' if trademode == TradingMode.SPOT else 'stop' api_mock.create_order = MagicMock(return_value={ 'id': order_id, From 7a5439321c9f45fbd8103538c4049074d1dae495 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 25 Dec 2022 21:29:37 +0100 Subject: [PATCH 132/795] Show new metrics in backtesting --- freqtrade/data/metrics.py | 12 ++++++------ freqtrade/optimize/optimize_reports.py | 12 ++++++++++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/freqtrade/data/metrics.py b/freqtrade/data/metrics.py index eccb8a04d..00168bbfa 100644 --- a/freqtrade/data/metrics.py +++ b/freqtrade/data/metrics.py @@ -287,8 +287,8 @@ def calculate_sharpe(trades: pd.DataFrame, return sharp_ratio -def calculate_calmar(trades: pd.DataFrame, - min_date: datetime, max_date: datetime) -> float: +def calculate_calmar(trades: pd.DataFrame, min_date: datetime, max_date: datetime, + starting_balance: float) -> float: """ Calculate calmar :param trades: DataFrame containing trades (requires columns close_date and profit_ratio) @@ -297,17 +297,17 @@ def calculate_calmar(trades: pd.DataFrame, if (len(trades) == 0) or (min_date is None) or (max_date is None) or (min_date == max_date): return 0 - total_profit = trades["profit_ratio"] - days_period = (max_date - min_date).days + total_profit = trades['profit_abs'].sum() / starting_balance + days_period = max(1, (max_date - min_date).days) # adding slippage of 0.1% per trade # total_profit = total_profit - 0.0005 - expected_returns_mean = total_profit.sum() / days_period * 100 + expected_returns_mean = total_profit / days_period * 100 # calculate max drawdown try: _, _, _, _, _, max_drawdown = calculate_max_drawdown( - trades, value_col="profit_abs" + trades, value_col="profit_abs", starting_balance=starting_balance ) except ValueError: max_drawdown = 0 diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index 8ad37e7d8..eb635cde6 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -9,8 +9,9 @@ from tabulate import tabulate from freqtrade.constants import (DATETIME_PRINT_FORMAT, LAST_BT_RESULT_FN, UNLIMITED_STAKE_AMOUNT, Config) -from freqtrade.data.metrics import (calculate_cagr, calculate_csum, calculate_market_change, - calculate_max_drawdown) +from freqtrade.data.metrics import (calculate_cagr, calculate_calmar, calculate_csum, + calculate_expectancy, calculate_market_change, + calculate_max_drawdown, calculate_sharpe, calculate_sortino) from freqtrade.misc import decimals_per_coin, file_dump_joblib, file_dump_json, round_coin_value from freqtrade.optimize.backtest_caching import get_backtest_metadata_filename @@ -448,6 +449,10 @@ def generate_strategy_stats(pairlist: List[str], 'profit_total_long_abs': results.loc[~results['is_short'], 'profit_abs'].sum(), 'profit_total_short_abs': results.loc[results['is_short'], 'profit_abs'].sum(), 'cagr': calculate_cagr(backtest_days, start_balance, content['final_balance']), + 'expectancy': calculate_expectancy(results), + 'sortino': calculate_sortino(results, min_date, max_date), + 'sharpe': calculate_sharpe(results, min_date, max_date), + 'calmar': calculate_calmar(results, min_date, max_date, start_balance), 'profit_factor': profit_factor, 'backtest_start': min_date.strftime(DATETIME_PRINT_FORMAT), 'backtest_start_ts': int(min_date.timestamp() * 1000), @@ -785,6 +790,9 @@ def text_table_add_metrics(strat_results: Dict) -> str: strat_results['stake_currency'])), ('Total profit %', f"{strat_results['profit_total']:.2%}"), ('CAGR %', f"{strat_results['cagr']:.2%}" if 'cagr' in strat_results else 'N/A'), + ('Sortino', f"{strat_results['sortino']:.2f}" if 'sortino' in strat_results else 'N/A'), + ('Sharpe', f"{strat_results['sharpe']:.2f}" if 'sharpe' in strat_results else 'N/A'), + ('Calmar', f"{strat_results['calmar']:.2f}" if 'calmar' in strat_results else 'N/A'), ('Profit factor', f'{strat_results["profit_factor"]:.2f}' if 'profit_factor' in strat_results else 'N/A'), ('Trades per day', strat_results['trades_per_day']), From 6353f3ac1aff1a93d54def083bfa392d7a0f01be Mon Sep 17 00:00:00 2001 From: Stefano Ariestasia Date: Mon, 26 Dec 2022 08:19:51 +0900 Subject: [PATCH 133/795] fix formulas and implement new metrics --- freqtrade/data/metrics.py | 28 +++++++++----------------- freqtrade/optimize/optimize_reports.py | 6 ++++-- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/freqtrade/data/metrics.py b/freqtrade/data/metrics.py index 00168bbfa..8401e31bb 100644 --- a/freqtrade/data/metrics.py +++ b/freqtrade/data/metrics.py @@ -222,8 +222,8 @@ def calculate_expectancy(trades: pd.DataFrame) -> float: return expectancy -def calculate_sortino(trades: pd.DataFrame, - min_date: datetime, max_date: datetime) -> float: +def calculate_sortino(trades: pd.DataFrame, min_date: datetime, max_date: datetime, + starting_balance: float) -> float: """ Calculate sortino :param trades: DataFrame containing trades (requires columns profit_ratio) @@ -232,18 +232,13 @@ def calculate_sortino(trades: pd.DataFrame, if (len(trades) == 0) or (min_date is None) or (max_date is None) or (min_date == max_date): return 0 - total_profit = trades["profit_ratio"] - days_period = (max_date - min_date).days + total_profit = trades['profit_abs'] / starting_balance + days_period = max(1, (max_date - min_date).days) - if days_period == 0: - return 0 - - # adding slippage of 0.1% per trade - # total_profit = total_profit - 0.0005 expected_returns_mean = total_profit.sum() / days_period trades['downside_returns'] = 0 - trades.loc[total_profit < 0, 'downside_returns'] = trades['profit_ratio'] + trades.loc[total_profit < 0, 'downside_returns'] = (trades['profit_abs'] / starting_balance) down_stdev = np.std(trades['downside_returns']) if down_stdev != 0: @@ -256,8 +251,8 @@ def calculate_sortino(trades: pd.DataFrame, return sortino_ratio -def calculate_sharpe(trades: pd.DataFrame, - min_date: datetime, max_date: datetime) -> float: +def calculate_sharpe(trades: pd.DataFrame, min_date: datetime, max_date: datetime, + starting_balance: float) -> float: """ Calculate sharpe :param trades: DataFrame containing trades (requires columns close_date and profit_ratio) @@ -266,14 +261,9 @@ def calculate_sharpe(trades: pd.DataFrame, if (len(trades) == 0) or (min_date is None) or (max_date is None) or (min_date == max_date): return 0 - total_profit = trades["profit_ratio"] - days_period = (max_date - min_date).days + total_profit = trades['profit_abs'] / starting_balance + days_period = max(1, (max_date - min_date).days) - if days_period == 0: - return 0 - - # adding slippage of 0.1% per trade - # total_profit = total_profit - 0.0005 expected_returns_mean = total_profit.sum() / days_period up_stdev = np.std(total_profit) diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index eb635cde6..7de8f1a47 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -450,8 +450,8 @@ def generate_strategy_stats(pairlist: List[str], 'profit_total_short_abs': results.loc[results['is_short'], 'profit_abs'].sum(), 'cagr': calculate_cagr(backtest_days, start_balance, content['final_balance']), 'expectancy': calculate_expectancy(results), - 'sortino': calculate_sortino(results, min_date, max_date), - 'sharpe': calculate_sharpe(results, min_date, max_date), + 'sortino': calculate_sortino(results, min_date, max_date, start_balance), + 'sharpe': calculate_sharpe(results, min_date, max_date, start_balance), 'calmar': calculate_calmar(results, min_date, max_date, start_balance), 'profit_factor': profit_factor, 'backtest_start': min_date.strftime(DATETIME_PRINT_FORMAT), @@ -795,6 +795,8 @@ def text_table_add_metrics(strat_results: Dict) -> str: ('Calmar', f"{strat_results['calmar']:.2f}" if 'calmar' in strat_results else 'N/A'), ('Profit factor', f'{strat_results["profit_factor"]:.2f}' if 'profit_factor' in strat_results else 'N/A'), + ('Expectancy', f"{strat_results['expectancy']:.2f}" if 'expectancy' + in strat_results else 'N/A'), ('Trades per day', strat_results['trades_per_day']), ('Avg. daily profit %', f"{(strat_results['profit_total'] / strat_results['backtest_days']):.2%}"), From b1bf6d8dc96390a18dd81ecf63c6f1ee924d29e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Dec 2022 03:00:41 +0000 Subject: [PATCH 134/795] Bump nbconvert from 7.2.6 to 7.2.7 Bumps [nbconvert](https://github.com/jupyter/nbconvert) from 7.2.6 to 7.2.7. - [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.2.6...v7.2.7) --- updated-dependencies: - dependency-name: nbconvert 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 336351019..3e6226f71 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -23,7 +23,7 @@ time-machine==2.8.2 httpx==0.23.1 # Convert jupyter notebooks to markdown documents -nbconvert==7.2.6 +nbconvert==7.2.7 # mypy types types-cachetools==5.2.1 From e0f60e175f396285d9a9534df09a8ba893dcfbd9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Dec 2022 03:01:00 +0000 Subject: [PATCH 135/795] Bump pre-commit from 2.20.0 to 2.21.0 Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 2.20.0 to 2.21.0. - [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/v2.20.0...v2.21.0) --- 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 336351019..6ed13397a 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -10,7 +10,7 @@ coveralls==3.3.1 flake8==6.0.0 flake8-tidy-imports==4.8.0 mypy==0.991 -pre-commit==2.20.0 +pre-commit==2.21.0 pytest==7.2.0 pytest-asyncio==0.20.3 pytest-cov==4.0.0 From 3993bd7c1c4b52cde41275585d36b4a434d66202 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Dec 2022 03:01:11 +0000 Subject: [PATCH 136/795] Bump types-requests from 2.28.11.5 to 2.28.11.7 Bumps [types-requests](https://github.com/python/typeshed) from 2.28.11.5 to 2.28.11.7. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-requests 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 336351019..842b1ae76 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -28,6 +28,6 @@ nbconvert==7.2.6 # mypy types types-cachetools==5.2.1 types-filelock==3.2.7 -types-requests==2.28.11.5 +types-requests==2.28.11.7 types-tabulate==0.9.0.0 types-python-dateutil==2.8.19.5 From 9ea8792d3cba24487f2927e1b86808fca56510f7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 26 Dec 2022 08:45:02 +0100 Subject: [PATCH 137/795] Attempt brew fix --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 77432cc9e..ef85d1bdd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -149,6 +149,7 @@ jobs: run: | # homebrew fails to update python 3.9.1 to 3.9.1.1 due to unlinking failure rm /usr/local/bin/2to3 || true + rm /usr/local/bin/2to3-3.11 || true # homebrew fails to update python from 3.9 to 3.10 due to another unlinking failure rm /usr/local/bin/idle3 || true rm /usr/local/bin/pydoc3 || true From 18709406c5bac070950df2bd076e7f216a8d1158 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 26 Dec 2022 08:50:55 +0100 Subject: [PATCH 138/795] use link overwrite --- .github/workflows/ci.yml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef85d1bdd..3fa06951d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -147,16 +147,10 @@ jobs: - name: Installation - macOS if: runner.os == 'macOS' run: | - # homebrew fails to update python 3.9.1 to 3.9.1.1 due to unlinking failure - rm /usr/local/bin/2to3 || true - rm /usr/local/bin/2to3-3.11 || true - # homebrew fails to update python from 3.9 to 3.10 due to another unlinking failure - rm /usr/local/bin/idle3 || true - rm /usr/local/bin/pydoc3 || true - rm /usr/local/bin/python3 || true - rm /usr/local/bin/python3-config || true + brew update + # homebrew fails to update python due to unlinking failures # Ignore brew update failures - https://github.com/actions/runner-images/issues/6817 - brew update || true + brew link --overwrite python@3.10 python@3.11 brew install hdf5 c-blosc python -m pip install --upgrade pip wheel export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH From 9a556d2639e89cc34e21a4f392edcda6cf4d962b Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 26 Dec 2022 08:57:01 +0100 Subject: [PATCH 139/795] Remove all mac conflicts --- .github/workflows/ci.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3fa06951d..608565fdc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -149,8 +149,18 @@ jobs: run: | brew update # homebrew fails to update python due to unlinking failures - # Ignore brew update failures - https://github.com/actions/runner-images/issues/6817 - brew link --overwrite python@3.10 python@3.11 + # https://github.com/actions/runner-images/issues/6817 + rm /usr/local/bin/2to3 || true + rm /usr/local/bin/2to3-3.11 || true + rm /usr/local/bin/idle3 || true + rm /usr/local/bin/idle3.11 || true + rm /usr/local/bin/pydoc3 || true + rm /usr/local/bin/pydoc3.11 || true + rm /usr/local/bin/python3 || true + rm /usr/local/bin/python3.11 || true + rm /usr/local/bin/python3-config || true + rm /usr/local/bin/python3.11-config || true + brew install hdf5 c-blosc python -m pip install --upgrade pip wheel export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH From 00112d81d22bbac6057f20062c32617ef7d581a6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 26 Dec 2022 09:21:18 +0100 Subject: [PATCH 140/795] Bump types-requests pre-commit --- .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 a7e60ce90..306e4bbda 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: additional_dependencies: - types-cachetools==5.2.1 - types-filelock==3.2.7 - - types-requests==2.28.11.5 + - types-requests==2.28.11.7 - types-tabulate==0.9.0.0 - types-python-dateutil==2.8.19.5 # stages: [push] From c5b246af8001051903a42f8da5548d3ea128c8d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Dec 2022 08:24:40 +0000 Subject: [PATCH 141/795] Bump isort from 5.11.3 to 5.11.4 Bumps [isort](https://github.com/pycqa/isort) from 5.11.3 to 5.11.4. - [Release notes](https://github.com/pycqa/isort/releases) - [Changelog](https://github.com/PyCQA/isort/blob/main/CHANGELOG.md) - [Commits](https://github.com/pycqa/isort/compare/5.11.3...5.11.4) --- updated-dependencies: - dependency-name: isort 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 3e6226f71..a3aaeee1e 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -16,7 +16,7 @@ pytest-asyncio==0.20.3 pytest-cov==4.0.0 pytest-mock==3.10.0 pytest-random-order==1.1.0 -isort==5.11.3 +isort==5.11.4 # For datetime mocking time-machine==2.8.2 # fastapi testing From d60b38dad2badc94d30038ac5e03f24fb8a2c238 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Dec 2022 09:04:53 +0000 Subject: [PATCH 142/795] Bump tables from 3.7.0 to 3.8.0 Bumps [tables](https://github.com/PyTables/PyTables) from 3.7.0 to 3.8.0. - [Release notes](https://github.com/PyTables/PyTables/releases) - [Changelog](https://github.com/PyTables/PyTables/blob/master/RELEASE_NOTES.rst) - [Commits](https://github.com/PyTables/PyTables/compare/v3.7.0...v3.8.0) --- updated-dependencies: - dependency-name: tables 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 fa689de14..4e2ea6caf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,7 +19,7 @@ technical==1.3.0 tabulate==0.9.0 pycoingecko==3.1.0 jinja2==3.1.2 -tables==3.7.0 +tables==3.8.0 blosc==1.11.1 joblib==1.2.0 pyarrow==10.0.1; platform_machine != 'armv7l' From aaeeb86622d7fe98a3c461600bc5d90c8757bdae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Dec 2022 12:40:26 +0000 Subject: [PATCH 143/795] Bump ccxt from 2.4.27 to 2.4.60 Bumps [ccxt](https://github.com/ccxt/ccxt) from 2.4.27 to 2.4.60. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/2.4.27...2.4.60) --- 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 fa689de14..6e0edcb3b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ numpy==1.23.5 pandas==1.5.2 pandas-ta==0.3.14b -ccxt==2.4.27 +ccxt==2.4.60 # Pin cryptography for now due to rust build errors with piwheels cryptography==38.0.1; platform_machine == 'armv7l' cryptography==38.0.4; platform_machine != 'armv7l' From 63f114395ad71ffc9499697057f7a04a1af90ef9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 26 Dec 2022 14:02:47 +0100 Subject: [PATCH 144/795] is_short should be a boolean --- freqtrade/data/btanalysis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index 9bc543a9d..6350aca55 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -279,7 +279,7 @@ def load_backtest_data(filename: Union[Path, str], strategy: Optional[str] = Non ) # Compatibility support for pre short Columns if 'is_short' not in df.columns: - df['is_short'] = 0 + df['is_short'] = False if 'leverage' not in df.columns: df['leverage'] = 1.0 if 'enter_tag' not in df.columns: From 1cef40a1342cfd798992eee87e45989489b2079d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Dec 2022 14:31:47 +0000 Subject: [PATCH 145/795] Bump numpy from 1.23.5 to 1.24.1 Bumps [numpy](https://github.com/numpy/numpy) from 1.23.5 to 1.24.1. - [Release notes](https://github.com/numpy/numpy/releases) - [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst) - [Commits](https://github.com/numpy/numpy/compare/v1.23.5...v1.24.1) --- updated-dependencies: - dependency-name: numpy 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 6e0edcb3b..90bc4f702 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -numpy==1.23.5 +numpy==1.24.1 pandas==1.5.2 pandas-ta==0.3.14b From 6a15a9b41216a870f8655a53bd78978d81a568a5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 26 Dec 2022 14:25:45 +0100 Subject: [PATCH 146/795] Update backtest-result_new fixing the calculation of profit_abs - which was incorrect previously. --- tests/data/test_btanalysis.py | 15 ++++++++------- tests/test_plotting.py | 2 +- .../backtest_results/backtest-result_new.json | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/data/test_btanalysis.py b/tests/data/test_btanalysis.py index ec7b457ea..95de6b53e 100644 --- a/tests/data/test_btanalysis.py +++ b/tests/data/test_btanalysis.py @@ -268,7 +268,7 @@ def test_create_cum_profit(testdatadir): "cum_profits", timeframe="5m") assert "cum_profits" in cum_profits.columns assert cum_profits.iloc[0]['cum_profits'] == 0 - assert pytest.approx(cum_profits.iloc[-1]['cum_profits']) == 8.723007518796964e-06 + assert pytest.approx(cum_profits.iloc[-1]['cum_profits']) == 9.0225563e-05 def test_create_cum_profit1(testdatadir): @@ -286,7 +286,7 @@ def test_create_cum_profit1(testdatadir): "cum_profits", timeframe="5m") assert "cum_profits" in cum_profits.columns assert cum_profits.iloc[0]['cum_profits'] == 0 - assert pytest.approx(cum_profits.iloc[-1]['cum_profits']) == 8.723007518796964e-06 + assert pytest.approx(cum_profits.iloc[-1]['cum_profits']) == 9.0225563e-05 with pytest.raises(ValueError, match='Trade dataframe empty.'): create_cum_profit(df.set_index('date'), bt_data[bt_data["pair"] == 'NOTAPAIR'], @@ -299,13 +299,13 @@ def test_calculate_max_drawdown(testdatadir): _, hdate, lowdate, hval, lval, drawdown = calculate_max_drawdown( bt_data, value_col="profit_abs") assert isinstance(drawdown, float) - assert pytest.approx(drawdown) == 0.12071099 + assert pytest.approx(drawdown) == 0.29753914 assert isinstance(hdate, Timestamp) assert isinstance(lowdate, Timestamp) assert isinstance(hval, float) assert isinstance(lval, float) - assert hdate == Timestamp('2018-01-25 01:30:00', tz='UTC') - assert lowdate == Timestamp('2018-01-25 03:50:00', tz='UTC') + assert hdate == Timestamp('2018-01-16 19:30:00', tz='UTC') + assert lowdate == Timestamp('2018-01-16 22:25:00', tz='UTC') underwater = calculate_underwater(bt_data) assert isinstance(underwater, DataFrame) @@ -324,8 +324,9 @@ def test_calculate_csum(testdatadir): assert isinstance(csum_min, float) assert isinstance(csum_max, float) - assert csum_min < 0.01 - assert csum_max > 0.02 + assert csum_min < csum_max + assert csum_min < 0.0001 + assert csum_max > 0.0002 csum_min1, csum_max1 = calculate_csum(bt_data, 5) assert csum_min1 == csum_min + 5 diff --git a/tests/test_plotting.py b/tests/test_plotting.py index f13bdee13..64089c4c6 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -354,7 +354,7 @@ def test_generate_profit_graph(testdatadir): profit = find_trace_in_fig_data(figure.data, "Profit") assert isinstance(profit, go.Scatter) - drawdown = find_trace_in_fig_data(figure.data, "Max drawdown 35.69%") + drawdown = find_trace_in_fig_data(figure.data, "Max drawdown 73.89%") assert isinstance(drawdown, go.Scatter) parallel = find_trace_in_fig_data(figure.data, "Parallel trades") assert isinstance(parallel, go.Scatter) diff --git a/tests/testdata/backtest_results/backtest-result_new.json b/tests/testdata/backtest_results/backtest-result_new.json index 03fdb455a..f16f95c33 100644 --- a/tests/testdata/backtest_results/backtest-result_new.json +++ b/tests/testdata/backtest_results/backtest-result_new.json @@ -1 +1 @@ -{"strategy":{"StrategyTestV3":{"trades":[{"pair":"TRX/BTC","stake_amount":0.001,"amount":10.37344398340249,"open_date":"2018-01-10 07:15:00+00:00","close_date":"2018-01-10 07:20:00+00:00","open_rate":9.64e-05,"close_rate":0.00010074887218045112,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":4.348872180451118e-06,"exit_reason":"roi","initial_stop_loss_abs":8.676e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.676e-05,"stop_loss_ratio":0.1,"min_rate":9.64e-05,"max_rate":0.00010074887218045112,"is_open":false,"buy_tag":null,"open_timestamp":1515568500000.0,"close_timestamp":1515568800000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":21.026072329688816,"open_date":"2018-01-10 07:15:00+00:00","close_date":"2018-01-10 07:30:00+00:00","open_rate":4.756e-05,"close_rate":4.9705563909774425e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":15,"profit_ratio":0.03990025,"profit_abs":2.1455639097744267e-06,"exit_reason":"roi","initial_stop_loss_abs":4.2804e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.2804e-05,"stop_loss_ratio":0.1,"min_rate":4.756e-05,"max_rate":4.9705563909774425e-05,"is_open":false,"buy_tag":"buy_tag","open_timestamp":1515568500000.0,"close_timestamp":1515569400000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":29.94908655286014,"open_date":"2018-01-10 07:25:00+00:00","close_date":"2018-01-10 07:35:00+00:00","open_rate":3.339e-05,"close_rate":3.489631578947368e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":10,"profit_ratio":0.03990025,"profit_abs":1.506315789473681e-06,"exit_reason":"roi","initial_stop_loss_abs":3.0050999999999997e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.0050999999999997e-05,"stop_loss_ratio":0.1,"min_rate":3.339e-05,"max_rate":3.489631578947368e-05,"is_open":false,"buy_tag":null,"open_timestamp":1515569100000.0,"close_timestamp":1515569700000.0},{"pair":"TRX/BTC","stake_amount":0.001,"amount":10.313531353135314,"open_date":"2018-01-10 07:25:00+00:00","close_date":"2018-01-10 07:40:00+00:00","open_rate":9.696e-05,"close_rate":0.00010133413533834584,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":15,"profit_ratio":0.03990025,"profit_abs":4.3741353383458455e-06,"exit_reason":"roi","initial_stop_loss_abs":8.7264e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.7264e-05,"stop_loss_ratio":0.1,"min_rate":9.696e-05,"max_rate":0.00010133413533834584,"is_open":false,"buy_tag":null,"open_timestamp":1515569100000.0,"close_timestamp":1515570000000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010604453870625663,"open_date":"2018-01-10 07:35:00+00:00","close_date":"2018-01-10 08:35:00+00:00","open_rate":0.0943,"close_rate":0.09477268170426063,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":-0.0,"profit_abs":0.0004726817042606385,"exit_reason":"roi","initial_stop_loss_abs":0.08487,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.08487,"stop_loss_ratio":0.1,"min_rate":0.0943,"max_rate":0.09477268170426063,"is_open":false,"buy_tag":null,"open_timestamp":1515569700000.0,"close_timestamp":1515573300000.0},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03677001860930642,"open_date":"2018-01-10 07:40:00+00:00","close_date":"2018-01-10 08:10:00+00:00","open_rate":0.02719607,"close_rate":0.02760503345864661,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":0.00040896345864661204,"exit_reason":"roi","initial_stop_loss_abs":0.024476463,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.024476463,"stop_loss_ratio":0.1,"min_rate":0.02719607,"max_rate":0.02760503345864661,"is_open":false,"buy_tag":null,"open_timestamp":1515570000000.0,"close_timestamp":1515571800000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.021575196463739,"open_date":"2018-01-10 08:15:00+00:00","close_date":"2018-01-10 09:55:00+00:00","open_rate":0.04634952,"close_rate":0.046581848421052625,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":100,"profit_ratio":0.0,"profit_abs":0.0002323284210526272,"exit_reason":"roi","initial_stop_loss_abs":0.041714568,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.041714568,"stop_loss_ratio":0.1,"min_rate":0.04634952,"max_rate":0.046581848421052625,"is_open":false,"buy_tag":null,"open_timestamp":1515572100000.0,"close_timestamp":1515578100000.0},{"pair":"NXT/BTC","stake_amount":0.001,"amount":32.615786040443574,"open_date":"2018-01-10 14:45:00+00:00","close_date":"2018-01-10 15:50:00+00:00","open_rate":3.066e-05,"close_rate":3.081368421052631e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":65,"profit_ratio":-0.0,"profit_abs":1.5368421052630647e-07,"exit_reason":"roi","initial_stop_loss_abs":2.7594e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.7594e-05,"stop_loss_ratio":0.1,"min_rate":3.066e-05,"max_rate":3.081368421052631e-05,"is_open":false,"buy_tag":null,"open_timestamp":1515595500000.0,"close_timestamp":1515599400000.0},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.05917194776300452,"open_date":"2018-01-10 16:35:00+00:00","close_date":"2018-01-10 17:15:00+00:00","open_rate":0.0168999,"close_rate":0.016984611278195488,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":8.471127819548868e-05,"exit_reason":"roi","initial_stop_loss_abs":0.01520991,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.01520991,"stop_loss_ratio":0.1,"min_rate":0.0168999,"max_rate":0.016984611278195488,"is_open":false,"buy_tag":null,"open_timestamp":1515602100000.0,"close_timestamp":1515604500000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010949822656672253,"open_date":"2018-01-10 16:40:00+00:00","close_date":"2018-01-10 17:20:00+00:00","open_rate":0.09132568,"close_rate":0.0917834528320802,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.0004577728320801916,"exit_reason":"roi","initial_stop_loss_abs":0.08219311200000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.08219311200000001,"stop_loss_ratio":0.1,"min_rate":0.09132568,"max_rate":0.0917834528320802,"is_open":false,"buy_tag":null,"open_timestamp":1515602400000.0,"close_timestamp":1515604800000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011238476768326556,"open_date":"2018-01-10 18:50:00+00:00","close_date":"2018-01-10 19:45:00+00:00","open_rate":0.08898003,"close_rate":0.08942604518796991,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":0.00044601518796991146,"exit_reason":"roi","initial_stop_loss_abs":0.080082027,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.080082027,"stop_loss_ratio":0.1,"min_rate":0.08898003,"max_rate":0.08942604518796991,"is_open":false,"buy_tag":null,"open_timestamp":1515610200000.0,"close_timestamp":1515613500000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011682232072680307,"open_date":"2018-01-10 22:15:00+00:00","close_date":"2018-01-10 23:00:00+00:00","open_rate":0.08560008,"close_rate":0.08602915308270676,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":0.0,"profit_abs":0.00042907308270676014,"exit_reason":"roi","initial_stop_loss_abs":0.077040072,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.077040072,"stop_loss_ratio":0.1,"min_rate":0.08560008,"max_rate":0.08602915308270676,"is_open":false,"buy_tag":null,"open_timestamp":1515622500000.0,"close_timestamp":1515625200000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.4014726015023105,"open_date":"2018-01-10 22:50:00+00:00","close_date":"2018-01-10 23:20:00+00:00","open_rate":0.00249083,"close_rate":0.0025282860902255634,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":3.745609022556351e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002241747,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002241747,"stop_loss_ratio":0.1,"min_rate":0.00249083,"max_rate":0.0025282860902255634,"is_open":false,"buy_tag":null,"open_timestamp":1515624600000.0,"close_timestamp":1515626400000.0},{"pair":"NXT/BTC","stake_amount":0.001,"amount":33.090668431502316,"open_date":"2018-01-10 23:15:00+00:00","close_date":"2018-01-11 00:15:00+00:00","open_rate":3.022e-05,"close_rate":3.037147869674185e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":-0.0,"profit_abs":1.5147869674185174e-07,"exit_reason":"roi","initial_stop_loss_abs":2.7198e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.7198e-05,"stop_loss_ratio":0.1,"min_rate":3.022e-05,"max_rate":3.037147869674185e-05,"is_open":false,"buy_tag":null,"open_timestamp":1515626100000.0,"close_timestamp":1515629700000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.41034058268362744,"open_date":"2018-01-10 23:40:00+00:00","close_date":"2018-01-11 00:05:00+00:00","open_rate":0.002437,"close_rate":0.0024980776942355883,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":6.107769423558838e-05,"exit_reason":"roi","initial_stop_loss_abs":0.0021933,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0021933,"stop_loss_ratio":0.1,"min_rate":0.002437,"max_rate":0.0024980776942355883,"is_open":false,"buy_tag":null,"open_timestamp":1515627600000.0,"close_timestamp":1515629100000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02095643931654345,"open_date":"2018-01-11 00:00:00+00:00","close_date":"2018-01-11 00:35:00+00:00","open_rate":0.04771803,"close_rate":0.04843559436090225,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":0.0007175643609022495,"exit_reason":"roi","initial_stop_loss_abs":0.042946227000000003,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.042946227000000003,"stop_loss_ratio":0.1,"min_rate":0.04771803,"max_rate":0.04843559436090225,"is_open":false,"buy_tag":null,"open_timestamp":1515628800000.0,"close_timestamp":1515630900000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":27.389756231169542,"open_date":"2018-01-11 03:40:00+00:00","close_date":"2018-01-11 04:25:00+00:00","open_rate":3.651e-05,"close_rate":3.2859000000000005e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.10448878,"profit_abs":-3.650999999999996e-06,"exit_reason":"stop_loss","initial_stop_loss_abs":3.2859000000000005e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.2859000000000005e-05,"stop_loss_ratio":0.1,"min_rate":3.2859000000000005e-05,"max_rate":3.651e-05,"is_open":false,"buy_tag":null,"open_timestamp":1515642000000.0,"close_timestamp":1515644700000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011332594070446804,"open_date":"2018-01-11 03:55:00+00:00","close_date":"2018-01-11 04:25:00+00:00","open_rate":0.08824105,"close_rate":0.08956798308270676,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":0.0013269330827067605,"exit_reason":"roi","initial_stop_loss_abs":0.079416945,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.079416945,"stop_loss_ratio":0.1,"min_rate":0.08824105,"max_rate":0.08956798308270676,"is_open":false,"buy_tag":null,"open_timestamp":1515642900000.0,"close_timestamp":1515644700000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.411522633744856,"open_date":"2018-01-11 04:00:00+00:00","close_date":"2018-01-11 04:50:00+00:00","open_rate":0.00243,"close_rate":0.002442180451127819,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":-0.0,"profit_abs":1.2180451127819219e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002187,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002187,"stop_loss_ratio":0.1,"min_rate":0.00243,"max_rate":0.002442180451127819,"is_open":false,"buy_tag":null,"open_timestamp":1515643200000.0,"close_timestamp":1515646200000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.022001890402423376,"open_date":"2018-01-11 04:30:00+00:00","close_date":"2018-01-11 04:55:00+00:00","open_rate":0.04545064,"close_rate":0.046589753784461146,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":0.001139113784461146,"exit_reason":"roi","initial_stop_loss_abs":0.040905576,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.040905576,"stop_loss_ratio":0.1,"min_rate":0.04545064,"max_rate":0.046589753784461146,"is_open":false,"buy_tag":null,"open_timestamp":1515645000000.0,"close_timestamp":1515646500000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":29.655990510083036,"open_date":"2018-01-11 04:30:00+00:00","close_date":"2018-01-11 04:50:00+00:00","open_rate":3.372e-05,"close_rate":3.456511278195488e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":8.4511278195488e-07,"exit_reason":"roi","initial_stop_loss_abs":3.0348e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.0348e-05,"stop_loss_ratio":0.1,"min_rate":3.372e-05,"max_rate":3.456511278195488e-05,"is_open":false,"buy_tag":null,"open_timestamp":1515645000000.0,"close_timestamp":1515646200000.0},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.037821482602118005,"open_date":"2018-01-11 04:55:00+00:00","close_date":"2018-01-11 05:15:00+00:00","open_rate":0.02644,"close_rate":0.02710265664160401,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":0.0006626566416040071,"exit_reason":"roi","initial_stop_loss_abs":0.023796,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.023796,"stop_loss_ratio":0.1,"min_rate":0.02644,"max_rate":0.02710265664160401,"is_open":false,"buy_tag":null,"open_timestamp":1515646500000.0,"close_timestamp":1515647700000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011348161597821153,"open_date":"2018-01-11 11:20:00+00:00","close_date":"2018-01-11 12:00:00+00:00","open_rate":0.08812,"close_rate":0.08856170426065162,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.0004417042606516125,"exit_reason":"roi","initial_stop_loss_abs":0.079308,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.079308,"stop_loss_ratio":0.1,"min_rate":0.08812,"max_rate":0.08856170426065162,"is_open":false,"buy_tag":null,"open_timestamp":1515669600000.0,"close_timestamp":1515672000000.0},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.037263696923919086,"open_date":"2018-01-11 11:35:00+00:00","close_date":"2018-01-11 12:15:00+00:00","open_rate":0.02683577,"close_rate":0.026970285137844607,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.00013451513784460897,"exit_reason":"roi","initial_stop_loss_abs":0.024152193,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.024152193,"stop_loss_ratio":0.1,"min_rate":0.02683577,"max_rate":0.026970285137844607,"is_open":false,"buy_tag":null,"open_timestamp":1515670500000.0,"close_timestamp":1515672900000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":20.329335230737954,"open_date":"2018-01-11 14:00:00+00:00","close_date":"2018-01-11 14:25:00+00:00","open_rate":4.919e-05,"close_rate":5.04228320802005e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":1.232832080200495e-06,"exit_reason":"roi","initial_stop_loss_abs":4.4271000000000004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.4271000000000004e-05,"stop_loss_ratio":0.1,"min_rate":4.919e-05,"max_rate":5.04228320802005e-05,"is_open":false,"buy_tag":null,"open_timestamp":1515679200000.0,"close_timestamp":1515680700000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.01138317402960718,"open_date":"2018-01-11 19:25:00+00:00","close_date":"2018-01-11 20:35:00+00:00","open_rate":0.08784896,"close_rate":0.08828930566416039,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":70,"profit_ratio":-0.0,"profit_abs":0.0004403456641603881,"exit_reason":"roi","initial_stop_loss_abs":0.079064064,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.079064064,"stop_loss_ratio":0.1,"min_rate":0.08784896,"max_rate":0.08828930566416039,"is_open":false,"buy_tag":null,"open_timestamp":1515698700000.0,"close_timestamp":1515702900000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.58863858961802,"open_date":"2018-01-11 22:35:00+00:00","close_date":"2018-01-11 23:30:00+00:00","open_rate":5.105e-05,"close_rate":5.130588972431077e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":2.558897243107704e-07,"exit_reason":"roi","initial_stop_loss_abs":4.5945e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.5945e-05,"stop_loss_ratio":0.1,"min_rate":5.105e-05,"max_rate":5.130588972431077e-05,"is_open":false,"buy_tag":null,"open_timestamp":1515710100000.0,"close_timestamp":1515713400000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":25.252525252525253,"open_date":"2018-01-11 22:55:00+00:00","close_date":"2018-01-11 23:25:00+00:00","open_rate":3.96e-05,"close_rate":4.019548872180451e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":5.954887218045116e-07,"exit_reason":"roi","initial_stop_loss_abs":3.5640000000000004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.5640000000000004e-05,"stop_loss_ratio":0.1,"min_rate":3.96e-05,"max_rate":4.019548872180451e-05,"is_open":false,"buy_tag":null,"open_timestamp":1515711300000.0,"close_timestamp":1515713100000.0},{"pair":"NXT/BTC","stake_amount":0.001,"amount":34.66204506065858,"open_date":"2018-01-11 22:55:00+00:00","close_date":"2018-01-11 23:35:00+00:00","open_rate":2.885e-05,"close_rate":2.899461152882205e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":1.4461152882205115e-07,"exit_reason":"roi","initial_stop_loss_abs":2.5965e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.5965e-05,"stop_loss_ratio":0.1,"min_rate":2.885e-05,"max_rate":2.899461152882205e-05,"is_open":false,"buy_tag":null,"open_timestamp":1515711300000.0,"close_timestamp":1515713700000.0},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03780718336483932,"open_date":"2018-01-11 23:30:00+00:00","close_date":"2018-01-12 00:05:00+00:00","open_rate":0.02645,"close_rate":0.026847744360902256,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":0.0003977443609022545,"exit_reason":"roi","initial_stop_loss_abs":0.023805000000000003,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.023805000000000003,"stop_loss_ratio":0.1,"min_rate":0.02645,"max_rate":0.026847744360902256,"is_open":false,"buy_tag":null,"open_timestamp":1515713400000.0,"close_timestamp":1515715500000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.020833333333333332,"open_date":"2018-01-11 23:55:00+00:00","close_date":"2018-01-12 01:15:00+00:00","open_rate":0.048,"close_rate":0.04824060150375939,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":-0.0,"profit_abs":0.00024060150375938838,"exit_reason":"roi","initial_stop_loss_abs":0.0432,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0432,"stop_loss_ratio":0.1,"min_rate":0.048,"max_rate":0.04824060150375939,"is_open":false,"buy_tag":null,"open_timestamp":1515714900000.0,"close_timestamp":1515719700000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":21.31287297527707,"open_date":"2018-01-12 21:15:00+00:00","close_date":"2018-01-12 21:40:00+00:00","open_rate":4.692e-05,"close_rate":4.809593984962405e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":1.1759398496240516e-06,"exit_reason":"roi","initial_stop_loss_abs":4.2227999999999996e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.2227999999999996e-05,"stop_loss_ratio":0.1,"min_rate":4.692e-05,"max_rate":4.809593984962405e-05,"is_open":false,"buy_tag":null,"open_timestamp":1515791700000.0,"close_timestamp":1515793200000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.38915654211062944,"open_date":"2018-01-13 00:55:00+00:00","close_date":"2018-01-13 06:20:00+00:00","open_rate":0.00256966,"close_rate":0.0025825405012531327,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":325,"profit_ratio":-0.0,"profit_abs":1.2880501253132587e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002312694,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002312694,"stop_loss_ratio":0.1,"min_rate":0.00256966,"max_rate":0.0025825405012531327,"is_open":false,"buy_tag":null,"open_timestamp":1515804900000.0,"close_timestamp":1515824400000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":15.96933886937081,"open_date":"2018-01-13 10:55:00+00:00","close_date":"2018-01-13 11:35:00+00:00","open_rate":6.262e-05,"close_rate":6.293388471177944e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":3.138847117794446e-07,"exit_reason":"roi","initial_stop_loss_abs":5.6358e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.6358e-05,"stop_loss_ratio":0.1,"min_rate":6.262e-05,"max_rate":6.293388471177944e-05,"is_open":false,"buy_tag":null,"open_timestamp":1515840900000.0,"close_timestamp":1515843300000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":21.141649048625794,"open_date":"2018-01-13 13:05:00+00:00","close_date":"2018-01-15 14:10:00+00:00","open_rate":4.73e-05,"close_rate":4.753709273182957e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":2945,"profit_ratio":0.0,"profit_abs":2.3709273182957117e-07,"exit_reason":"roi","initial_stop_loss_abs":4.257e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.257e-05,"stop_loss_ratio":0.1,"min_rate":4.73e-05,"max_rate":4.753709273182957e-05,"is_open":false,"buy_tag":null,"open_timestamp":1515848700000.0,"close_timestamp":1516025400000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":16.49348507339601,"open_date":"2018-01-13 13:30:00+00:00","close_date":"2018-01-13 14:45:00+00:00","open_rate":6.063e-05,"close_rate":6.0933909774436085e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":75,"profit_ratio":-0.0,"profit_abs":3.039097744360846e-07,"exit_reason":"roi","initial_stop_loss_abs":5.4567e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.4567e-05,"stop_loss_ratio":0.1,"min_rate":6.063e-05,"max_rate":6.0933909774436085e-05,"is_open":false,"buy_tag":null,"open_timestamp":1515850200000.0,"close_timestamp":1515854700000.0},{"pair":"TRX/BTC","stake_amount":0.001,"amount":9.023641941887746,"open_date":"2018-01-13 13:40:00+00:00","close_date":"2018-01-13 23:30:00+00:00","open_rate":0.00011082,"close_rate":0.00011137548872180448,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":590,"profit_ratio":-0.0,"profit_abs":5.554887218044781e-07,"exit_reason":"roi","initial_stop_loss_abs":9.9738e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":9.9738e-05,"stop_loss_ratio":0.1,"min_rate":0.00011082,"max_rate":0.00011137548872180448,"is_open":false,"buy_tag":null,"open_timestamp":1515850800000.0,"close_timestamp":1515886200000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":16.863406408094438,"open_date":"2018-01-13 15:15:00+00:00","close_date":"2018-01-13 15:55:00+00:00","open_rate":5.93e-05,"close_rate":5.9597243107769415e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":2.9724310776941686e-07,"exit_reason":"roi","initial_stop_loss_abs":5.337e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.337e-05,"stop_loss_ratio":0.1,"min_rate":5.93e-05,"max_rate":5.9597243107769415e-05,"is_open":false,"buy_tag":null,"open_timestamp":1515856500000.0,"close_timestamp":1515858900000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.020618543947292404,"open_date":"2018-01-13 16:30:00+00:00","close_date":"2018-01-13 17:10:00+00:00","open_rate":0.04850003,"close_rate":0.04874313791979949,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.00024310791979949287,"exit_reason":"roi","initial_stop_loss_abs":0.043650027,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.043650027,"stop_loss_ratio":0.1,"min_rate":0.04850003,"max_rate":0.04874313791979949,"is_open":false,"buy_tag":null,"open_timestamp":1515861000000.0,"close_timestamp":1515863400000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010178097365511457,"open_date":"2018-01-13 22:05:00+00:00","close_date":"2018-01-14 06:25:00+00:00","open_rate":0.09825019,"close_rate":0.09874267215538848,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":500,"profit_ratio":-0.0,"profit_abs":0.0004924821553884823,"exit_reason":"roi","initial_stop_loss_abs":0.088425171,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.088425171,"stop_loss_ratio":0.1,"min_rate":0.09825019,"max_rate":0.09874267215538848,"is_open":false,"buy_tag":null,"open_timestamp":1515881100000.0,"close_timestamp":1515911100000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":16.616816218012627,"open_date":"2018-01-14 00:20:00+00:00","close_date":"2018-01-14 22:55:00+00:00","open_rate":6.018e-05,"close_rate":6.048165413533834e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":1355,"profit_ratio":0.0,"profit_abs":3.0165413533833987e-07,"exit_reason":"roi","initial_stop_loss_abs":5.4162e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.4162e-05,"stop_loss_ratio":0.1,"min_rate":6.018e-05,"max_rate":6.048165413533834e-05,"is_open":false,"buy_tag":null,"open_timestamp":1515889200000.0,"close_timestamp":1515970500000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010246952581919518,"open_date":"2018-01-14 12:45:00+00:00","close_date":"2018-01-14 13:25:00+00:00","open_rate":0.09758999,"close_rate":0.0980791628822055,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.0004891728822054991,"exit_reason":"roi","initial_stop_loss_abs":0.087830991,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.087830991,"stop_loss_ratio":0.1,"min_rate":0.09758999,"max_rate":0.0980791628822055,"is_open":false,"buy_tag":null,"open_timestamp":1515933900000.0,"close_timestamp":1515936300000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.3215434083601286,"open_date":"2018-01-14 15:30:00+00:00","close_date":"2018-01-14 16:00:00+00:00","open_rate":0.00311,"close_rate":0.0031567669172932328,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":4.676691729323286e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002799,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002799,"stop_loss_ratio":0.1,"min_rate":0.00311,"max_rate":0.0031567669172932328,"is_open":false,"buy_tag":null,"open_timestamp":1515943800000.0,"close_timestamp":1515945600000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.32010140812609433,"open_date":"2018-01-14 20:45:00+00:00","close_date":"2018-01-14 22:15:00+00:00","open_rate":0.00312401,"close_rate":0.003139669197994987,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":90,"profit_ratio":-0.0,"profit_abs":1.5659197994987058e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002811609,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002811609,"stop_loss_ratio":0.1,"min_rate":0.00312401,"max_rate":0.003139669197994987,"is_open":false,"buy_tag":null,"open_timestamp":1515962700000.0,"close_timestamp":1515968100000.0},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.057247866085791646,"open_date":"2018-01-14 23:35:00+00:00","close_date":"2018-01-15 00:30:00+00:00","open_rate":0.0174679,"close_rate":0.017555458395989976,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":0.0,"profit_abs":8.755839598997492e-05,"exit_reason":"roi","initial_stop_loss_abs":0.015721110000000003,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.015721110000000003,"stop_loss_ratio":0.1,"min_rate":0.0174679,"max_rate":0.017555458395989976,"is_open":false,"buy_tag":null,"open_timestamp":1515972900000.0,"close_timestamp":1515976200000.0},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.013611282991367997,"open_date":"2018-01-14 23:45:00+00:00","close_date":"2018-01-15 00:25:00+00:00","open_rate":0.07346846,"close_rate":0.07383672295739348,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.00036826295739347814,"exit_reason":"roi","initial_stop_loss_abs":0.066121614,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.066121614,"stop_loss_ratio":0.1,"min_rate":0.07346846,"max_rate":0.07383672295739348,"is_open":false,"buy_tag":null,"open_timestamp":1515973500000.0,"close_timestamp":1515975900000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010204706410596568,"open_date":"2018-01-15 02:25:00+00:00","close_date":"2018-01-15 03:05:00+00:00","open_rate":0.097994,"close_rate":0.09848519799498744,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.0004911979949874384,"exit_reason":"roi","initial_stop_loss_abs":0.0881946,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0881946,"stop_loss_ratio":0.1,"min_rate":0.097994,"max_rate":0.09848519799498744,"is_open":false,"buy_tag":null,"open_timestamp":1515983100000.0,"close_timestamp":1515985500000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010353038616834042,"open_date":"2018-01-15 07:20:00+00:00","close_date":"2018-01-15 08:00:00+00:00","open_rate":0.09659,"close_rate":0.09707416040100247,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.0004841604010024786,"exit_reason":"roi","initial_stop_loss_abs":0.086931,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.086931,"stop_loss_ratio":0.1,"min_rate":0.09659,"max_rate":0.09707416040100247,"is_open":false,"buy_tag":null,"open_timestamp":1516000800000.0,"close_timestamp":1516003200000.0},{"pair":"TRX/BTC","stake_amount":0.001,"amount":10.013016921998599,"open_date":"2018-01-15 08:20:00+00:00","close_date":"2018-01-15 08:55:00+00:00","open_rate":9.987e-05,"close_rate":0.00010137180451127818,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.501804511278178e-06,"exit_reason":"roi","initial_stop_loss_abs":8.9883e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.9883e-05,"stop_loss_ratio":0.1,"min_rate":9.987e-05,"max_rate":0.00010137180451127818,"is_open":false,"buy_tag":null,"open_timestamp":1516004400000.0,"close_timestamp":1516006500000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010537752023511832,"open_date":"2018-01-15 12:10:00+00:00","close_date":"2018-01-16 02:50:00+00:00","open_rate":0.0948969,"close_rate":0.09537257368421052,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":880,"profit_ratio":0.0,"profit_abs":0.0004756736842105175,"exit_reason":"roi","initial_stop_loss_abs":0.08540721000000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.08540721000000001,"stop_loss_ratio":0.1,"min_rate":0.0948969,"max_rate":0.09537257368421052,"is_open":false,"buy_tag":null,"open_timestamp":1516018200000.0,"close_timestamp":1516071000000.0},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014084507042253523,"open_date":"2018-01-15 14:10:00+00:00","close_date":"2018-01-15 17:40:00+00:00","open_rate":0.071,"close_rate":0.07135588972431077,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":210,"profit_ratio":-0.0,"profit_abs":0.00035588972431077615,"exit_reason":"roi","initial_stop_loss_abs":0.0639,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0639,"stop_loss_ratio":0.1,"min_rate":0.071,"max_rate":0.07135588972431077,"is_open":false,"buy_tag":null,"open_timestamp":1516025400000.0,"close_timestamp":1516038000000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.021736763017766978,"open_date":"2018-01-15 14:30:00+00:00","close_date":"2018-01-15 15:10:00+00:00","open_rate":0.04600501,"close_rate":0.046235611553884705,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.00023060155388470588,"exit_reason":"roi","initial_stop_loss_abs":0.041404509,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.041404509,"stop_loss_ratio":0.1,"min_rate":0.04600501,"max_rate":0.046235611553884705,"is_open":false,"buy_tag":null,"open_timestamp":1516026600000.0,"close_timestamp":1516029000000.0},{"pair":"TRX/BTC","stake_amount":0.001,"amount":10.595465140919686,"open_date":"2018-01-15 18:10:00+00:00","close_date":"2018-01-15 19:25:00+00:00","open_rate":9.438e-05,"close_rate":9.485308270676693e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":75,"profit_ratio":-0.0,"profit_abs":4.7308270676692514e-07,"exit_reason":"roi","initial_stop_loss_abs":8.4942e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.4942e-05,"stop_loss_ratio":0.1,"min_rate":9.438e-05,"max_rate":9.485308270676693e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516039800000.0,"close_timestamp":1516044300000.0},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.032894726021471705,"open_date":"2018-01-15 18:35:00+00:00","close_date":"2018-01-15 19:15:00+00:00","open_rate":0.03040001,"close_rate":0.030552391002506264,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.0001523810025062626,"exit_reason":"roi","initial_stop_loss_abs":0.027360009,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.027360009,"stop_loss_ratio":0.1,"min_rate":0.03040001,"max_rate":0.030552391002506264,"is_open":false,"buy_tag":null,"open_timestamp":1516041300000.0,"close_timestamp":1516043700000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":17.13208840157615,"open_date":"2018-01-15 20:25:00+00:00","close_date":"2018-01-16 08:25:00+00:00","open_rate":5.837e-05,"close_rate":5.2533e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":720,"profit_ratio":-0.10448878,"profit_abs":-5.8369999999999985e-06,"exit_reason":"stop_loss","initial_stop_loss_abs":5.2533e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.2533e-05,"stop_loss_ratio":0.1,"min_rate":5.2533e-05,"max_rate":5.837e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516047900000.0,"close_timestamp":1516091100000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.021722130506560085,"open_date":"2018-01-15 20:40:00+00:00","close_date":"2018-01-15 22:00:00+00:00","open_rate":0.046036,"close_rate":0.04626675689223057,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":-0.0,"profit_abs":0.00023075689223057277,"exit_reason":"roi","initial_stop_loss_abs":0.0414324,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0414324,"stop_loss_ratio":0.1,"min_rate":0.046036,"max_rate":0.04626675689223057,"is_open":false,"buy_tag":null,"open_timestamp":1516048800000.0,"close_timestamp":1516053600000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.34861425832316545,"open_date":"2018-01-16 00:30:00+00:00","close_date":"2018-01-16 01:10:00+00:00","open_rate":0.0028685,"close_rate":0.0028828784461152877,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":1.4378446115287727e-05,"exit_reason":"roi","initial_stop_loss_abs":0.00258165,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.00258165,"stop_loss_ratio":0.1,"min_rate":0.0028685,"max_rate":0.0028828784461152877,"is_open":false,"buy_tag":null,"open_timestamp":1516062600000.0,"close_timestamp":1516065000000.0},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014854967241083492,"open_date":"2018-01-16 01:15:00+00:00","close_date":"2018-01-16 02:35:00+00:00","open_rate":0.06731755,"close_rate":0.0676549813283208,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":0.0,"profit_abs":0.00033743132832080025,"exit_reason":"roi","initial_stop_loss_abs":0.060585795000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.060585795000000005,"stop_loss_ratio":0.1,"min_rate":0.06731755,"max_rate":0.0676549813283208,"is_open":false,"buy_tag":null,"open_timestamp":1516065300000.0,"close_timestamp":1516070100000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010848794492804754,"open_date":"2018-01-16 07:45:00+00:00","close_date":"2018-01-16 08:40:00+00:00","open_rate":0.09217614,"close_rate":0.09263817578947368,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":0.0,"profit_abs":0.0004620357894736804,"exit_reason":"roi","initial_stop_loss_abs":0.082958526,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.082958526,"stop_loss_ratio":0.1,"min_rate":0.09217614,"max_rate":0.09263817578947368,"is_open":false,"buy_tag":null,"open_timestamp":1516088700000.0,"close_timestamp":1516092000000.0},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.06060606060606061,"open_date":"2018-01-16 08:35:00+00:00","close_date":"2018-01-16 08:55:00+00:00","open_rate":0.0165,"close_rate":0.016913533834586467,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":0.00041353383458646656,"exit_reason":"roi","initial_stop_loss_abs":0.01485,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.01485,"stop_loss_ratio":0.1,"min_rate":0.0165,"max_rate":0.016913533834586467,"is_open":false,"buy_tag":null,"open_timestamp":1516091700000.0,"close_timestamp":1516092900000.0},{"pair":"TRX/BTC","stake_amount":0.001,"amount":12.57387149503332,"open_date":"2018-01-16 08:35:00+00:00","close_date":"2018-01-16 08:40:00+00:00","open_rate":7.953e-05,"close_rate":8.311781954887218e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":3.587819548872171e-06,"exit_reason":"roi","initial_stop_loss_abs":7.157700000000001e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":7.157700000000001e-05,"stop_loss_ratio":0.1,"min_rate":7.953e-05,"max_rate":8.311781954887218e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516091700000.0,"close_timestamp":1516092000000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.022122914915269236,"open_date":"2018-01-16 08:45:00+00:00","close_date":"2018-01-16 09:50:00+00:00","open_rate":0.045202,"close_rate":0.04542857644110275,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":65,"profit_ratio":-0.0,"profit_abs":0.00022657644110275071,"exit_reason":"roi","initial_stop_loss_abs":0.0406818,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0406818,"stop_loss_ratio":0.1,"min_rate":0.045202,"max_rate":0.04542857644110275,"is_open":false,"buy_tag":null,"open_timestamp":1516092300000.0,"close_timestamp":1516096200000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.054878048780488,"open_date":"2018-01-16 09:15:00+00:00","close_date":"2018-01-16 09:45:00+00:00","open_rate":5.248e-05,"close_rate":5.326917293233082e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":7.891729323308177e-07,"exit_reason":"roi","initial_stop_loss_abs":4.7232e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7232e-05,"stop_loss_ratio":0.1,"min_rate":5.248e-05,"max_rate":5.326917293233082e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516094100000.0,"close_timestamp":1516095900000.0},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03457434486802627,"open_date":"2018-01-16 09:15:00+00:00","close_date":"2018-01-16 09:55:00+00:00","open_rate":0.02892318,"close_rate":0.02906815834586466,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":0.0001449783458646603,"exit_reason":"roi","initial_stop_loss_abs":0.026030862000000002,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.026030862000000002,"stop_loss_ratio":0.1,"min_rate":0.02892318,"max_rate":0.02906815834586466,"is_open":false,"buy_tag":null,"open_timestamp":1516094100000.0,"close_timestamp":1516096500000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.38735944164405,"open_date":"2018-01-16 09:50:00+00:00","close_date":"2018-01-16 10:10:00+00:00","open_rate":5.158e-05,"close_rate":5.287273182957392e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":1.2927318295739246e-06,"exit_reason":"roi","initial_stop_loss_abs":4.6422e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6422e-05,"stop_loss_ratio":0.1,"min_rate":5.158e-05,"max_rate":5.287273182957392e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516096200000.0,"close_timestamp":1516097400000.0},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.035357778286929785,"open_date":"2018-01-16 10:05:00+00:00","close_date":"2018-01-16 10:35:00+00:00","open_rate":0.02828232,"close_rate":0.02870761804511278,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":0.00042529804511277913,"exit_reason":"roi","initial_stop_loss_abs":0.025454088,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025454088,"stop_loss_ratio":0.1,"min_rate":0.02828232,"max_rate":0.02870761804511278,"is_open":false,"buy_tag":null,"open_timestamp":1516097100000.0,"close_timestamp":1516098900000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.022948496230938982,"open_date":"2018-01-16 10:05:00+00:00","close_date":"2018-01-16 10:40:00+00:00","open_rate":0.04357584,"close_rate":0.044231115789473675,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":0.0006552757894736777,"exit_reason":"roi","initial_stop_loss_abs":0.039218256,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.039218256,"stop_loss_ratio":0.1,"min_rate":0.04357584,"max_rate":0.044231115789473675,"is_open":false,"buy_tag":null,"open_timestamp":1516097100000.0,"close_timestamp":1516099200000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":18.64975755315181,"open_date":"2018-01-16 13:45:00+00:00","close_date":"2018-01-16 14:20:00+00:00","open_rate":5.362e-05,"close_rate":5.442631578947368e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":8.063157894736843e-07,"exit_reason":"roi","initial_stop_loss_abs":4.8258e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.8258e-05,"stop_loss_ratio":0.1,"min_rate":5.362e-05,"max_rate":5.442631578947368e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516110300000.0,"close_timestamp":1516112400000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":18.86080724254998,"open_date":"2018-01-16 17:30:00+00:00","close_date":"2018-01-16 18:25:00+00:00","open_rate":5.302e-05,"close_rate":5.328576441102756e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":2.6576441102756397e-07,"exit_reason":"roi","initial_stop_loss_abs":4.7718e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7718e-05,"stop_loss_ratio":0.1,"min_rate":5.302e-05,"max_rate":5.328576441102756e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516123800000.0,"close_timestamp":1516127100000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010952903718828448,"open_date":"2018-01-16 18:15:00+00:00","close_date":"2018-01-16 18:45:00+00:00","open_rate":0.09129999,"close_rate":0.09267292218045112,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":0.0013729321804511196,"exit_reason":"roi","initial_stop_loss_abs":0.082169991,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.082169991,"stop_loss_ratio":0.1,"min_rate":0.09129999,"max_rate":0.09267292218045112,"is_open":false,"buy_tag":null,"open_timestamp":1516126500000.0,"close_timestamp":1516128300000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":26.26050420168067,"open_date":"2018-01-16 18:15:00+00:00","close_date":"2018-01-16 18:35:00+00:00","open_rate":3.808e-05,"close_rate":3.903438596491228e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":9.543859649122774e-07,"exit_reason":"roi","initial_stop_loss_abs":3.4272e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.4272e-05,"stop_loss_ratio":0.1,"min_rate":3.808e-05,"max_rate":3.903438596491228e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516126500000.0,"close_timestamp":1516127700000.0},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.035574376772493324,"open_date":"2018-01-16 19:00:00+00:00","close_date":"2018-01-16 19:30:00+00:00","open_rate":0.02811012,"close_rate":0.028532828571428567,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":0.00042270857142856846,"exit_reason":"roi","initial_stop_loss_abs":0.025299108,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025299108,"stop_loss_ratio":0.1,"min_rate":0.02811012,"max_rate":0.028532828571428567,"is_open":false,"buy_tag":null,"open_timestamp":1516129200000.0,"close_timestamp":1516131000000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.387028357567759,"open_date":"2018-01-16 21:25:00+00:00","close_date":"2018-01-16 22:25:00+00:00","open_rate":0.00258379,"close_rate":0.002325411,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":-0.10448878,"profit_abs":-0.000258379,"exit_reason":"stop_loss","initial_stop_loss_abs":0.002325411,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002325411,"stop_loss_ratio":0.1,"min_rate":0.002325411,"max_rate":0.00258379,"is_open":false,"buy_tag":null,"open_timestamp":1516137900000.0,"close_timestamp":1516141500000.0},{"pair":"NXT/BTC","stake_amount":0.001,"amount":39.07776475185619,"open_date":"2018-01-16 21:25:00+00:00","close_date":"2018-01-16 22:45:00+00:00","open_rate":2.559e-05,"close_rate":2.3031e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":-0.10448878,"profit_abs":-2.5590000000000004e-06,"exit_reason":"stop_loss","initial_stop_loss_abs":2.3031e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.3031e-05,"stop_loss_ratio":0.1,"min_rate":2.3031e-05,"max_rate":2.559e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516137900000.0,"close_timestamp":1516142700000.0},{"pair":"TRX/BTC","stake_amount":0.001,"amount":13.123359580052494,"open_date":"2018-01-16 21:35:00+00:00","close_date":"2018-01-16 22:25:00+00:00","open_rate":7.62e-05,"close_rate":6.858e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":-0.10448878,"profit_abs":-7.619999999999998e-06,"exit_reason":"stop_loss","initial_stop_loss_abs":6.858e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":6.858e-05,"stop_loss_ratio":0.1,"min_rate":6.858e-05,"max_rate":7.62e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516138500000.0,"close_timestamp":1516141500000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.4350777048780912,"open_date":"2018-01-16 22:30:00+00:00","close_date":"2018-01-16 22:35:00+00:00","open_rate":0.00229844,"close_rate":0.002402129022556391,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":0.00010368902255639091,"exit_reason":"roi","initial_stop_loss_abs":0.0020685960000000002,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0020685960000000002,"stop_loss_ratio":0.1,"min_rate":0.00229844,"max_rate":0.002402129022556391,"is_open":false,"buy_tag":null,"open_timestamp":1516141800000.0,"close_timestamp":1516142100000.0},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.06622516556291391,"open_date":"2018-01-16 22:30:00+00:00","close_date":"2018-01-16 22:40:00+00:00","open_rate":0.0151,"close_rate":0.015781203007518795,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":10,"profit_ratio":0.03990025,"profit_abs":0.0006812030075187946,"exit_reason":"roi","initial_stop_loss_abs":0.013590000000000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.013590000000000001,"stop_loss_ratio":0.1,"min_rate":0.0151,"max_rate":0.015781203007518795,"is_open":false,"buy_tag":null,"open_timestamp":1516141800000.0,"close_timestamp":1516142400000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.42431134269081283,"open_date":"2018-01-16 22:40:00+00:00","close_date":"2018-01-16 22:45:00+00:00","open_rate":0.00235676,"close_rate":0.00246308,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":0.00010632000000000003,"exit_reason":"roi","initial_stop_loss_abs":0.002121084,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002121084,"stop_loss_ratio":0.1,"min_rate":0.00235676,"max_rate":0.00246308,"is_open":false,"buy_tag":null,"open_timestamp":1516142400000.0,"close_timestamp":1516142700000.0},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.01585559988076589,"open_date":"2018-01-16 22:45:00+00:00","close_date":"2018-01-16 23:05:00+00:00","open_rate":0.0630692,"close_rate":0.06464988170426066,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":0.0015806817042606502,"exit_reason":"roi","initial_stop_loss_abs":0.056762280000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.056762280000000005,"stop_loss_ratio":0.1,"min_rate":0.0630692,"max_rate":0.06464988170426066,"is_open":false,"buy_tag":null,"open_timestamp":1516142700000.0,"close_timestamp":1516143900000.0},{"pair":"NXT/BTC","stake_amount":0.001,"amount":45.45454545454545,"open_date":"2018-01-16 22:50:00+00:00","close_date":"2018-01-16 22:55:00+00:00","open_rate":2.2e-05,"close_rate":2.299248120300751e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":9.924812030075114e-07,"exit_reason":"roi","initial_stop_loss_abs":1.98e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":1.98e-05,"stop_loss_ratio":0.1,"min_rate":2.2e-05,"max_rate":2.299248120300751e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516143000000.0,"close_timestamp":1516143300000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":20.10454362685967,"open_date":"2018-01-17 03:30:00+00:00","close_date":"2018-01-17 04:00:00+00:00","open_rate":4.974e-05,"close_rate":5.048796992481203e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":7.479699248120277e-07,"exit_reason":"roi","initial_stop_loss_abs":4.4766000000000005e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.4766000000000005e-05,"stop_loss_ratio":0.1,"min_rate":4.974e-05,"max_rate":5.048796992481203e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516159800000.0,"close_timestamp":1516161600000.0},{"pair":"TRX/BTC","stake_amount":0.001,"amount":14.068655036578503,"open_date":"2018-01-17 03:55:00+00:00","close_date":"2018-01-17 04:15:00+00:00","open_rate":7.108e-05,"close_rate":7.28614536340852e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":1.7814536340851996e-06,"exit_reason":"roi","initial_stop_loss_abs":6.3972e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":6.3972e-05,"stop_loss_ratio":0.1,"min_rate":7.108e-05,"max_rate":7.28614536340852e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516161300000.0,"close_timestamp":1516162500000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.0231107002542177,"open_date":"2018-01-17 09:35:00+00:00","close_date":"2018-01-17 10:15:00+00:00","open_rate":0.04327,"close_rate":0.04348689223057644,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.0002168922305764362,"exit_reason":"roi","initial_stop_loss_abs":0.038943000000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.038943000000000005,"stop_loss_ratio":0.1,"min_rate":0.04327,"max_rate":0.04348689223057644,"is_open":false,"buy_tag":null,"open_timestamp":1516181700000.0,"close_timestamp":1516184100000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":20.012007204322593,"open_date":"2018-01-17 10:20:00+00:00","close_date":"2018-01-17 17:00:00+00:00","open_rate":4.997e-05,"close_rate":5.022047619047618e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":400,"profit_ratio":-0.0,"profit_abs":2.504761904761831e-07,"exit_reason":"roi","initial_stop_loss_abs":4.4973e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.4973e-05,"stop_loss_ratio":0.1,"min_rate":4.997e-05,"max_rate":5.022047619047618e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516184400000.0,"close_timestamp":1516208400000.0},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014626687444363738,"open_date":"2018-01-17 10:30:00+00:00","close_date":"2018-01-17 11:25:00+00:00","open_rate":0.06836818,"close_rate":0.06871087764411027,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":0.00034269764411026804,"exit_reason":"roi","initial_stop_loss_abs":0.061531362,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.061531362,"stop_loss_ratio":0.1,"min_rate":0.06836818,"max_rate":0.06871087764411027,"is_open":false,"buy_tag":null,"open_timestamp":1516185000000.0,"close_timestamp":1516188300000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":27.548209366391184,"open_date":"2018-01-17 10:30:00+00:00","close_date":"2018-01-17 11:10:00+00:00","open_rate":3.63e-05,"close_rate":3.648195488721804e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":1.8195488721804031e-07,"exit_reason":"roi","initial_stop_loss_abs":3.2670000000000004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.2670000000000004e-05,"stop_loss_ratio":0.1,"min_rate":3.63e-05,"max_rate":3.648195488721804e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516185000000.0,"close_timestamp":1516187400000.0},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03558718861209965,"open_date":"2018-01-17 12:30:00+00:00","close_date":"2018-01-17 22:05:00+00:00","open_rate":0.0281,"close_rate":0.02824085213032581,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":575,"profit_ratio":-0.0,"profit_abs":0.0001408521303258095,"exit_reason":"roi","initial_stop_loss_abs":0.02529,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.02529,"stop_loss_ratio":0.1,"min_rate":0.0281,"max_rate":0.02824085213032581,"is_open":false,"buy_tag":null,"open_timestamp":1516192200000.0,"close_timestamp":1516226700000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011559355963546878,"open_date":"2018-01-17 12:35:00+00:00","close_date":"2018-01-17 16:55:00+00:00","open_rate":0.08651001,"close_rate":0.08694364413533832,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":260,"profit_ratio":-0.0,"profit_abs":0.00043363413533832607,"exit_reason":"roi","initial_stop_loss_abs":0.077859009,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.077859009,"stop_loss_ratio":0.1,"min_rate":0.08651001,"max_rate":0.08694364413533832,"is_open":false,"buy_tag":null,"open_timestamp":1516192500000.0,"close_timestamp":1516208100000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":17.752529735487308,"open_date":"2018-01-18 05:00:00+00:00","close_date":"2018-01-18 05:55:00+00:00","open_rate":5.633e-05,"close_rate":5.6612355889724306e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":2.8235588972430847e-07,"exit_reason":"roi","initial_stop_loss_abs":5.0697e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.0697e-05,"stop_loss_ratio":0.1,"min_rate":5.633e-05,"max_rate":5.6612355889724306e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516251600000.0,"close_timestamp":1516254900000.0},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.01430923457900944,"open_date":"2018-01-18 05:20:00+00:00","close_date":"2018-01-18 05:55:00+00:00","open_rate":0.06988494,"close_rate":0.07093584135338346,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":0.0010509013533834544,"exit_reason":"roi","initial_stop_loss_abs":0.06289644600000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.06289644600000001,"stop_loss_ratio":0.1,"min_rate":0.06988494,"max_rate":0.07093584135338346,"is_open":false,"buy_tag":null,"open_timestamp":1516252800000.0,"close_timestamp":1516254900000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":18.034265103697024,"open_date":"2018-01-18 07:35:00+00:00","close_date":"2018-01-18 08:15:00+00:00","open_rate":5.545e-05,"close_rate":5.572794486215538e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":2.779448621553787e-07,"exit_reason":"roi","initial_stop_loss_abs":4.9905e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.9905e-05,"stop_loss_ratio":0.1,"min_rate":5.545e-05,"max_rate":5.572794486215538e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516260900000.0,"close_timestamp":1516263300000.0},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.06121723118136401,"open_date":"2018-01-18 09:00:00+00:00","close_date":"2018-01-18 09:40:00+00:00","open_rate":0.01633527,"close_rate":0.016417151052631574,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":8.188105263157511e-05,"exit_reason":"roi","initial_stop_loss_abs":0.014701743,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.014701743,"stop_loss_ratio":0.1,"min_rate":0.01633527,"max_rate":0.016417151052631574,"is_open":false,"buy_tag":null,"open_timestamp":1516266000000.0,"close_timestamp":1516268400000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.3707356136045141,"open_date":"2018-01-18 16:40:00+00:00","close_date":"2018-01-18 17:20:00+00:00","open_rate":0.00269734,"close_rate":0.002710860501253133,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":1.3520501253133123e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002427606,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002427606,"stop_loss_ratio":0.1,"min_rate":0.00269734,"max_rate":0.002710860501253133,"is_open":false,"buy_tag":null,"open_timestamp":1516293600000.0,"close_timestamp":1516296000000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":22.346368715083802,"open_date":"2018-01-18 18:05:00+00:00","close_date":"2018-01-18 18:30:00+00:00","open_rate":4.475e-05,"close_rate":4.587155388471177e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":1.1215538847117757e-06,"exit_reason":"roi","initial_stop_loss_abs":4.0274999999999996e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.0274999999999996e-05,"stop_loss_ratio":0.1,"min_rate":4.475e-05,"max_rate":4.587155388471177e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516298700000.0,"close_timestamp":1516300200000.0},{"pair":"NXT/BTC","stake_amount":0.001,"amount":35.842293906810035,"open_date":"2018-01-18 18:25:00+00:00","close_date":"2018-01-18 18:55:00+00:00","open_rate":2.79e-05,"close_rate":2.8319548872180444e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":4.1954887218044365e-07,"exit_reason":"roi","initial_stop_loss_abs":2.5110000000000002e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.5110000000000002e-05,"stop_loss_ratio":0.1,"min_rate":2.79e-05,"max_rate":2.8319548872180444e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516299900000.0,"close_timestamp":1516301700000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.022525942001105574,"open_date":"2018-01-18 20:10:00+00:00","close_date":"2018-01-18 20:50:00+00:00","open_rate":0.04439326,"close_rate":0.04461578260651629,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":0.00022252260651629135,"exit_reason":"roi","initial_stop_loss_abs":0.039953933999999997,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.039953933999999997,"stop_loss_ratio":0.1,"min_rate":0.04439326,"max_rate":0.04461578260651629,"is_open":false,"buy_tag":null,"open_timestamp":1516306200000.0,"close_timestamp":1516308600000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":22.271714922048996,"open_date":"2018-01-18 21:30:00+00:00","close_date":"2018-01-19 00:35:00+00:00","open_rate":4.49e-05,"close_rate":4.51250626566416e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":185,"profit_ratio":0.0,"profit_abs":2.2506265664159932e-07,"exit_reason":"roi","initial_stop_loss_abs":4.041e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.041e-05,"stop_loss_ratio":0.1,"min_rate":4.49e-05,"max_rate":4.51250626566416e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516311000000.0,"close_timestamp":1516322100000.0},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03502626970227671,"open_date":"2018-01-18 21:55:00+00:00","close_date":"2018-01-19 05:05:00+00:00","open_rate":0.02855,"close_rate":0.028693107769423555,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":430,"profit_ratio":-0.0,"profit_abs":0.00014310776942355607,"exit_reason":"roi","initial_stop_loss_abs":0.025695,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025695,"stop_loss_ratio":0.1,"min_rate":0.02855,"max_rate":0.028693107769423555,"is_open":false,"buy_tag":null,"open_timestamp":1516312500000.0,"close_timestamp":1516338300000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":17.25327812284334,"open_date":"2018-01-18 22:10:00+00:00","close_date":"2018-01-18 22:50:00+00:00","open_rate":5.796e-05,"close_rate":5.8250526315789473e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":2.905263157894727e-07,"exit_reason":"roi","initial_stop_loss_abs":5.2164000000000004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.2164000000000004e-05,"stop_loss_ratio":0.1,"min_rate":5.796e-05,"max_rate":5.8250526315789473e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516313400000.0,"close_timestamp":1516315800000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02303975994413319,"open_date":"2018-01-18 23:50:00+00:00","close_date":"2018-01-19 00:30:00+00:00","open_rate":0.04340323,"close_rate":0.04362079005012531,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":0.0002175600501253122,"exit_reason":"roi","initial_stop_loss_abs":0.039062907,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.039062907,"stop_loss_ratio":0.1,"min_rate":0.04340323,"max_rate":0.04362079005012531,"is_open":false,"buy_tag":null,"open_timestamp":1516319400000.0,"close_timestamp":1516321800000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02244943545282195,"open_date":"2018-01-19 16:45:00+00:00","close_date":"2018-01-19 17:35:00+00:00","open_rate":0.04454455,"close_rate":0.04476783095238095,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":0.0,"profit_abs":0.0002232809523809512,"exit_reason":"roi","initial_stop_loss_abs":0.040090095000000006,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.040090095000000006,"stop_loss_ratio":0.1,"min_rate":0.04454455,"max_rate":0.04476783095238095,"is_open":false,"buy_tag":null,"open_timestamp":1516380300000.0,"close_timestamp":1516383300000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":17.793594306049823,"open_date":"2018-01-19 17:15:00+00:00","close_date":"2018-01-19 19:55:00+00:00","open_rate":5.62e-05,"close_rate":5.648170426065162e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":160,"profit_ratio":-0.0,"profit_abs":2.817042606516199e-07,"exit_reason":"roi","initial_stop_loss_abs":5.058e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.058e-05,"stop_loss_ratio":0.1,"min_rate":5.62e-05,"max_rate":5.648170426065162e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516382100000.0,"close_timestamp":1516391700000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":23.046784973496194,"open_date":"2018-01-19 17:20:00+00:00","close_date":"2018-01-19 20:15:00+00:00","open_rate":4.339e-05,"close_rate":4.360749373433584e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":175,"profit_ratio":-0.0,"profit_abs":2.174937343358337e-07,"exit_reason":"roi","initial_stop_loss_abs":3.9051e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.9051e-05,"stop_loss_ratio":0.1,"min_rate":4.339e-05,"max_rate":4.360749373433584e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516382400000.0,"close_timestamp":1516392900000.0},{"pair":"TRX/BTC","stake_amount":0.001,"amount":9.910802775024777,"open_date":"2018-01-20 04:45:00+00:00","close_date":"2018-01-20 17:35:00+00:00","open_rate":0.0001009,"close_rate":0.00010140576441102755,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":770,"profit_ratio":0.0,"profit_abs":5.057644110275549e-07,"exit_reason":"roi","initial_stop_loss_abs":9.081e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":9.081e-05,"stop_loss_ratio":0.1,"min_rate":0.0001009,"max_rate":0.00010140576441102755,"is_open":false,"buy_tag":null,"open_timestamp":1516423500000.0,"close_timestamp":1516469700000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.3696789338459548,"open_date":"2018-01-20 04:50:00+00:00","close_date":"2018-01-20 15:15:00+00:00","open_rate":0.00270505,"close_rate":0.002718609147869674,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":625,"profit_ratio":-0.0,"profit_abs":1.3559147869673764e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002434545,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002434545,"stop_loss_ratio":0.1,"min_rate":0.00270505,"max_rate":0.002718609147869674,"is_open":false,"buy_tag":null,"open_timestamp":1516423800000.0,"close_timestamp":1516461300000.0},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.033333311111125925,"open_date":"2018-01-20 04:50:00+00:00","close_date":"2018-01-20 07:00:00+00:00","open_rate":0.03000002,"close_rate":0.030150396040100245,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":130,"profit_ratio":-0.0,"profit_abs":0.00015037604010024672,"exit_reason":"roi","initial_stop_loss_abs":0.027000018,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.027000018,"stop_loss_ratio":0.1,"min_rate":0.03000002,"max_rate":0.030150396040100245,"is_open":false,"buy_tag":null,"open_timestamp":1516423800000.0,"close_timestamp":1516431600000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":18.315018315018317,"open_date":"2018-01-20 09:00:00+00:00","close_date":"2018-01-20 09:40:00+00:00","open_rate":5.46e-05,"close_rate":5.4873684210526304e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":2.736842105263053e-07,"exit_reason":"roi","initial_stop_loss_abs":4.914e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.914e-05,"stop_loss_ratio":0.1,"min_rate":5.46e-05,"max_rate":5.4873684210526304e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516438800000.0,"close_timestamp":1516441200000.0},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03244412634781012,"open_date":"2018-01-20 18:25:00+00:00","close_date":"2018-01-25 03:50:00+00:00","open_rate":0.03082222,"close_rate":0.027739998,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":6325,"profit_ratio":-0.10448878,"profit_abs":-0.0030822220000000025,"exit_reason":"stop_loss","initial_stop_loss_abs":0.027739998000000002,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.027739998000000002,"stop_loss_ratio":0.1,"min_rate":0.027739998,"max_rate":0.03082222,"is_open":false,"buy_tag":null,"open_timestamp":1516472700000.0,"close_timestamp":1516852200000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011148273260677065,"open_date":"2018-01-20 22:25:00+00:00","close_date":"2018-01-20 23:15:00+00:00","open_rate":0.08969999,"close_rate":0.09014961401002504,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":-0.0,"profit_abs":0.00044962401002504593,"exit_reason":"roi","initial_stop_loss_abs":0.080729991,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.080729991,"stop_loss_ratio":0.1,"min_rate":0.08969999,"max_rate":0.09014961401002504,"is_open":false,"buy_tag":null,"open_timestamp":1516487100000.0,"close_timestamp":1516490100000.0},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.06125570520324337,"open_date":"2018-01-21 02:50:00+00:00","close_date":"2018-01-21 14:30:00+00:00","open_rate":0.01632501,"close_rate":0.01640683962406015,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":700,"profit_ratio":0.0,"profit_abs":8.182962406014932e-05,"exit_reason":"roi","initial_stop_loss_abs":0.014692509000000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.014692509000000001,"stop_loss_ratio":0.1,"min_rate":0.01632501,"max_rate":0.01640683962406015,"is_open":false,"buy_tag":null,"open_timestamp":1516503000000.0,"close_timestamp":1516545000000.0},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.01417675579120474,"open_date":"2018-01-21 10:20:00+00:00","close_date":"2018-01-21 11:00:00+00:00","open_rate":0.070538,"close_rate":0.07089157393483708,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.00035357393483707866,"exit_reason":"roi","initial_stop_loss_abs":0.0634842,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0634842,"stop_loss_ratio":0.1,"min_rate":0.070538,"max_rate":0.07089157393483708,"is_open":false,"buy_tag":null,"open_timestamp":1516530000000.0,"close_timestamp":1516532400000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":18.864365214110546,"open_date":"2018-01-21 15:50:00+00:00","close_date":"2018-01-21 18:45:00+00:00","open_rate":5.301e-05,"close_rate":5.327571428571427e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":175,"profit_ratio":-0.0,"profit_abs":2.657142857142672e-07,"exit_reason":"roi","initial_stop_loss_abs":4.7709e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7709e-05,"stop_loss_ratio":0.1,"min_rate":5.301e-05,"max_rate":5.327571428571427e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516549800000.0,"close_timestamp":1516560300000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":25.284450063211125,"open_date":"2018-01-21 16:20:00+00:00","close_date":"2018-01-21 17:00:00+00:00","open_rate":3.955e-05,"close_rate":3.9748245614035085e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":1.9824561403508552e-07,"exit_reason":"roi","initial_stop_loss_abs":3.5595e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.5595e-05,"stop_loss_ratio":0.1,"min_rate":3.955e-05,"max_rate":3.9748245614035085e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516551600000.0,"close_timestamp":1516554000000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.38683971296493297,"open_date":"2018-01-21 21:15:00+00:00","close_date":"2018-01-21 21:45:00+00:00","open_rate":0.00258505,"close_rate":0.002623922932330827,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":3.8872932330826816e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002326545,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002326545,"stop_loss_ratio":0.1,"min_rate":0.00258505,"max_rate":0.002623922932330827,"is_open":false,"buy_tag":null,"open_timestamp":1516569300000.0,"close_timestamp":1516571100000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":25.621316935690498,"open_date":"2018-01-21 21:15:00+00:00","close_date":"2018-01-21 21:55:00+00:00","open_rate":3.903e-05,"close_rate":3.922563909774435e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":1.9563909774435151e-07,"exit_reason":"roi","initial_stop_loss_abs":3.5127e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.5127e-05,"stop_loss_ratio":0.1,"min_rate":3.903e-05,"max_rate":3.922563909774435e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516569300000.0,"close_timestamp":1516571700000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.098548510313215,"open_date":"2018-01-22 00:35:00+00:00","close_date":"2018-01-22 10:35:00+00:00","open_rate":5.236e-05,"close_rate":5.262245614035087e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":600,"profit_ratio":-0.0,"profit_abs":2.624561403508717e-07,"exit_reason":"roi","initial_stop_loss_abs":4.7124e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7124e-05,"stop_loss_ratio":0.1,"min_rate":5.236e-05,"max_rate":5.262245614035087e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516581300000.0,"close_timestamp":1516617300000.0},{"pair":"TRX/BTC","stake_amount":0.001,"amount":11.076650420912717,"open_date":"2018-01-22 01:30:00+00:00","close_date":"2018-01-22 02:10:00+00:00","open_rate":9.028e-05,"close_rate":9.07325313283208e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":4.5253132832080657e-07,"exit_reason":"roi","initial_stop_loss_abs":8.1252e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.1252e-05,"stop_loss_ratio":0.1,"min_rate":9.028e-05,"max_rate":9.07325313283208e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516584600000.0,"close_timestamp":1516587000000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.3721622627465575,"open_date":"2018-01-22 12:25:00+00:00","close_date":"2018-01-22 14:35:00+00:00","open_rate":0.002687,"close_rate":0.002700468671679198,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":130,"profit_ratio":-0.0,"profit_abs":1.3468671679197925e-05,"exit_reason":"roi","initial_stop_loss_abs":0.0024183000000000004,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0024183000000000004,"stop_loss_ratio":0.1,"min_rate":0.002687,"max_rate":0.002700468671679198,"is_open":false,"buy_tag":null,"open_timestamp":1516623900000.0,"close_timestamp":1516631700000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":23.99232245681382,"open_date":"2018-01-22 13:15:00+00:00","close_date":"2018-01-22 13:55:00+00:00","open_rate":4.168e-05,"close_rate":4.188892230576441e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":2.0892230576441054e-07,"exit_reason":"roi","initial_stop_loss_abs":3.7512e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.7512e-05,"stop_loss_ratio":0.1,"min_rate":4.168e-05,"max_rate":4.188892230576441e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516626900000.0,"close_timestamp":1516629300000.0},{"pair":"TRX/BTC","stake_amount":0.001,"amount":11.336583153837434,"open_date":"2018-01-22 14:00:00+00:00","close_date":"2018-01-22 14:30:00+00:00","open_rate":8.821e-05,"close_rate":8.953646616541353e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.326466165413529e-06,"exit_reason":"roi","initial_stop_loss_abs":7.9389e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":7.9389e-05,"stop_loss_ratio":0.1,"min_rate":8.821e-05,"max_rate":8.953646616541353e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516629600000.0,"close_timestamp":1516631400000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.334880123743233,"open_date":"2018-01-22 15:55:00+00:00","close_date":"2018-01-22 16:40:00+00:00","open_rate":5.172e-05,"close_rate":5.1979248120300745e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":2.592481203007459e-07,"exit_reason":"roi","initial_stop_loss_abs":4.6548e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6548e-05,"stop_loss_ratio":0.1,"min_rate":5.172e-05,"max_rate":5.1979248120300745e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516636500000.0,"close_timestamp":1516639200000.0},{"pair":"NXT/BTC","stake_amount":0.001,"amount":33.04692663582287,"open_date":"2018-01-22 16:05:00+00:00","close_date":"2018-01-22 16:25:00+00:00","open_rate":3.026e-05,"close_rate":3.101839598997494e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":7.5839598997494e-07,"exit_reason":"roi","initial_stop_loss_abs":2.7234e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.7234e-05,"stop_loss_ratio":0.1,"min_rate":3.026e-05,"max_rate":3.101839598997494e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516637100000.0,"close_timestamp":1516638300000.0},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014156285390713478,"open_date":"2018-01-22 19:50:00+00:00","close_date":"2018-01-23 00:10:00+00:00","open_rate":0.07064,"close_rate":0.07099408521303258,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":260,"profit_ratio":0.0,"profit_abs":0.00035408521303258167,"exit_reason":"roi","initial_stop_loss_abs":0.063576,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.063576,"stop_loss_ratio":0.1,"min_rate":0.07064,"max_rate":0.07099408521303258,"is_open":false,"buy_tag":null,"open_timestamp":1516650600000.0,"close_timestamp":1516666200000.0},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.06080938507725528,"open_date":"2018-01-22 21:25:00+00:00","close_date":"2018-01-22 22:05:00+00:00","open_rate":0.01644483,"close_rate":0.01652726022556391,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":8.243022556390922e-05,"exit_reason":"roi","initial_stop_loss_abs":0.014800347,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.014800347,"stop_loss_ratio":0.1,"min_rate":0.01644483,"max_rate":0.01652726022556391,"is_open":false,"buy_tag":null,"open_timestamp":1516656300000.0,"close_timestamp":1516658700000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":23.08935580697299,"open_date":"2018-01-23 00:05:00+00:00","close_date":"2018-01-23 00:35:00+00:00","open_rate":4.331e-05,"close_rate":4.3961278195488714e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":6.512781954887175e-07,"exit_reason":"roi","initial_stop_loss_abs":3.8979e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.8979e-05,"stop_loss_ratio":0.1,"min_rate":4.331e-05,"max_rate":4.3961278195488714e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516665900000.0,"close_timestamp":1516667700000.0},{"pair":"NXT/BTC","stake_amount":0.001,"amount":31.250000000000004,"open_date":"2018-01-23 01:50:00+00:00","close_date":"2018-01-23 02:15:00+00:00","open_rate":3.2e-05,"close_rate":3.2802005012531326e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":8.020050125313278e-07,"exit_reason":"roi","initial_stop_loss_abs":2.88e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.88e-05,"stop_loss_ratio":0.1,"min_rate":3.2e-05,"max_rate":3.2802005012531326e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516672200000.0,"close_timestamp":1516673700000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010907854156754155,"open_date":"2018-01-23 04:25:00+00:00","close_date":"2018-01-23 05:15:00+00:00","open_rate":0.09167706,"close_rate":0.09213659413533835,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":0.0,"profit_abs":0.0004595341353383492,"exit_reason":"roi","initial_stop_loss_abs":0.08250935400000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.08250935400000001,"stop_loss_ratio":0.1,"min_rate":0.09167706,"max_rate":0.09213659413533835,"is_open":false,"buy_tag":null,"open_timestamp":1516681500000.0,"close_timestamp":1516684500000.0},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014440474918339115,"open_date":"2018-01-23 07:35:00+00:00","close_date":"2018-01-23 09:00:00+00:00","open_rate":0.0692498,"close_rate":0.06959691679197995,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":85,"profit_ratio":0.0,"profit_abs":0.0003471167919799484,"exit_reason":"roi","initial_stop_loss_abs":0.06232482,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.06232482,"stop_loss_ratio":0.1,"min_rate":0.0692498,"max_rate":0.06959691679197995,"is_open":false,"buy_tag":null,"open_timestamp":1516692900000.0,"close_timestamp":1516698000000.0},{"pair":"NXT/BTC","stake_amount":0.001,"amount":31.426775612822127,"open_date":"2018-01-23 10:50:00+00:00","close_date":"2018-01-23 13:05:00+00:00","open_rate":3.182e-05,"close_rate":3.197949874686716e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":135,"profit_ratio":0.0,"profit_abs":1.594987468671663e-07,"exit_reason":"roi","initial_stop_loss_abs":2.8638e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.8638e-05,"stop_loss_ratio":0.1,"min_rate":3.182e-05,"max_rate":3.197949874686716e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516704600000.0,"close_timestamp":1516712700000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.024461839530332683,"open_date":"2018-01-23 11:05:00+00:00","close_date":"2018-01-23 16:05:00+00:00","open_rate":0.04088,"close_rate":0.04108491228070175,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":300,"profit_ratio":-0.0,"profit_abs":0.0002049122807017481,"exit_reason":"roi","initial_stop_loss_abs":0.036792,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.036792,"stop_loss_ratio":0.1,"min_rate":0.04088,"max_rate":0.04108491228070175,"is_open":false,"buy_tag":null,"open_timestamp":1516705500000.0,"close_timestamp":1516723500000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.417475728155342,"open_date":"2018-01-23 14:55:00+00:00","close_date":"2018-01-23 15:35:00+00:00","open_rate":5.15e-05,"close_rate":5.175814536340851e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":2.5814536340851513e-07,"exit_reason":"roi","initial_stop_loss_abs":4.635e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.635e-05,"stop_loss_ratio":0.1,"min_rate":5.15e-05,"max_rate":5.175814536340851e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516719300000.0,"close_timestamp":1516721700000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011023294646713328,"open_date":"2018-01-23 16:35:00+00:00","close_date":"2018-01-24 00:05:00+00:00","open_rate":0.09071698,"close_rate":0.09117170170426064,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":450,"profit_ratio":0.0,"profit_abs":0.00045472170426064107,"exit_reason":"roi","initial_stop_loss_abs":0.081645282,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.081645282,"stop_loss_ratio":0.1,"min_rate":0.09071698,"max_rate":0.09117170170426064,"is_open":false,"buy_tag":null,"open_timestamp":1516725300000.0,"close_timestamp":1516752300000.0},{"pair":"NXT/BTC","stake_amount":0.001,"amount":31.969309462915604,"open_date":"2018-01-23 17:25:00+00:00","close_date":"2018-01-23 18:45:00+00:00","open_rate":3.128e-05,"close_rate":3.1436791979949865e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":-0.0,"profit_abs":1.5679197994986587e-07,"exit_reason":"roi","initial_stop_loss_abs":2.8152e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.8152e-05,"stop_loss_ratio":0.1,"min_rate":3.128e-05,"max_rate":3.1436791979949865e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516728300000.0,"close_timestamp":1516733100000.0},{"pair":"TRX/BTC","stake_amount":0.001,"amount":10.465724751439037,"open_date":"2018-01-23 20:15:00+00:00","close_date":"2018-01-23 22:00:00+00:00","open_rate":9.555e-05,"close_rate":9.602894736842104e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":105,"profit_ratio":-0.0,"profit_abs":4.789473684210343e-07,"exit_reason":"roi","initial_stop_loss_abs":8.5995e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.5995e-05,"stop_loss_ratio":0.1,"min_rate":9.555e-05,"max_rate":9.602894736842104e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516738500000.0,"close_timestamp":1516744800000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02450979791426522,"open_date":"2018-01-23 22:30:00+00:00","close_date":"2018-01-23 23:10:00+00:00","open_rate":0.04080001,"close_rate":0.0410045213283208,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.00020451132832080554,"exit_reason":"roi","initial_stop_loss_abs":0.036720009,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.036720009,"stop_loss_ratio":0.1,"min_rate":0.04080001,"max_rate":0.0410045213283208,"is_open":false,"buy_tag":null,"open_timestamp":1516746600000.0,"close_timestamp":1516749000000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.368584156498162,"open_date":"2018-01-23 23:50:00+00:00","close_date":"2018-01-24 03:35:00+00:00","open_rate":5.163e-05,"close_rate":5.18887969924812e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":225,"profit_ratio":-0.0,"profit_abs":2.587969924812037e-07,"exit_reason":"roi","initial_stop_loss_abs":4.6467e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6467e-05,"stop_loss_ratio":0.1,"min_rate":5.163e-05,"max_rate":5.18887969924812e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516751400000.0,"close_timestamp":1516764900000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.024747691102289384,"open_date":"2018-01-24 00:20:00+00:00","close_date":"2018-01-24 01:50:00+00:00","open_rate":0.04040781,"close_rate":0.04061035541353383,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":90,"profit_ratio":0.0,"profit_abs":0.0002025454135338306,"exit_reason":"roi","initial_stop_loss_abs":0.036367029,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.036367029,"stop_loss_ratio":0.1,"min_rate":0.04040781,"max_rate":0.04061035541353383,"is_open":false,"buy_tag":null,"open_timestamp":1516753200000.0,"close_timestamp":1516758600000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.485580670303975,"open_date":"2018-01-24 06:45:00+00:00","close_date":"2018-01-24 07:25:00+00:00","open_rate":5.132e-05,"close_rate":5.157724310776942e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":2.5724310776941724e-07,"exit_reason":"roi","initial_stop_loss_abs":4.6188000000000006e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6188000000000006e-05,"stop_loss_ratio":0.1,"min_rate":5.132e-05,"max_rate":5.157724310776942e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516776300000.0,"close_timestamp":1516778700000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.23816852635629,"open_date":"2018-01-24 14:15:00+00:00","close_date":"2018-01-24 14:25:00+00:00","open_rate":5.198e-05,"close_rate":5.432496240601503e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":10,"profit_ratio":0.03990025,"profit_abs":2.344962406015033e-06,"exit_reason":"roi","initial_stop_loss_abs":4.6782e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6782e-05,"stop_loss_ratio":0.1,"min_rate":5.198e-05,"max_rate":5.432496240601503e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516803300000.0,"close_timestamp":1516803900000.0},{"pair":"NXT/BTC","stake_amount":0.001,"amount":32.74394237066143,"open_date":"2018-01-24 14:50:00+00:00","close_date":"2018-01-24 16:35:00+00:00","open_rate":3.054e-05,"close_rate":3.069308270676692e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":105,"profit_ratio":-0.0,"profit_abs":1.5308270676691466e-07,"exit_reason":"roi","initial_stop_loss_abs":2.7486000000000004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.7486000000000004e-05,"stop_loss_ratio":0.1,"min_rate":3.054e-05,"max_rate":3.069308270676692e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516805400000.0,"close_timestamp":1516811700000.0},{"pair":"TRX/BTC","stake_amount":0.001,"amount":10.795638562020944,"open_date":"2018-01-24 15:10:00+00:00","close_date":"2018-01-24 16:15:00+00:00","open_rate":9.263e-05,"close_rate":9.309431077694236e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":65,"profit_ratio":0.0,"profit_abs":4.6431077694236234e-07,"exit_reason":"roi","initial_stop_loss_abs":8.3367e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.3367e-05,"stop_loss_ratio":0.1,"min_rate":9.263e-05,"max_rate":9.309431077694236e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516806600000.0,"close_timestamp":1516810500000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":18.13565469713457,"open_date":"2018-01-24 22:40:00+00:00","close_date":"2018-01-24 23:25:00+00:00","open_rate":5.514e-05,"close_rate":5.54163909774436e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":2.7639097744360576e-07,"exit_reason":"roi","initial_stop_loss_abs":4.9625999999999995e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.9625999999999995e-05,"stop_loss_ratio":0.1,"min_rate":5.514e-05,"max_rate":5.54163909774436e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516833600000.0,"close_timestamp":1516836300000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":20.3210729526519,"open_date":"2018-01-25 00:50:00+00:00","close_date":"2018-01-25 01:30:00+00:00","open_rate":4.921e-05,"close_rate":4.9456666666666664e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":2.4666666666666543e-07,"exit_reason":"roi","initial_stop_loss_abs":4.4289e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.4289e-05,"stop_loss_ratio":0.1,"min_rate":4.921e-05,"max_rate":4.9456666666666664e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516841400000.0,"close_timestamp":1516843800000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.38461538461538464,"open_date":"2018-01-25 08:15:00+00:00","close_date":"2018-01-25 12:15:00+00:00","open_rate":0.0026,"close_rate":0.002613032581453634,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":240,"profit_ratio":0.0,"profit_abs":1.3032581453634e-05,"exit_reason":"roi","initial_stop_loss_abs":0.00234,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.00234,"stop_loss_ratio":0.1,"min_rate":0.0026,"max_rate":0.002613032581453634,"is_open":false,"buy_tag":null,"open_timestamp":1516868100000.0,"close_timestamp":1516882500000.0},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03571593119825878,"open_date":"2018-01-25 10:25:00+00:00","close_date":"2018-01-25 16:15:00+00:00","open_rate":0.02799871,"close_rate":0.028139054411027563,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":350,"profit_ratio":-0.0,"profit_abs":0.00014034441102756326,"exit_reason":"roi","initial_stop_loss_abs":0.025198839,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025198839,"stop_loss_ratio":0.1,"min_rate":0.02799871,"max_rate":0.028139054411027563,"is_open":false,"buy_tag":null,"open_timestamp":1516875900000.0,"close_timestamp":1516896900000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.024516401717913302,"open_date":"2018-01-25 11:00:00+00:00","close_date":"2018-01-25 11:45:00+00:00","open_rate":0.04078902,"close_rate":0.0409934762406015,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":0.00020445624060149575,"exit_reason":"roi","initial_stop_loss_abs":0.036710118,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.036710118,"stop_loss_ratio":0.1,"min_rate":0.04078902,"max_rate":0.0409934762406015,"is_open":false,"buy_tag":null,"open_timestamp":1516878000000.0,"close_timestamp":1516880700000.0},{"pair":"NXT/BTC","stake_amount":0.001,"amount":34.602076124567475,"open_date":"2018-01-25 13:05:00+00:00","close_date":"2018-01-25 13:45:00+00:00","open_rate":2.89e-05,"close_rate":2.904486215538847e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":1.4486215538846723e-07,"exit_reason":"roi","initial_stop_loss_abs":2.601e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.601e-05,"stop_loss_ratio":0.1,"min_rate":2.89e-05,"max_rate":2.904486215538847e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516885500000.0,"close_timestamp":1516887900000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02432912439481303,"open_date":"2018-01-25 13:20:00+00:00","close_date":"2018-01-25 14:05:00+00:00","open_rate":0.041103,"close_rate":0.04130903007518797,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":0.00020603007518796984,"exit_reason":"roi","initial_stop_loss_abs":0.0369927,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0369927,"stop_loss_ratio":0.1,"min_rate":0.041103,"max_rate":0.04130903007518797,"is_open":false,"buy_tag":null,"open_timestamp":1516886400000.0,"close_timestamp":1516889100000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":18.422991893883566,"open_date":"2018-01-25 15:45:00+00:00","close_date":"2018-01-25 16:15:00+00:00","open_rate":5.428e-05,"close_rate":5.509624060150376e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":8.162406015037611e-07,"exit_reason":"roi","initial_stop_loss_abs":4.8852000000000006e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.8852000000000006e-05,"stop_loss_ratio":0.1,"min_rate":5.428e-05,"max_rate":5.509624060150376e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516895100000.0,"close_timestamp":1516896900000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":18.47063169560399,"open_date":"2018-01-25 17:45:00+00:00","close_date":"2018-01-25 23:15:00+00:00","open_rate":5.414e-05,"close_rate":5.441137844611528e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":330,"profit_ratio":-0.0,"profit_abs":2.713784461152774e-07,"exit_reason":"roi","initial_stop_loss_abs":4.8726e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.8726e-05,"stop_loss_ratio":0.1,"min_rate":5.414e-05,"max_rate":5.441137844611528e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516902300000.0,"close_timestamp":1516922100000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.024150056861308878,"open_date":"2018-01-25 21:15:00+00:00","close_date":"2018-01-25 21:55:00+00:00","open_rate":0.04140777,"close_rate":0.0416153277443609,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.0002075577443608964,"exit_reason":"roi","initial_stop_loss_abs":0.037266993000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.037266993000000005,"stop_loss_ratio":0.1,"min_rate":0.04140777,"max_rate":0.0416153277443609,"is_open":false,"buy_tag":null,"open_timestamp":1516914900000.0,"close_timestamp":1516917300000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.3932224183965176,"open_date":"2018-01-26 02:05:00+00:00","close_date":"2018-01-26 02:45:00+00:00","open_rate":0.00254309,"close_rate":0.002555837318295739,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":1.2747318295739177e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002288781,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002288781,"stop_loss_ratio":0.1,"min_rate":0.00254309,"max_rate":0.002555837318295739,"is_open":false,"buy_tag":null,"open_timestamp":1516932300000.0,"close_timestamp":1516934700000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":17.834849295523455,"open_date":"2018-01-26 02:55:00+00:00","close_date":"2018-01-26 15:10:00+00:00","open_rate":5.607e-05,"close_rate":5.6351052631578935e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":735,"profit_ratio":-0.0,"profit_abs":2.810526315789381e-07,"exit_reason":"roi","initial_stop_loss_abs":5.0463e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.0463e-05,"stop_loss_ratio":0.1,"min_rate":5.607e-05,"max_rate":5.6351052631578935e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516935300000.0,"close_timestamp":1516979400000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.39400171784748983,"open_date":"2018-01-26 06:10:00+00:00","close_date":"2018-01-26 09:25:00+00:00","open_rate":0.00253806,"close_rate":0.0025507821052631577,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":195,"profit_ratio":0.0,"profit_abs":1.2722105263157733e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002284254,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002284254,"stop_loss_ratio":0.1,"min_rate":0.00253806,"max_rate":0.0025507821052631577,"is_open":false,"buy_tag":null,"open_timestamp":1516947000000.0,"close_timestamp":1516958700000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.024096385542168672,"open_date":"2018-01-26 07:25:00+00:00","close_date":"2018-01-26 09:55:00+00:00","open_rate":0.0415,"close_rate":0.04170802005012531,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":150,"profit_ratio":-0.0,"profit_abs":0.00020802005012530989,"exit_reason":"roi","initial_stop_loss_abs":0.03735,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.03735,"stop_loss_ratio":0.1,"min_rate":0.0415,"max_rate":0.04170802005012531,"is_open":false,"buy_tag":null,"open_timestamp":1516951500000.0,"close_timestamp":1516960500000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":18.793459875963165,"open_date":"2018-01-26 09:55:00+00:00","close_date":"2018-01-26 10:25:00+00:00","open_rate":5.321e-05,"close_rate":5.401015037593984e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":8.00150375939842e-07,"exit_reason":"roi","initial_stop_loss_abs":4.7889e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7889e-05,"stop_loss_ratio":0.1,"min_rate":5.321e-05,"max_rate":5.401015037593984e-05,"is_open":false,"buy_tag":null,"open_timestamp":1516960500000.0,"close_timestamp":1516962300000.0},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.036074437437185386,"open_date":"2018-01-26 16:05:00+00:00","close_date":"2018-01-26 16:45:00+00:00","open_rate":0.02772046,"close_rate":0.02785940967418546,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.00013894967418546025,"exit_reason":"roi","initial_stop_loss_abs":0.024948414,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.024948414,"stop_loss_ratio":0.1,"min_rate":0.02772046,"max_rate":0.02785940967418546,"is_open":false,"buy_tag":null,"open_timestamp":1516982700000.0,"close_timestamp":1516985100000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010569326272036914,"open_date":"2018-01-26 23:35:00+00:00","close_date":"2018-01-27 00:15:00+00:00","open_rate":0.09461341,"close_rate":0.09508766268170424,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":0.00047425268170424306,"exit_reason":"roi","initial_stop_loss_abs":0.085152069,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.085152069,"stop_loss_ratio":0.1,"min_rate":0.09461341,"max_rate":0.09508766268170424,"is_open":false,"buy_tag":null,"open_timestamp":1517009700000.0,"close_timestamp":1517012100000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":17.809439002671414,"open_date":"2018-01-27 00:35:00+00:00","close_date":"2018-01-27 01:30:00+00:00","open_rate":5.615e-05,"close_rate":5.643145363408521e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":2.814536340852038e-07,"exit_reason":"roi","initial_stop_loss_abs":5.0535e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.0535e-05,"stop_loss_ratio":0.1,"min_rate":5.615e-05,"max_rate":5.643145363408521e-05,"is_open":false,"buy_tag":null,"open_timestamp":1517013300000.0,"close_timestamp":1517016600000.0},{"pair":"ADA/BTC","stake_amount":0.001,"amount":17.998560115190784,"open_date":"2018-01-27 00:45:00+00:00","close_date":"2018-01-30 04:45:00+00:00","open_rate":5.556e-05,"close_rate":5.144e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":4560,"profit_ratio":-0.07877175,"profit_abs":-4.120000000000001e-06,"exit_reason":"force_exit","initial_stop_loss_abs":5.0004000000000004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.0004000000000004e-05,"stop_loss_ratio":0.1,"min_rate":5.144e-05,"max_rate":5.556e-05,"is_open":false,"buy_tag":null,"open_timestamp":1517013900000.0,"close_timestamp":1517287500000.0},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014492751522789634,"open_date":"2018-01-27 02:30:00+00:00","close_date":"2018-01-27 11:25:00+00:00","open_rate":0.06900001,"close_rate":0.06934587471177944,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":535,"profit_ratio":-0.0,"profit_abs":0.0003458647117794422,"exit_reason":"roi","initial_stop_loss_abs":0.062100009000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.062100009000000005,"stop_loss_ratio":0.1,"min_rate":0.06900001,"max_rate":0.06934587471177944,"is_open":false,"buy_tag":null,"open_timestamp":1517020200000.0,"close_timestamp":1517052300000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010582027378879437,"open_date":"2018-01-27 06:25:00+00:00","close_date":"2018-01-27 07:05:00+00:00","open_rate":0.09449985,"close_rate":0.0949735334586466,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.0004736834586466093,"exit_reason":"roi","initial_stop_loss_abs":0.085049865,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.085049865,"stop_loss_ratio":0.1,"min_rate":0.09449985,"max_rate":0.0949735334586466,"is_open":false,"buy_tag":null,"open_timestamp":1517034300000.0,"close_timestamp":1517036700000.0},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02434885085598385,"open_date":"2018-01-27 09:40:00+00:00","close_date":"2018-01-30 04:40:00+00:00","open_rate":0.0410697,"close_rate":0.03928809,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":4020,"profit_ratio":-0.04815133,"profit_abs":-0.001781610000000003,"exit_reason":"force_exit","initial_stop_loss_abs":0.03696273,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.03696273,"stop_loss_ratio":0.1,"min_rate":0.03928809,"max_rate":0.0410697,"is_open":false,"buy_tag":null,"open_timestamp":1517046000000.0,"close_timestamp":1517287200000.0},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03508771929824561,"open_date":"2018-01-27 11:45:00+00:00","close_date":"2018-01-27 12:30:00+00:00","open_rate":0.0285,"close_rate":0.02864285714285714,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":0.00014285714285713902,"exit_reason":"roi","initial_stop_loss_abs":0.025650000000000003,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025650000000000003,"stop_loss_ratio":0.1,"min_rate":0.0285,"max_rate":0.02864285714285714,"is_open":false,"buy_tag":null,"open_timestamp":1517053500000.0,"close_timestamp":1517056200000.0},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.034887307020861215,"open_date":"2018-01-27 12:35:00+00:00","close_date":"2018-01-27 15:25:00+00:00","open_rate":0.02866372,"close_rate":0.02880739779448621,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":170,"profit_ratio":-0.0,"profit_abs":0.00014367779448621124,"exit_reason":"roi","initial_stop_loss_abs":0.025797348,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025797348,"stop_loss_ratio":0.1,"min_rate":0.02866372,"max_rate":0.02880739779448621,"is_open":false,"buy_tag":null,"open_timestamp":1517056500000.0,"close_timestamp":1517066700000.0},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010484268355332824,"open_date":"2018-01-27 15:50:00+00:00","close_date":"2018-01-27 16:50:00+00:00","open_rate":0.095381,"close_rate":0.09585910025062656,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":-0.0,"profit_abs":0.00047810025062657024,"exit_reason":"roi","initial_stop_loss_abs":0.0858429,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0858429,"stop_loss_ratio":0.1,"min_rate":0.095381,"max_rate":0.09585910025062656,"is_open":false,"buy_tag":null,"open_timestamp":1517068200000.0,"close_timestamp":1517071800000.0},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014794886650455417,"open_date":"2018-01-27 17:05:00+00:00","close_date":"2018-01-27 17:45:00+00:00","open_rate":0.06759092,"close_rate":0.06792972160401002,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.00033880160401002224,"exit_reason":"roi","initial_stop_loss_abs":0.060831828,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.060831828,"stop_loss_ratio":0.1,"min_rate":0.06759092,"max_rate":0.06792972160401002,"is_open":false,"buy_tag":null,"open_timestamp":1517072700000.0,"close_timestamp":1517075100000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.38684569885609726,"open_date":"2018-01-27 23:40:00+00:00","close_date":"2018-01-28 01:05:00+00:00","open_rate":0.00258501,"close_rate":0.002597967443609022,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":85,"profit_ratio":-0.0,"profit_abs":1.2957443609021985e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002326509,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002326509,"stop_loss_ratio":0.1,"min_rate":0.00258501,"max_rate":0.002597967443609022,"is_open":false,"buy_tag":null,"open_timestamp":1517096400000.0,"close_timestamp":1517101500000.0},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014928710926711672,"open_date":"2018-01-28 02:25:00+00:00","close_date":"2018-01-28 08:10:00+00:00","open_rate":0.06698502,"close_rate":0.0673207845112782,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":345,"profit_ratio":-0.0,"profit_abs":0.00033576451127818874,"exit_reason":"roi","initial_stop_loss_abs":0.060286518000000004,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.060286518000000004,"stop_loss_ratio":0.1,"min_rate":0.06698502,"max_rate":0.0673207845112782,"is_open":false,"buy_tag":null,"open_timestamp":1517106300000.0,"close_timestamp":1517127000000.0},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014767187899175548,"open_date":"2018-01-28 10:25:00+00:00","close_date":"2018-01-28 16:30:00+00:00","open_rate":0.0677177,"close_rate":0.06805713709273183,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":365,"profit_ratio":-0.0,"profit_abs":0.0003394370927318202,"exit_reason":"roi","initial_stop_loss_abs":0.06094593000000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.06094593000000001,"stop_loss_ratio":0.1,"min_rate":0.0677177,"max_rate":0.06805713709273183,"is_open":false,"buy_tag":null,"open_timestamp":1517135100000.0,"close_timestamp":1517157000000.0},{"pair":"XLM/BTC","stake_amount":0.001,"amount":19.175455417066157,"open_date":"2018-01-28 20:35:00+00:00","close_date":"2018-01-28 21:35:00+00:00","open_rate":5.215e-05,"close_rate":5.2411403508771925e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":0.0,"profit_abs":2.6140350877192417e-07,"exit_reason":"roi","initial_stop_loss_abs":4.6935000000000004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6935000000000004e-05,"stop_loss_ratio":0.1,"min_rate":5.215e-05,"max_rate":5.2411403508771925e-05,"is_open":false,"buy_tag":null,"open_timestamp":1517171700000.0,"close_timestamp":1517175300000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.36521808998243305,"open_date":"2018-01-28 22:00:00+00:00","close_date":"2018-01-28 22:30:00+00:00","open_rate":0.00273809,"close_rate":0.002779264285714285,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":4.117428571428529e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002464281,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002464281,"stop_loss_ratio":0.1,"min_rate":0.00273809,"max_rate":0.002779264285714285,"is_open":false,"buy_tag":null,"open_timestamp":1517176800000.0,"close_timestamp":1517178600000.0},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.3641236272539253,"open_date":"2018-01-29 00:00:00+00:00","close_date":"2018-01-29 00:30:00+00:00","open_rate":0.00274632,"close_rate":0.002787618045112782,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":4.129804511278194e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002471688,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002471688,"stop_loss_ratio":0.1,"min_rate":0.00274632,"max_rate":0.002787618045112782,"is_open":false,"buy_tag":null,"open_timestamp":1517184000000.0,"close_timestamp":1517185800000.0},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.061634117689115045,"open_date":"2018-01-29 02:15:00+00:00","close_date":"2018-01-29 03:00:00+00:00","open_rate":0.01622478,"close_rate":0.016306107218045113,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":0.0,"profit_abs":8.132721804511231e-05,"exit_reason":"roi","initial_stop_loss_abs":0.014602302000000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.014602302000000001,"stop_loss_ratio":0.1,"min_rate":0.01622478,"max_rate":0.016306107218045113,"is_open":false,"buy_tag":null,"open_timestamp":1517192100000.0,"close_timestamp":1517194800000.0},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014492753623188404,"open_date":"2018-01-29 03:05:00+00:00","close_date":"2018-01-29 03:45:00+00:00","open_rate":0.069,"close_rate":0.06934586466165413,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.00034586466165412166,"exit_reason":"roi","initial_stop_loss_abs":0.06210000000000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.06210000000000001,"stop_loss_ratio":0.1,"min_rate":0.069,"max_rate":0.06934586466165413,"is_open":false,"buy_tag":null,"open_timestamp":1517195100000.0,"close_timestamp":1517197500000.0},{"pair":"TRX/BTC","stake_amount":0.001,"amount":11.42204454597373,"open_date":"2018-01-29 05:20:00+00:00","close_date":"2018-01-29 06:55:00+00:00","open_rate":8.755e-05,"close_rate":8.798884711779448e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":95,"profit_ratio":-0.0,"profit_abs":4.3884711779447504e-07,"exit_reason":"roi","initial_stop_loss_abs":7.879500000000001e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":7.879500000000001e-05,"stop_loss_ratio":0.1,"min_rate":8.755e-05,"max_rate":8.798884711779448e-05,"is_open":false,"buy_tag":null,"open_timestamp":1517203200000.0,"close_timestamp":1517208900000.0},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014650376815016871,"open_date":"2018-01-29 07:00:00+00:00","close_date":"2018-01-29 19:25:00+00:00","open_rate":0.06825763,"close_rate":0.06859977350877192,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":745,"profit_ratio":-0.0,"profit_abs":0.00034214350877191657,"exit_reason":"roi","initial_stop_loss_abs":0.061431867,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.061431867,"stop_loss_ratio":0.1,"min_rate":0.06825763,"max_rate":0.06859977350877192,"is_open":false,"buy_tag":null,"open_timestamp":1517209200000.0,"close_timestamp":1517253900000.0},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014894490408841846,"open_date":"2018-01-29 19:45:00+00:00","close_date":"2018-01-29 20:25:00+00:00","open_rate":0.06713892,"close_rate":0.06747545593984962,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":0.0003365359398496137,"exit_reason":"roi","initial_stop_loss_abs":0.060425028000000006,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.060425028000000006,"stop_loss_ratio":0.1,"min_rate":0.06713892,"max_rate":0.06747545593984962,"is_open":false,"buy_tag":null,"open_timestamp":1517255100000.0,"close_timestamp":1517257500000.0},{"pair":"TRX/BTC","stake_amount":0.001,"amount":11.193194537721066,"open_date":"2018-01-29 23:30:00+00:00","close_date":"2018-01-30 04:45:00+00:00","open_rate":8.934e-05,"close_rate":8.8e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":315,"profit_ratio":-0.0199116,"profit_abs":-1.3399999999999973e-06,"exit_reason":"force_exit","initial_stop_loss_abs":8.0406e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.0406e-05,"stop_loss_ratio":0.1,"min_rate":8.8e-05,"max_rate":8.934e-05,"is_open":false,"buy_tag":null,"open_timestamp":1517268600000.0,"close_timestamp":1517287500000.0}],"locks":[],"best_pair":{"key":"LTC/BTC","trades":8,"profit_mean":0.00748129625,"profit_mean_pct":0.748129625,"profit_sum":0.05985037,"profit_sum_pct":5.99,"profit_total_abs":0.0015944746365914707,"profit_total":0.15944746365914708,"profit_total_pct":15.94,"duration_avg":"1:59:00","wins":8,"draws":0,"losses":0},"worst_pair":{"key":"XMR/BTC","trades":16,"profit_mean":-0.0027899012500000007,"profit_mean_pct":-0.2789901250000001,"profit_sum":-0.04463842000000001,"profit_sum_pct":-4.46,"profit_total_abs":0.0006671885263157366,"profit_total":0.06671885263157366,"profit_total_pct":6.67,"duration_avg":"8:41:00","wins":15,"draws":0,"losses":1},"results_per_pair":[{"key":"ETH/BTC","trades":21,"profit_mean":0.0009500057142857142,"profit_mean_pct":0.09500057142857142,"profit_sum":0.01995012,"profit_sum_pct":2.0,"profit_total_abs":0.011505731278195264,"profit_total":1.1505731278195264,"profit_total_pct":115.06,"duration_avg":"2:17:00","wins":21,"draws":0,"losses":0},{"key":"DASH/BTC","trades":16,"profit_mean":0.0018703237499999997,"profit_mean_pct":0.18703237499999997,"profit_sum":0.029925179999999996,"profit_sum_pct":2.99,"profit_total_abs":0.007475052681704161,"profit_total":0.7475052681704161,"profit_total_pct":74.75,"duration_avg":"3:03:00","wins":16,"draws":0,"losses":0},{"key":"ZEC/BTC","trades":21,"profit_mean":-0.00039290904761904774,"profit_mean_pct":-0.03929090476190478,"profit_sum":-0.008251090000000003,"profit_sum_pct":-0.83,"profit_total_abs":0.004452605639097655,"profit_total":0.4452605639097655,"profit_total_pct":44.53,"duration_avg":"4:17:00","wins":20,"draws":0,"losses":1},{"key":"LTC/BTC","trades":8,"profit_mean":0.00748129625,"profit_mean_pct":0.748129625,"profit_sum":0.05985037,"profit_sum_pct":5.99,"profit_total_abs":0.0015944746365914707,"profit_total":0.15944746365914708,"profit_total_pct":15.94,"duration_avg":"1:59:00","wins":8,"draws":0,"losses":0},{"key":"XMR/BTC","trades":16,"profit_mean":-0.0027899012500000007,"profit_mean_pct":-0.2789901250000001,"profit_sum":-0.04463842000000001,"profit_sum_pct":-4.46,"profit_total_abs":0.0006671885263157366,"profit_total":0.06671885263157366,"profit_total_pct":6.67,"duration_avg":"8:41:00","wins":15,"draws":0,"losses":1},{"key":"ETC/BTC","trades":20,"profit_mean":0.0022568569999999997,"profit_mean_pct":0.22568569999999996,"profit_sum":0.04513713999999999,"profit_sum_pct":4.51,"profit_total_abs":0.00036538235338345404,"profit_total":0.0365382353383454,"profit_total_pct":3.65,"duration_avg":"1:45:00","wins":19,"draws":0,"losses":1},{"key":"TRX/BTC","trades":15,"profit_mean":0.0023467073333333323,"profit_mean_pct":0.23467073333333321,"profit_sum":0.035200609999999986,"profit_sum_pct":3.52,"profit_total_abs":1.1329523809523682e-05,"profit_total":0.0011329523809523682,"profit_total_pct":0.11,"duration_avg":"2:28:00","wins":13,"draws":0,"losses":2},{"key":"XLM/BTC","trades":21,"profit_mean":0.0026243899999999994,"profit_mean_pct":0.2624389999999999,"profit_sum":0.05511218999999999,"profit_sum_pct":5.51,"profit_total_abs":7.340779448621465e-06,"profit_total":0.0007340779448621465,"profit_total_pct":0.07,"duration_avg":"3:21:00","wins":20,"draws":0,"losses":1},{"key":"ADA/BTC","trades":29,"profit_mean":-0.0011598141379310352,"profit_mean_pct":-0.11598141379310352,"profit_sum":-0.03363461000000002,"profit_sum_pct":-3.36,"profit_total_abs":4.916634085212862e-06,"profit_total":0.0004916634085212862,"profit_total_pct":0.05,"duration_avg":"5:35:00","wins":27,"draws":0,"losses":2},{"key":"NXT/BTC","trades":12,"profit_mean":-0.0012261025000000006,"profit_mean_pct":-0.12261025000000006,"profit_sum":-0.014713230000000008,"profit_sum_pct":-1.47,"profit_total_abs":1.4774411027568458e-06,"profit_total":0.00014774411027568458,"profit_total_pct":0.01,"duration_avg":"0:57:00","wins":11,"draws":0,"losses":1},{"key":"TOTAL","trades":179,"profit_mean":0.0008041243575418989,"profit_mean_pct":0.0804124357541899,"profit_sum":0.1439382599999999,"profit_sum_pct":14.39,"profit_total_abs":0.026085499493733857,"profit_total":2.6085499493733857,"profit_total_pct":260.85,"duration_avg":"3:40:00","wins":170,"draws":0,"losses":9}],"results_per_enter_tag":[{"key":"TOTAL","trades":179,"profit_mean":0.0008041243575418989,"profit_mean_pct":0.0804124357541899,"profit_sum":0.1439382599999999,"profit_sum_pct":14.39,"profit_total_abs":0.026085499493733857,"profit_total":2.6085499493733857,"profit_total_pct":260.85,"duration_avg":"3:40:00","wins":170,"draws":0,"losses":9}],"exit_reason_summary":[{"exit_reason":"roi","trades":170,"wins":170,"draws":0,"losses":0,"profit_mean":0.005398268352941177,"profit_mean_pct":0.54,"profit_sum":0.91770562,"profit_sum_pct":91.77,"profit_total_abs":0.031232837493733862,"profit_total":0.30590187333333335,"profit_total_pct":30.59},{"exit_reason":"stop_loss","trades":6,"wins":0,"draws":0,"losses":6,"profit_mean":-0.10448878000000002,"profit_mean_pct":-10.45,"profit_sum":-0.6269326800000001,"profit_sum_pct":-62.69,"profit_total_abs":-0.0033602680000000026,"profit_total":-0.20897756000000003,"profit_total_pct":-20.9},{"exit_reason":"force_exit","trades":3,"wins":0,"draws":0,"losses":3,"profit_mean":-0.04894489333333333,"profit_mean_pct":-4.89,"profit_sum":-0.14683468,"profit_sum_pct":-14.68,"profit_total_abs":-0.001787070000000003,"profit_total":-0.04894489333333333,"profit_total_pct":-4.89}],"left_open_trades":[{"key":"TOTAL","trades":0,"profit_mean":0.0,"profit_mean_pct":0.0,"profit_sum":0.0,"profit_sum_pct":0.0,"profit_total_abs":0.0,"profit_total":0.0,"profit_total_pct":0.0,"duration_avg":"0:00","wins":0,"draws":0,"losses":0}],"total_trades":179,"total_volume":0.17900000000000005,"avg_stake_amount":0.0010000000000000002,"profit_mean":0.0008041243575418989,"profit_median":0.0,"profit_total":2.6085499493733857,"profit_total_abs":0.026085499493733857,"backtest_start":"2018-01-10 07:15:00","backtest_start_ts":1515568500000,"backtest_end":"2018-01-30 04:45:00","backtest_end_ts":1517287500000,"backtest_days":19,"backtest_run_start_ts":"2020-10-01 18:00:00+00:00","backtest_run_end_ts":"2020-10-01 18:01:00+00:00","trades_per_day":9.42,"market_change":1.22,"pairlist":[],"stake_amount":0.001,"stake_currency":"BTC","stake_currency_decimals":8,"starting_balance":0.01,"dry_run_wallet":0.01,"final_balance":0.03608549949373386,"rejected_signals":0,"max_open_trades":3,"max_open_trades_setting":3,"timeframe":"5m","timeframe_detail":"","timerange":"","enable_protections":false,"strategy_name":"StrategyTestV2","stoploss":0.1,"trailing_stop":false,"trailing_stop_positive":null,"trailing_stop_positive_offset":0.0,"trailing_only_offset_is_reached":false,"use_custom_stoploss":false,"minimal_roi":{},"use_exit_signal":true,"exit_profit_only":false,"exit_profit_offset":false,"ignore_roi_if_entry_signal":false,"backtest_best_day":0.17955111999999998,"backtest_worst_day":-0.14683468,"backtest_best_day_abs":0.0071570099,"backtest_worst_day_abs":-0.0023093218,"winning_days":19,"draw_days":0,"losing_days":2,"daily_profit":[["2018-01-10",0.0025815306],["2018-01-11",0.0049356655],["2018-01-12",0.0006395218],["2018-01-13",0.0002574589],["2018-01-14",0.0010443828],["2018-01-15",0.0024030209],["2018-01-16",0.0071570099],["2018-01-17",0.001137038],["2018-01-18",0.0013712174],["2018-01-19",0.000584673],["2018-01-20",0.0006143386],["2018-01-21",0.0004749361],["2018-01-22",9.91669e-05],["2018-01-23",0.0015726664],["2018-01-24",0.0006610219],["2018-01-25",-0.0023093218],["2018-01-26",0.0003735204],["2018-01-27",0.0023975191],["2018-01-28",0.0007295947],["2018-01-29",0.0011476082],["2018-01-30",-0.00178707]],"wins":48,"losses":9,"draws":122,"holding_avg":"3:40:00","holding_avg_s":13200.0,"winner_holding_avg":"0:24:00","winner_holding_avg_s":1440.0,"loser_holding_avg":"1 day, 5:57:00","loser_holding_avg_s":107820.0,"max_drawdown":0.21142322000000008,"max_drawdown_account":0.08674033488183289,"max_drawdown_abs":0.0030822220000000025,"drawdown_start":"2018-01-25 01:30:00","drawdown_start_ts":1516843800000.0,"drawdown_end":"2018-01-25 03:50:00","drawdown_end_ts":1516852200000.0,"max_drawdown_low":0.02245167355388436,"max_drawdown_high":0.025533895553884363,"csum_min":0.01000434887218045,"csum_max":0.03608683949373386}},"strategy_comparison":[{"key":"StrategyTestV2","trades":179,"profit_mean":0.0008041243575418989,"profit_mean_pct":0.0804124357541899,"profit_sum":0.1439382599999999,"profit_sum_pct":14.39,"profit_total_abs":0.026085499493733857,"profit_total":2.6085499493733857,"profit_total_pct":260.85,"duration_avg":"3:40:00","wins":170,"draws":0,"losses":9,"max_drawdown_account":0.08674033488183289,"max_drawdown_abs":"0.00308222"}]} +{"metadata":{"StrategyTestV3":{"run_id":"asdf","backtest_start_time":"2020-10-01 18:00:00+00:00"}},"strategy":{"StrategyTestV3":{"trades":[{"pair":"TRX/BTC","stake_amount":0.001,"amount":10.37344398340249,"open_date":"2018-01-10 07:15:00+00:00","close_date":"2018-01-10 07:20:00+00:00","open_rate":9.64e-05,"close_rate":0.00010074887218045112,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":8.676e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.676e-05,"stop_loss_ratio":0.1,"min_rate":9.64e-05,"max_rate":0.00010074887218045112,"is_open":false,"open_timestamp":1515568500000.0,"close_timestamp":1515568800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":21.026072329688816,"open_date":"2018-01-10 07:15:00+00:00","close_date":"2018-01-10 07:30:00+00:00","open_rate":4.756e-05,"close_rate":4.9705563909774425e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":15,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":4.2804e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.2804e-05,"stop_loss_ratio":0.1,"min_rate":4.756e-05,"max_rate":4.9705563909774425e-05,"is_open":false,"open_timestamp":1515568500000.0,"close_timestamp":1515569400000.0,"is_short":false,"leverage":1.0,"enter_tag":"buy_tag","orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":29.94908655286014,"open_date":"2018-01-10 07:25:00+00:00","close_date":"2018-01-10 07:35:00+00:00","open_rate":3.339e-05,"close_rate":3.489631578947368e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":10,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":3.0050999999999997e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.0050999999999997e-05,"stop_loss_ratio":0.1,"min_rate":3.339e-05,"max_rate":3.489631578947368e-05,"is_open":false,"open_timestamp":1515569100000.0,"close_timestamp":1515569700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":10.313531353135314,"open_date":"2018-01-10 07:25:00+00:00","close_date":"2018-01-10 07:40:00+00:00","open_rate":9.696e-05,"close_rate":0.00010133413533834584,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":15,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":8.7264e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.7264e-05,"stop_loss_ratio":0.1,"min_rate":9.696e-05,"max_rate":0.00010133413533834584,"is_open":false,"open_timestamp":1515569100000.0,"close_timestamp":1515570000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010604453870625663,"open_date":"2018-01-10 07:35:00+00:00","close_date":"2018-01-10 08:35:00+00:00","open_rate":0.0943,"close_rate":0.09477268170426063,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.08487,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.08487,"stop_loss_ratio":0.1,"min_rate":0.0943,"max_rate":0.09477268170426063,"is_open":false,"open_timestamp":1515569700000.0,"close_timestamp":1515573300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03677001860930642,"open_date":"2018-01-10 07:40:00+00:00","close_date":"2018-01-10 08:10:00+00:00","open_rate":0.02719607,"close_rate":0.02760503345864661,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.024476463,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.024476463,"stop_loss_ratio":0.1,"min_rate":0.02719607,"max_rate":0.02760503345864661,"is_open":false,"open_timestamp":1515570000000.0,"close_timestamp":1515571800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.021575196463739,"open_date":"2018-01-10 08:15:00+00:00","close_date":"2018-01-10 09:55:00+00:00","open_rate":0.04634952,"close_rate":0.046581848421052625,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":100,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.041714568,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.041714568,"stop_loss_ratio":0.1,"min_rate":0.04634952,"max_rate":0.046581848421052625,"is_open":false,"open_timestamp":1515572100000.0,"close_timestamp":1515578100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":32.615786040443574,"open_date":"2018-01-10 14:45:00+00:00","close_date":"2018-01-10 15:50:00+00:00","open_rate":3.066e-05,"close_rate":3.081368421052631e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":65,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":2.7594e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.7594e-05,"stop_loss_ratio":0.1,"min_rate":3.066e-05,"max_rate":3.081368421052631e-05,"is_open":false,"open_timestamp":1515595500000.0,"close_timestamp":1515599400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.05917194776300452,"open_date":"2018-01-10 16:35:00+00:00","close_date":"2018-01-10 17:15:00+00:00","open_rate":0.0168999,"close_rate":0.016984611278195488,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.01520991,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.01520991,"stop_loss_ratio":0.1,"min_rate":0.0168999,"max_rate":0.016984611278195488,"is_open":false,"open_timestamp":1515602100000.0,"close_timestamp":1515604500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010949822656672253,"open_date":"2018-01-10 16:40:00+00:00","close_date":"2018-01-10 17:20:00+00:00","open_rate":0.09132568,"close_rate":0.0917834528320802,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.08219311200000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.08219311200000001,"stop_loss_ratio":0.1,"min_rate":0.09132568,"max_rate":0.0917834528320802,"is_open":false,"open_timestamp":1515602400000.0,"close_timestamp":1515604800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011238476768326556,"open_date":"2018-01-10 18:50:00+00:00","close_date":"2018-01-10 19:45:00+00:00","open_rate":0.08898003,"close_rate":0.08942604518796991,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.080082027,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.080082027,"stop_loss_ratio":0.1,"min_rate":0.08898003,"max_rate":0.08942604518796991,"is_open":false,"open_timestamp":1515610200000.0,"close_timestamp":1515613500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011682232072680309,"open_date":"2018-01-10 22:15:00+00:00","close_date":"2018-01-10 23:00:00+00:00","open_rate":0.08560008,"close_rate":0.08602915308270676,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.077040072,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.077040072,"stop_loss_ratio":0.1,"min_rate":0.08560008,"max_rate":0.08602915308270676,"is_open":false,"open_timestamp":1515622500000.0,"close_timestamp":1515625200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.4014726015023105,"open_date":"2018-01-10 22:50:00+00:00","close_date":"2018-01-10 23:20:00+00:00","open_rate":0.00249083,"close_rate":0.0025282860902255634,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002241747,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002241747,"stop_loss_ratio":0.1,"min_rate":0.00249083,"max_rate":0.0025282860902255634,"is_open":false,"open_timestamp":1515624600000.0,"close_timestamp":1515626400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":33.090668431502316,"open_date":"2018-01-10 23:15:00+00:00","close_date":"2018-01-11 00:15:00+00:00","open_rate":3.022e-05,"close_rate":3.037147869674185e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":2.7198e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.7198e-05,"stop_loss_ratio":0.1,"min_rate":3.022e-05,"max_rate":3.037147869674185e-05,"is_open":false,"open_timestamp":1515626100000.0,"close_timestamp":1515629700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.41034058268362744,"open_date":"2018-01-10 23:40:00+00:00","close_date":"2018-01-11 00:05:00+00:00","open_rate":0.002437,"close_rate":0.0024980776942355883,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":0.0021933,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0021933,"stop_loss_ratio":0.1,"min_rate":0.002437,"max_rate":0.0024980776942355883,"is_open":false,"open_timestamp":1515627600000.0,"close_timestamp":1515629100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02095643931654345,"open_date":"2018-01-11 00:00:00+00:00","close_date":"2018-01-11 00:35:00+00:00","open_rate":0.04771803,"close_rate":0.04843559436090225,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.042946227,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.042946227,"stop_loss_ratio":0.1,"min_rate":0.04771803,"max_rate":0.04843559436090225,"is_open":false,"open_timestamp":1515628800000.0,"close_timestamp":1515630900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":27.389756231169542,"open_date":"2018-01-11 03:40:00+00:00","close_date":"2018-01-11 04:25:00+00:00","open_rate":3.651e-05,"close_rate":3.2859000000000005e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.10448878,"profit_abs":-9.999999999999994e-05,"exit_reason":"stop_loss","initial_stop_loss_abs":3.2859000000000005e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.2859000000000005e-05,"stop_loss_ratio":0.1,"min_rate":3.2859000000000005e-05,"max_rate":3.651e-05,"is_open":false,"open_timestamp":1515642000000.0,"close_timestamp":1515644700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011332594070446804,"open_date":"2018-01-11 03:55:00+00:00","close_date":"2018-01-11 04:25:00+00:00","open_rate":0.08824105,"close_rate":0.08956798308270676,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.079416945,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.079416945,"stop_loss_ratio":0.1,"min_rate":0.08824105,"max_rate":0.08956798308270676,"is_open":false,"open_timestamp":1515642900000.0,"close_timestamp":1515644700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.411522633744856,"open_date":"2018-01-11 04:00:00+00:00","close_date":"2018-01-11 04:50:00+00:00","open_rate":0.00243,"close_rate":0.002442180451127819,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002187,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002187,"stop_loss_ratio":0.1,"min_rate":0.00243,"max_rate":0.002442180451127819,"is_open":false,"open_timestamp":1515643200000.0,"close_timestamp":1515646200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.022001890402423376,"open_date":"2018-01-11 04:30:00+00:00","close_date":"2018-01-11 04:55:00+00:00","open_rate":0.04545064,"close_rate":0.046589753784461146,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":0.040905576,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.040905576,"stop_loss_ratio":0.1,"min_rate":0.04545064,"max_rate":0.046589753784461146,"is_open":false,"open_timestamp":1515645000000.0,"close_timestamp":1515646500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":29.655990510083036,"open_date":"2018-01-11 04:30:00+00:00","close_date":"2018-01-11 04:50:00+00:00","open_rate":3.372e-05,"close_rate":3.456511278195488e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":3.0348e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.0348e-05,"stop_loss_ratio":0.1,"min_rate":3.372e-05,"max_rate":3.456511278195488e-05,"is_open":false,"open_timestamp":1515645000000.0,"close_timestamp":1515646200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.037821482602118005,"open_date":"2018-01-11 04:55:00+00:00","close_date":"2018-01-11 05:15:00+00:00","open_rate":0.02644,"close_rate":0.02710265664160401,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":0.023796,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.023796,"stop_loss_ratio":0.1,"min_rate":0.02644,"max_rate":0.02710265664160401,"is_open":false,"open_timestamp":1515646500000.0,"close_timestamp":1515647700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011348161597821153,"open_date":"2018-01-11 11:20:00+00:00","close_date":"2018-01-11 12:00:00+00:00","open_rate":0.08812,"close_rate":0.08856170426065162,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.079308,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.079308,"stop_loss_ratio":0.1,"min_rate":0.08812,"max_rate":0.08856170426065162,"is_open":false,"open_timestamp":1515669600000.0,"close_timestamp":1515672000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.037263696923919086,"open_date":"2018-01-11 11:35:00+00:00","close_date":"2018-01-11 12:15:00+00:00","open_rate":0.02683577,"close_rate":0.026970285137844607,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.024152193,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.024152193,"stop_loss_ratio":0.1,"min_rate":0.02683577,"max_rate":0.026970285137844607,"is_open":false,"open_timestamp":1515670500000.0,"close_timestamp":1515672900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":20.329335230737954,"open_date":"2018-01-11 14:00:00+00:00","close_date":"2018-01-11 14:25:00+00:00","open_rate":4.919e-05,"close_rate":5.04228320802005e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":4.4271e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.4271e-05,"stop_loss_ratio":0.1,"min_rate":4.919e-05,"max_rate":5.04228320802005e-05,"is_open":false,"open_timestamp":1515679200000.0,"close_timestamp":1515680700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.01138317402960718,"open_date":"2018-01-11 19:25:00+00:00","close_date":"2018-01-11 20:35:00+00:00","open_rate":0.08784896,"close_rate":0.08828930566416039,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":70,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.079064064,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.079064064,"stop_loss_ratio":0.1,"min_rate":0.08784896,"max_rate":0.08828930566416039,"is_open":false,"open_timestamp":1515698700000.0,"close_timestamp":1515702900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.58863858961802,"open_date":"2018-01-11 22:35:00+00:00","close_date":"2018-01-11 23:30:00+00:00","open_rate":5.105e-05,"close_rate":5.130588972431077e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.5945e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.5945e-05,"stop_loss_ratio":0.1,"min_rate":5.105e-05,"max_rate":5.130588972431077e-05,"is_open":false,"open_timestamp":1515710100000.0,"close_timestamp":1515713400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":25.252525252525253,"open_date":"2018-01-11 22:55:00+00:00","close_date":"2018-01-11 23:25:00+00:00","open_rate":3.96e-05,"close_rate":4.019548872180451e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":3.5640000000000004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.5640000000000004e-05,"stop_loss_ratio":0.1,"min_rate":3.96e-05,"max_rate":4.019548872180451e-05,"is_open":false,"open_timestamp":1515711300000.0,"close_timestamp":1515713100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":34.66204506065858,"open_date":"2018-01-11 22:55:00+00:00","close_date":"2018-01-11 23:35:00+00:00","open_rate":2.885e-05,"close_rate":2.899461152882205e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":2.5965e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.5965e-05,"stop_loss_ratio":0.1,"min_rate":2.885e-05,"max_rate":2.899461152882205e-05,"is_open":false,"open_timestamp":1515711300000.0,"close_timestamp":1515713700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03780718336483932,"open_date":"2018-01-11 23:30:00+00:00","close_date":"2018-01-12 00:05:00+00:00","open_rate":0.02645,"close_rate":0.026847744360902256,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":0.023805000000000003,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.023805000000000003,"stop_loss_ratio":0.1,"min_rate":0.02645,"max_rate":0.026847744360902256,"is_open":false,"open_timestamp":1515713400000.0,"close_timestamp":1515715500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.020833333333333332,"open_date":"2018-01-11 23:55:00+00:00","close_date":"2018-01-12 01:15:00+00:00","open_rate":0.048,"close_rate":0.04824060150375939,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0432,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0432,"stop_loss_ratio":0.1,"min_rate":0.048,"max_rate":0.04824060150375939,"is_open":false,"open_timestamp":1515714900000.0,"close_timestamp":1515719700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":21.31287297527707,"open_date":"2018-01-12 21:15:00+00:00","close_date":"2018-01-12 21:40:00+00:00","open_rate":4.692e-05,"close_rate":4.809593984962405e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":4.2228e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.2228e-05,"stop_loss_ratio":0.1,"min_rate":4.692e-05,"max_rate":4.809593984962405e-05,"is_open":false,"open_timestamp":1515791700000.0,"close_timestamp":1515793200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.38915654211062944,"open_date":"2018-01-13 00:55:00+00:00","close_date":"2018-01-13 06:20:00+00:00","open_rate":0.00256966,"close_rate":0.0025825405012531327,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":325,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002312694,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002312694,"stop_loss_ratio":0.1,"min_rate":0.00256966,"max_rate":0.0025825405012531327,"is_open":false,"open_timestamp":1515804900000.0,"close_timestamp":1515824400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":15.96933886937081,"open_date":"2018-01-13 10:55:00+00:00","close_date":"2018-01-13 11:35:00+00:00","open_rate":6.262e-05,"close_rate":6.293388471177944e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":5.6358e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.6358e-05,"stop_loss_ratio":0.1,"min_rate":6.262e-05,"max_rate":6.293388471177944e-05,"is_open":false,"open_timestamp":1515840900000.0,"close_timestamp":1515843300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":21.14164904862579,"open_date":"2018-01-13 13:05:00+00:00","close_date":"2018-01-15 14:10:00+00:00","open_rate":4.73e-05,"close_rate":4.753709273182957e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":2945,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.257e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.257e-05,"stop_loss_ratio":0.1,"min_rate":4.73e-05,"max_rate":4.753709273182957e-05,"is_open":false,"open_timestamp":1515848700000.0,"close_timestamp":1516025400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":16.49348507339601,"open_date":"2018-01-13 13:30:00+00:00","close_date":"2018-01-13 14:45:00+00:00","open_rate":6.063e-05,"close_rate":6.0933909774436085e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":75,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":5.4567e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.4567e-05,"stop_loss_ratio":0.1,"min_rate":6.063e-05,"max_rate":6.0933909774436085e-05,"is_open":false,"open_timestamp":1515850200000.0,"close_timestamp":1515854700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":9.023641941887746,"open_date":"2018-01-13 13:40:00+00:00","close_date":"2018-01-13 23:30:00+00:00","open_rate":0.00011082,"close_rate":0.00011137548872180448,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":590,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":9.9738e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":9.9738e-05,"stop_loss_ratio":0.1,"min_rate":0.00011082,"max_rate":0.00011137548872180448,"is_open":false,"open_timestamp":1515850800000.0,"close_timestamp":1515886200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":16.863406408094438,"open_date":"2018-01-13 15:15:00+00:00","close_date":"2018-01-13 15:55:00+00:00","open_rate":5.93e-05,"close_rate":5.9597243107769415e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":5.337e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.337e-05,"stop_loss_ratio":0.1,"min_rate":5.93e-05,"max_rate":5.9597243107769415e-05,"is_open":false,"open_timestamp":1515856500000.0,"close_timestamp":1515858900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.020618543947292404,"open_date":"2018-01-13 16:30:00+00:00","close_date":"2018-01-13 17:10:00+00:00","open_rate":0.04850003,"close_rate":0.04874313791979949,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.043650027,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.043650027,"stop_loss_ratio":0.1,"min_rate":0.04850003,"max_rate":0.04874313791979949,"is_open":false,"open_timestamp":1515861000000.0,"close_timestamp":1515863400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010178097365511457,"open_date":"2018-01-13 22:05:00+00:00","close_date":"2018-01-14 06:25:00+00:00","open_rate":0.09825019,"close_rate":0.09874267215538848,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":500,"profit_ratio":-0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.088425171,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.088425171,"stop_loss_ratio":0.1,"min_rate":0.09825019,"max_rate":0.09874267215538848,"is_open":false,"open_timestamp":1515881100000.0,"close_timestamp":1515911100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":16.616816218012627,"open_date":"2018-01-14 00:20:00+00:00","close_date":"2018-01-14 22:55:00+00:00","open_rate":6.018e-05,"close_rate":6.048165413533834e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":1355,"profit_ratio":0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":5.4162e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.4162e-05,"stop_loss_ratio":0.1,"min_rate":6.018e-05,"max_rate":6.048165413533834e-05,"is_open":false,"open_timestamp":1515889200000.0,"close_timestamp":1515970500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010246952581919518,"open_date":"2018-01-14 12:45:00+00:00","close_date":"2018-01-14 13:25:00+00:00","open_rate":0.09758999,"close_rate":0.0980791628822055,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.087830991,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.087830991,"stop_loss_ratio":0.1,"min_rate":0.09758999,"max_rate":0.0980791628822055,"is_open":false,"open_timestamp":1515933900000.0,"close_timestamp":1515936300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.3215434083601286,"open_date":"2018-01-14 15:30:00+00:00","close_date":"2018-01-14 16:00:00+00:00","open_rate":0.00311,"close_rate":0.0031567669172932328,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002799,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002799,"stop_loss_ratio":0.1,"min_rate":0.00311,"max_rate":0.0031567669172932328,"is_open":false,"open_timestamp":1515943800000.0,"close_timestamp":1515945600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.32010140812609433,"open_date":"2018-01-14 20:45:00+00:00","close_date":"2018-01-14 22:15:00+00:00","open_rate":0.00312401,"close_rate":0.003139669197994987,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":90,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002811609,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002811609,"stop_loss_ratio":0.1,"min_rate":0.00312401,"max_rate":0.003139669197994987,"is_open":false,"open_timestamp":1515962700000.0,"close_timestamp":1515968100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.057247866085791646,"open_date":"2018-01-14 23:35:00+00:00","close_date":"2018-01-15 00:30:00+00:00","open_rate":0.0174679,"close_rate":0.017555458395989976,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.015721110000000003,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.015721110000000003,"stop_loss_ratio":0.1,"min_rate":0.0174679,"max_rate":0.017555458395989976,"is_open":false,"open_timestamp":1515972900000.0,"close_timestamp":1515976200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.013611282991367995,"open_date":"2018-01-14 23:45:00+00:00","close_date":"2018-01-15 00:25:00+00:00","open_rate":0.07346846,"close_rate":0.07383672295739348,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.066121614,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.066121614,"stop_loss_ratio":0.1,"min_rate":0.07346846,"max_rate":0.07383672295739348,"is_open":false,"open_timestamp":1515973500000.0,"close_timestamp":1515975900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010204706410596568,"open_date":"2018-01-15 02:25:00+00:00","close_date":"2018-01-15 03:05:00+00:00","open_rate":0.097994,"close_rate":0.09848519799498744,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0881946,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0881946,"stop_loss_ratio":0.1,"min_rate":0.097994,"max_rate":0.09848519799498744,"is_open":false,"open_timestamp":1515983100000.0,"close_timestamp":1515985500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010353038616834042,"open_date":"2018-01-15 07:20:00+00:00","close_date":"2018-01-15 08:00:00+00:00","open_rate":0.09659,"close_rate":0.09707416040100247,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.086931,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.086931,"stop_loss_ratio":0.1,"min_rate":0.09659,"max_rate":0.09707416040100247,"is_open":false,"open_timestamp":1516000800000.0,"close_timestamp":1516003200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":10.0130169219986,"open_date":"2018-01-15 08:20:00+00:00","close_date":"2018-01-15 08:55:00+00:00","open_rate":9.987e-05,"close_rate":0.00010137180451127818,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":8.9883e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.9883e-05,"stop_loss_ratio":0.1,"min_rate":9.987e-05,"max_rate":0.00010137180451127818,"is_open":false,"open_timestamp":1516004400000.0,"close_timestamp":1516006500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010537752023511832,"open_date":"2018-01-15 12:10:00+00:00","close_date":"2018-01-16 02:50:00+00:00","open_rate":0.0948969,"close_rate":0.09537257368421052,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":880,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.08540721000000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.08540721000000001,"stop_loss_ratio":0.1,"min_rate":0.0948969,"max_rate":0.09537257368421052,"is_open":false,"open_timestamp":1516018200000.0,"close_timestamp":1516071000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014084507042253523,"open_date":"2018-01-15 14:10:00+00:00","close_date":"2018-01-15 17:40:00+00:00","open_rate":0.071,"close_rate":0.07135588972431077,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":210,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0639,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0639,"stop_loss_ratio":0.1,"min_rate":0.071,"max_rate":0.07135588972431077,"is_open":false,"open_timestamp":1516025400000.0,"close_timestamp":1516038000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.021736763017766975,"open_date":"2018-01-15 14:30:00+00:00","close_date":"2018-01-15 15:10:00+00:00","open_rate":0.04600501,"close_rate":0.046235611553884705,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.041404509,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.041404509,"stop_loss_ratio":0.1,"min_rate":0.04600501,"max_rate":0.046235611553884705,"is_open":false,"open_timestamp":1516026600000.0,"close_timestamp":1516029000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":10.595465140919686,"open_date":"2018-01-15 18:10:00+00:00","close_date":"2018-01-15 19:25:00+00:00","open_rate":9.438e-05,"close_rate":9.485308270676693e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":75,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":8.4942e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.4942e-05,"stop_loss_ratio":0.1,"min_rate":9.438e-05,"max_rate":9.485308270676693e-05,"is_open":false,"open_timestamp":1516039800000.0,"close_timestamp":1516044300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.032894726021471705,"open_date":"2018-01-15 18:35:00+00:00","close_date":"2018-01-15 19:15:00+00:00","open_rate":0.03040001,"close_rate":0.030552391002506264,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.027360009,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.027360009,"stop_loss_ratio":0.1,"min_rate":0.03040001,"max_rate":0.030552391002506264,"is_open":false,"open_timestamp":1516041300000.0,"close_timestamp":1516043700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":17.13208840157615,"open_date":"2018-01-15 20:25:00+00:00","close_date":"2018-01-16 08:25:00+00:00","open_rate":5.837e-05,"close_rate":5.2533e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":720,"profit_ratio":-0.10448878,"profit_abs":-0.00010000000000000005,"exit_reason":"stop_loss","initial_stop_loss_abs":5.2533e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.2533e-05,"stop_loss_ratio":0.1,"min_rate":5.2533e-05,"max_rate":5.837e-05,"is_open":false,"open_timestamp":1516047900000.0,"close_timestamp":1516091100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.021722130506560085,"open_date":"2018-01-15 20:40:00+00:00","close_date":"2018-01-15 22:00:00+00:00","open_rate":0.046036,"close_rate":0.04626675689223057,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0414324,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0414324,"stop_loss_ratio":0.1,"min_rate":0.046036,"max_rate":0.04626675689223057,"is_open":false,"open_timestamp":1516048800000.0,"close_timestamp":1516053600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.34861425832316545,"open_date":"2018-01-16 00:30:00+00:00","close_date":"2018-01-16 01:10:00+00:00","open_rate":0.0028685,"close_rate":0.0028828784461152877,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.00258165,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.00258165,"stop_loss_ratio":0.1,"min_rate":0.0028685,"max_rate":0.0028828784461152877,"is_open":false,"open_timestamp":1516062600000.0,"close_timestamp":1516065000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014854967241083492,"open_date":"2018-01-16 01:15:00+00:00","close_date":"2018-01-16 02:35:00+00:00","open_rate":0.06731755,"close_rate":0.0676549813283208,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.060585795000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.060585795000000005,"stop_loss_ratio":0.1,"min_rate":0.06731755,"max_rate":0.0676549813283208,"is_open":false,"open_timestamp":1516065300000.0,"close_timestamp":1516070100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010848794492804754,"open_date":"2018-01-16 07:45:00+00:00","close_date":"2018-01-16 08:40:00+00:00","open_rate":0.09217614,"close_rate":0.09263817578947368,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.082958526,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.082958526,"stop_loss_ratio":0.1,"min_rate":0.09217614,"max_rate":0.09263817578947368,"is_open":false,"open_timestamp":1516088700000.0,"close_timestamp":1516092000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.06060606060606061,"open_date":"2018-01-16 08:35:00+00:00","close_date":"2018-01-16 08:55:00+00:00","open_rate":0.0165,"close_rate":0.016913533834586467,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641604113e-05,"exit_reason":"roi","initial_stop_loss_abs":0.01485,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.01485,"stop_loss_ratio":0.1,"min_rate":0.0165,"max_rate":0.016913533834586467,"is_open":false,"open_timestamp":1516091700000.0,"close_timestamp":1516092900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":12.57387149503332,"open_date":"2018-01-16 08:35:00+00:00","close_date":"2018-01-16 08:40:00+00:00","open_rate":7.953e-05,"close_rate":8.311781954887218e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":7.157700000000001e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":7.157700000000001e-05,"stop_loss_ratio":0.1,"min_rate":7.953e-05,"max_rate":8.311781954887218e-05,"is_open":false,"open_timestamp":1516091700000.0,"close_timestamp":1516092000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.022122914915269236,"open_date":"2018-01-16 08:45:00+00:00","close_date":"2018-01-16 09:50:00+00:00","open_rate":0.045202,"close_rate":0.04542857644110275,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":65,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0406818,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0406818,"stop_loss_ratio":0.1,"min_rate":0.045202,"max_rate":0.04542857644110275,"is_open":false,"open_timestamp":1516092300000.0,"close_timestamp":1516096200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.054878048780488,"open_date":"2018-01-16 09:15:00+00:00","close_date":"2018-01-16 09:45:00+00:00","open_rate":5.248e-05,"close_rate":5.326917293233082e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":4.7232e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7232e-05,"stop_loss_ratio":0.1,"min_rate":5.248e-05,"max_rate":5.326917293233082e-05,"is_open":false,"open_timestamp":1516094100000.0,"close_timestamp":1516095900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03457434486802627,"open_date":"2018-01-16 09:15:00+00:00","close_date":"2018-01-16 09:55:00+00:00","open_rate":0.02892318,"close_rate":0.02906815834586466,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.026030862,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.026030862,"stop_loss_ratio":0.1,"min_rate":0.02892318,"max_rate":0.02906815834586466,"is_open":false,"open_timestamp":1516094100000.0,"close_timestamp":1516096500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.38735944164405,"open_date":"2018-01-16 09:50:00+00:00","close_date":"2018-01-16 10:10:00+00:00","open_rate":5.158e-05,"close_rate":5.287273182957392e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":4.6422e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6422e-05,"stop_loss_ratio":0.1,"min_rate":5.158e-05,"max_rate":5.287273182957392e-05,"is_open":false,"open_timestamp":1516096200000.0,"close_timestamp":1516097400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.022948496230938985,"open_date":"2018-01-16 10:05:00+00:00","close_date":"2018-01-16 10:40:00+00:00","open_rate":0.04357584,"close_rate":0.044231115789473675,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.039218256,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.039218256,"stop_loss_ratio":0.1,"min_rate":0.04357584,"max_rate":0.044231115789473675,"is_open":false,"open_timestamp":1516097100000.0,"close_timestamp":1516099200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.035357778286929785,"open_date":"2018-01-16 10:05:00+00:00","close_date":"2018-01-16 10:35:00+00:00","open_rate":0.02828232,"close_rate":0.02870761804511278,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.025454088,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025454088,"stop_loss_ratio":0.1,"min_rate":0.02828232,"max_rate":0.02870761804511278,"is_open":false,"open_timestamp":1516097100000.0,"close_timestamp":1516098900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":18.64975755315181,"open_date":"2018-01-16 13:45:00+00:00","close_date":"2018-01-16 14:20:00+00:00","open_rate":5.362e-05,"close_rate":5.442631578947368e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":4.8258e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.8258e-05,"stop_loss_ratio":0.1,"min_rate":5.362e-05,"max_rate":5.442631578947368e-05,"is_open":false,"open_timestamp":1516110300000.0,"close_timestamp":1516112400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":18.86080724254998,"open_date":"2018-01-16 17:30:00+00:00","close_date":"2018-01-16 18:25:00+00:00","open_rate":5.302e-05,"close_rate":5.328576441102756e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.7718e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7718e-05,"stop_loss_ratio":0.1,"min_rate":5.302e-05,"max_rate":5.328576441102756e-05,"is_open":false,"open_timestamp":1516123800000.0,"close_timestamp":1516127100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010952903718828448,"open_date":"2018-01-16 18:15:00+00:00","close_date":"2018-01-16 18:45:00+00:00","open_rate":0.09129999,"close_rate":0.09267292218045112,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":0.082169991,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.082169991,"stop_loss_ratio":0.1,"min_rate":0.09129999,"max_rate":0.09267292218045112,"is_open":false,"open_timestamp":1516126500000.0,"close_timestamp":1516128300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":26.26050420168067,"open_date":"2018-01-16 18:15:00+00:00","close_date":"2018-01-16 18:35:00+00:00","open_rate":3.808e-05,"close_rate":3.903438596491228e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":3.4272e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.4272e-05,"stop_loss_ratio":0.1,"min_rate":3.808e-05,"max_rate":3.903438596491228e-05,"is_open":false,"open_timestamp":1516126500000.0,"close_timestamp":1516127700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.035574376772493324,"open_date":"2018-01-16 19:00:00+00:00","close_date":"2018-01-16 19:30:00+00:00","open_rate":0.02811012,"close_rate":0.028532828571428567,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.025299108,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025299108,"stop_loss_ratio":0.1,"min_rate":0.02811012,"max_rate":0.028532828571428567,"is_open":false,"open_timestamp":1516129200000.0,"close_timestamp":1516131000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.387028357567759,"open_date":"2018-01-16 21:25:00+00:00","close_date":"2018-01-16 22:25:00+00:00","open_rate":0.00258379,"close_rate":0.002325411,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":-0.10448878,"profit_abs":-0.00010000000000000005,"exit_reason":"stop_loss","initial_stop_loss_abs":0.002325411,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002325411,"stop_loss_ratio":0.1,"min_rate":0.002325411,"max_rate":0.00258379,"is_open":false,"open_timestamp":1516137900000.0,"close_timestamp":1516141500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":39.07776475185619,"open_date":"2018-01-16 21:25:00+00:00","close_date":"2018-01-16 22:45:00+00:00","open_rate":2.559e-05,"close_rate":2.3031e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":-0.10448878,"profit_abs":-0.00010000000000000005,"exit_reason":"stop_loss","initial_stop_loss_abs":2.3031e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.3031e-05,"stop_loss_ratio":0.1,"min_rate":2.3031e-05,"max_rate":2.559e-05,"is_open":false,"open_timestamp":1516137900000.0,"close_timestamp":1516142700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":13.123359580052494,"open_date":"2018-01-16 21:35:00+00:00","close_date":"2018-01-16 22:25:00+00:00","open_rate":7.62e-05,"close_rate":6.858e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":-0.10448878,"profit_abs":-0.00010000000000000005,"exit_reason":"stop_loss","initial_stop_loss_abs":6.858e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":6.858e-05,"stop_loss_ratio":0.1,"min_rate":6.858e-05,"max_rate":7.62e-05,"is_open":false,"open_timestamp":1516138500000.0,"close_timestamp":1516141500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.06622516556291391,"open_date":"2018-01-16 22:30:00+00:00","close_date":"2018-01-16 22:40:00+00:00","open_rate":0.0151,"close_rate":0.015781203007518795,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":10,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":0.01359,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.01359,"stop_loss_ratio":0.1,"min_rate":0.0151,"max_rate":0.015781203007518795,"is_open":false,"open_timestamp":1516141800000.0,"close_timestamp":1516142400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.4350777048780912,"open_date":"2018-01-16 22:30:00+00:00","close_date":"2018-01-16 22:35:00+00:00","open_rate":0.00229844,"close_rate":0.002402129022556391,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":4.511278195488727e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002068596,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002068596,"stop_loss_ratio":0.1,"min_rate":0.00229844,"max_rate":0.002402129022556391,"is_open":false,"open_timestamp":1516141800000.0,"close_timestamp":1516142100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.4243113426908128,"open_date":"2018-01-16 22:40:00+00:00","close_date":"2018-01-16 22:45:00+00:00","open_rate":0.00235676,"close_rate":0.00246308,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":4.511278195488727e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002121084,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002121084,"stop_loss_ratio":0.1,"min_rate":0.00235676,"max_rate":0.00246308,"is_open":false,"open_timestamp":1516142400000.0,"close_timestamp":1516142700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.01585559988076589,"open_date":"2018-01-16 22:45:00+00:00","close_date":"2018-01-16 23:05:00+00:00","open_rate":0.0630692,"close_rate":0.06464988170426066,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641604113e-05,"exit_reason":"roi","initial_stop_loss_abs":0.056762280000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.056762280000000005,"stop_loss_ratio":0.1,"min_rate":0.0630692,"max_rate":0.06464988170426066,"is_open":false,"open_timestamp":1516142700000.0,"close_timestamp":1516143900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":45.45454545454545,"open_date":"2018-01-16 22:50:00+00:00","close_date":"2018-01-16 22:55:00+00:00","open_rate":2.2e-05,"close_rate":2.299248120300751e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":4.511278195488684e-05,"exit_reason":"roi","initial_stop_loss_abs":1.98e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":1.98e-05,"stop_loss_ratio":0.1,"min_rate":2.2e-05,"max_rate":2.299248120300751e-05,"is_open":false,"open_timestamp":1516143000000.0,"close_timestamp":1516143300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":20.10454362685967,"open_date":"2018-01-17 03:30:00+00:00","close_date":"2018-01-17 04:00:00+00:00","open_rate":4.974e-05,"close_rate":5.048796992481203e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":4.4766000000000005e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.4766000000000005e-05,"stop_loss_ratio":0.1,"min_rate":4.974e-05,"max_rate":5.048796992481203e-05,"is_open":false,"open_timestamp":1516159800000.0,"close_timestamp":1516161600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":14.068655036578503,"open_date":"2018-01-17 03:55:00+00:00","close_date":"2018-01-17 04:15:00+00:00","open_rate":7.108e-05,"close_rate":7.28614536340852e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":6.3972e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":6.3972e-05,"stop_loss_ratio":0.1,"min_rate":7.108e-05,"max_rate":7.28614536340852e-05,"is_open":false,"open_timestamp":1516161300000.0,"close_timestamp":1516162500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.0231107002542177,"open_date":"2018-01-17 09:35:00+00:00","close_date":"2018-01-17 10:15:00+00:00","open_rate":0.04327,"close_rate":0.04348689223057644,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.038943000000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.038943000000000005,"stop_loss_ratio":0.1,"min_rate":0.04327,"max_rate":0.04348689223057644,"is_open":false,"open_timestamp":1516181700000.0,"close_timestamp":1516184100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":20.012007204322593,"open_date":"2018-01-17 10:20:00+00:00","close_date":"2018-01-17 17:00:00+00:00","open_rate":4.997e-05,"close_rate":5.022047619047618e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":400,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":4.4973e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.4973e-05,"stop_loss_ratio":0.1,"min_rate":4.997e-05,"max_rate":5.022047619047618e-05,"is_open":false,"open_timestamp":1516184400000.0,"close_timestamp":1516208400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014626687444363738,"open_date":"2018-01-17 10:30:00+00:00","close_date":"2018-01-17 11:25:00+00:00","open_rate":0.06836818,"close_rate":0.06871087764411027,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.061531362,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.061531362,"stop_loss_ratio":0.1,"min_rate":0.06836818,"max_rate":0.06871087764411027,"is_open":false,"open_timestamp":1516185000000.0,"close_timestamp":1516188300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":27.548209366391184,"open_date":"2018-01-17 10:30:00+00:00","close_date":"2018-01-17 11:10:00+00:00","open_rate":3.63e-05,"close_rate":3.648195488721804e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":3.2670000000000004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.2670000000000004e-05,"stop_loss_ratio":0.1,"min_rate":3.63e-05,"max_rate":3.648195488721804e-05,"is_open":false,"open_timestamp":1516185000000.0,"close_timestamp":1516187400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03558718861209965,"open_date":"2018-01-17 12:30:00+00:00","close_date":"2018-01-17 22:05:00+00:00","open_rate":0.0281,"close_rate":0.02824085213032581,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":575,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.02529,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.02529,"stop_loss_ratio":0.1,"min_rate":0.0281,"max_rate":0.02824085213032581,"is_open":false,"open_timestamp":1516192200000.0,"close_timestamp":1516226700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011559355963546878,"open_date":"2018-01-17 12:35:00+00:00","close_date":"2018-01-17 16:55:00+00:00","open_rate":0.08651001,"close_rate":0.08694364413533832,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":260,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.077859009,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.077859009,"stop_loss_ratio":0.1,"min_rate":0.08651001,"max_rate":0.08694364413533832,"is_open":false,"open_timestamp":1516192500000.0,"close_timestamp":1516208100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":17.752529735487308,"open_date":"2018-01-18 05:00:00+00:00","close_date":"2018-01-18 05:55:00+00:00","open_rate":5.633e-05,"close_rate":5.6612355889724306e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":5.0697e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.0697e-05,"stop_loss_ratio":0.1,"min_rate":5.633e-05,"max_rate":5.6612355889724306e-05,"is_open":false,"open_timestamp":1516251600000.0,"close_timestamp":1516254900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.01430923457900944,"open_date":"2018-01-18 05:20:00+00:00","close_date":"2018-01-18 05:55:00+00:00","open_rate":0.06988494,"close_rate":0.07093584135338346,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":0.06289644600000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.06289644600000001,"stop_loss_ratio":0.1,"min_rate":0.06988494,"max_rate":0.07093584135338346,"is_open":false,"open_timestamp":1516252800000.0,"close_timestamp":1516254900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":18.034265103697024,"open_date":"2018-01-18 07:35:00+00:00","close_date":"2018-01-18 08:15:00+00:00","open_rate":5.545e-05,"close_rate":5.572794486215538e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":4.9905e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.9905e-05,"stop_loss_ratio":0.1,"min_rate":5.545e-05,"max_rate":5.572794486215538e-05,"is_open":false,"open_timestamp":1516260900000.0,"close_timestamp":1516263300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.06121723118136401,"open_date":"2018-01-18 09:00:00+00:00","close_date":"2018-01-18 09:40:00+00:00","open_rate":0.01633527,"close_rate":0.016417151052631574,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.014701743,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.014701743,"stop_loss_ratio":0.1,"min_rate":0.01633527,"max_rate":0.016417151052631574,"is_open":false,"open_timestamp":1516266000000.0,"close_timestamp":1516268400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.3707356136045141,"open_date":"2018-01-18 16:40:00+00:00","close_date":"2018-01-18 17:20:00+00:00","open_rate":0.00269734,"close_rate":0.002710860501253133,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002427606,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002427606,"stop_loss_ratio":0.1,"min_rate":0.00269734,"max_rate":0.002710860501253133,"is_open":false,"open_timestamp":1516293600000.0,"close_timestamp":1516296000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":22.3463687150838,"open_date":"2018-01-18 18:05:00+00:00","close_date":"2018-01-18 18:30:00+00:00","open_rate":4.475e-05,"close_rate":4.587155388471177e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":2.5062656641604113e-05,"exit_reason":"roi","initial_stop_loss_abs":4.0275e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.0275e-05,"stop_loss_ratio":0.1,"min_rate":4.475e-05,"max_rate":4.587155388471177e-05,"is_open":false,"open_timestamp":1516298700000.0,"close_timestamp":1516300200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":35.842293906810035,"open_date":"2018-01-18 18:25:00+00:00","close_date":"2018-01-18 18:55:00+00:00","open_rate":2.79e-05,"close_rate":2.8319548872180444e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":2.511e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.511e-05,"stop_loss_ratio":0.1,"min_rate":2.79e-05,"max_rate":2.8319548872180444e-05,"is_open":false,"open_timestamp":1516299900000.0,"close_timestamp":1516301700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.022525942001105578,"open_date":"2018-01-18 20:10:00+00:00","close_date":"2018-01-18 20:50:00+00:00","open_rate":0.04439326,"close_rate":0.04461578260651629,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.039953934,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.039953934,"stop_loss_ratio":0.1,"min_rate":0.04439326,"max_rate":0.04461578260651629,"is_open":false,"open_timestamp":1516306200000.0,"close_timestamp":1516308600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":22.271714922048996,"open_date":"2018-01-18 21:30:00+00:00","close_date":"2018-01-19 00:35:00+00:00","open_rate":4.49e-05,"close_rate":4.51250626566416e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":185,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.041e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.041e-05,"stop_loss_ratio":0.1,"min_rate":4.49e-05,"max_rate":4.51250626566416e-05,"is_open":false,"open_timestamp":1516311000000.0,"close_timestamp":1516322100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03502626970227671,"open_date":"2018-01-18 21:55:00+00:00","close_date":"2018-01-19 05:05:00+00:00","open_rate":0.02855,"close_rate":0.028693107769423555,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":430,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.025695,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025695,"stop_loss_ratio":0.1,"min_rate":0.02855,"max_rate":0.028693107769423555,"is_open":false,"open_timestamp":1516312500000.0,"close_timestamp":1516338300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":17.25327812284334,"open_date":"2018-01-18 22:10:00+00:00","close_date":"2018-01-18 22:50:00+00:00","open_rate":5.796e-05,"close_rate":5.8250526315789473e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":5.2164e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.2164e-05,"stop_loss_ratio":0.1,"min_rate":5.796e-05,"max_rate":5.8250526315789473e-05,"is_open":false,"open_timestamp":1516313400000.0,"close_timestamp":1516315800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02303975994413319,"open_date":"2018-01-18 23:50:00+00:00","close_date":"2018-01-19 00:30:00+00:00","open_rate":0.04340323,"close_rate":0.04362079005012531,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.039062907,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.039062907,"stop_loss_ratio":0.1,"min_rate":0.04340323,"max_rate":0.04362079005012531,"is_open":false,"open_timestamp":1516319400000.0,"close_timestamp":1516321800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02244943545282195,"open_date":"2018-01-19 16:45:00+00:00","close_date":"2018-01-19 17:35:00+00:00","open_rate":0.04454455,"close_rate":0.04476783095238095,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.040090095000000006,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.040090095000000006,"stop_loss_ratio":0.1,"min_rate":0.04454455,"max_rate":0.04476783095238095,"is_open":false,"open_timestamp":1516380300000.0,"close_timestamp":1516383300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":17.793594306049823,"open_date":"2018-01-19 17:15:00+00:00","close_date":"2018-01-19 19:55:00+00:00","open_rate":5.62e-05,"close_rate":5.648170426065162e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":160,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":5.058e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.058e-05,"stop_loss_ratio":0.1,"min_rate":5.62e-05,"max_rate":5.648170426065162e-05,"is_open":false,"open_timestamp":1516382100000.0,"close_timestamp":1516391700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":23.04678497349619,"open_date":"2018-01-19 17:20:00+00:00","close_date":"2018-01-19 20:15:00+00:00","open_rate":4.339e-05,"close_rate":4.360749373433584e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":175,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":3.9051e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.9051e-05,"stop_loss_ratio":0.1,"min_rate":4.339e-05,"max_rate":4.360749373433584e-05,"is_open":false,"open_timestamp":1516382400000.0,"close_timestamp":1516392900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":9.910802775024775,"open_date":"2018-01-20 04:45:00+00:00","close_date":"2018-01-20 17:35:00+00:00","open_rate":0.0001009,"close_rate":0.00010140576441102755,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":770,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":9.081e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":9.081e-05,"stop_loss_ratio":0.1,"min_rate":0.0001009,"max_rate":0.00010140576441102755,"is_open":false,"open_timestamp":1516423500000.0,"close_timestamp":1516469700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.3696789338459548,"open_date":"2018-01-20 04:50:00+00:00","close_date":"2018-01-20 15:15:00+00:00","open_rate":0.00270505,"close_rate":0.002718609147869674,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":625,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002434545,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002434545,"stop_loss_ratio":0.1,"min_rate":0.00270505,"max_rate":0.002718609147869674,"is_open":false,"open_timestamp":1516423800000.0,"close_timestamp":1516461300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.033333311111125925,"open_date":"2018-01-20 04:50:00+00:00","close_date":"2018-01-20 07:00:00+00:00","open_rate":0.03000002,"close_rate":0.030150396040100245,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":130,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.027000018,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.027000018,"stop_loss_ratio":0.1,"min_rate":0.03000002,"max_rate":0.030150396040100245,"is_open":false,"open_timestamp":1516423800000.0,"close_timestamp":1516431600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":18.315018315018317,"open_date":"2018-01-20 09:00:00+00:00","close_date":"2018-01-20 09:40:00+00:00","open_rate":5.46e-05,"close_rate":5.4873684210526304e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.914e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.914e-05,"stop_loss_ratio":0.1,"min_rate":5.46e-05,"max_rate":5.4873684210526304e-05,"is_open":false,"open_timestamp":1516438800000.0,"close_timestamp":1516441200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03244412634781012,"open_date":"2018-01-20 18:25:00+00:00","close_date":"2018-01-25 03:50:00+00:00","open_rate":0.03082222,"close_rate":0.027739998,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":6325,"profit_ratio":-0.10448878,"profit_abs":-0.00010000000000000015,"exit_reason":"stop_loss","initial_stop_loss_abs":0.027739998,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.027739998,"stop_loss_ratio":0.1,"min_rate":0.027739998,"max_rate":0.03082222,"is_open":false,"open_timestamp":1516472700000.0,"close_timestamp":1516852200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011148273260677063,"open_date":"2018-01-20 22:25:00+00:00","close_date":"2018-01-20 23:15:00+00:00","open_rate":0.08969999,"close_rate":0.09014961401002504,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.080729991,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.080729991,"stop_loss_ratio":0.1,"min_rate":0.08969999,"max_rate":0.09014961401002504,"is_open":false,"open_timestamp":1516487100000.0,"close_timestamp":1516490100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.06125570520324337,"open_date":"2018-01-21 02:50:00+00:00","close_date":"2018-01-21 14:30:00+00:00","open_rate":0.01632501,"close_rate":0.01640683962406015,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":700,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.014692509,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.014692509,"stop_loss_ratio":0.1,"min_rate":0.01632501,"max_rate":0.01640683962406015,"is_open":false,"open_timestamp":1516503000000.0,"close_timestamp":1516545000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.01417675579120474,"open_date":"2018-01-21 10:20:00+00:00","close_date":"2018-01-21 11:00:00+00:00","open_rate":0.070538,"close_rate":0.07089157393483708,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0634842,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0634842,"stop_loss_ratio":0.1,"min_rate":0.070538,"max_rate":0.07089157393483708,"is_open":false,"open_timestamp":1516530000000.0,"close_timestamp":1516532400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":18.864365214110546,"open_date":"2018-01-21 15:50:00+00:00","close_date":"2018-01-21 18:45:00+00:00","open_rate":5.301e-05,"close_rate":5.327571428571427e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":175,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":4.7709e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7709e-05,"stop_loss_ratio":0.1,"min_rate":5.301e-05,"max_rate":5.327571428571427e-05,"is_open":false,"open_timestamp":1516549800000.0,"close_timestamp":1516560300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":25.284450063211125,"open_date":"2018-01-21 16:20:00+00:00","close_date":"2018-01-21 17:00:00+00:00","open_rate":3.955e-05,"close_rate":3.9748245614035085e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":3.5595e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.5595e-05,"stop_loss_ratio":0.1,"min_rate":3.955e-05,"max_rate":3.9748245614035085e-05,"is_open":false,"open_timestamp":1516551600000.0,"close_timestamp":1516554000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.38683971296493297,"open_date":"2018-01-21 21:15:00+00:00","close_date":"2018-01-21 21:45:00+00:00","open_rate":0.00258505,"close_rate":0.002623922932330827,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002326545,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002326545,"stop_loss_ratio":0.1,"min_rate":0.00258505,"max_rate":0.002623922932330827,"is_open":false,"open_timestamp":1516569300000.0,"close_timestamp":1516571100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":25.621316935690498,"open_date":"2018-01-21 21:15:00+00:00","close_date":"2018-01-21 21:55:00+00:00","open_rate":3.903e-05,"close_rate":3.922563909774435e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":3.5127e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.5127e-05,"stop_loss_ratio":0.1,"min_rate":3.903e-05,"max_rate":3.922563909774435e-05,"is_open":false,"open_timestamp":1516569300000.0,"close_timestamp":1516571700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.098548510313215,"open_date":"2018-01-22 00:35:00+00:00","close_date":"2018-01-22 10:35:00+00:00","open_rate":5.236e-05,"close_rate":5.262245614035087e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":600,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":4.7124e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7124e-05,"stop_loss_ratio":0.1,"min_rate":5.236e-05,"max_rate":5.262245614035087e-05,"is_open":false,"open_timestamp":1516581300000.0,"close_timestamp":1516617300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":11.076650420912715,"open_date":"2018-01-22 01:30:00+00:00","close_date":"2018-01-22 02:10:00+00:00","open_rate":9.028e-05,"close_rate":9.07325313283208e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":8.1252e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.1252e-05,"stop_loss_ratio":0.1,"min_rate":9.028e-05,"max_rate":9.07325313283208e-05,"is_open":false,"open_timestamp":1516584600000.0,"close_timestamp":1516587000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.3721622627465575,"open_date":"2018-01-22 12:25:00+00:00","close_date":"2018-01-22 14:35:00+00:00","open_rate":0.002687,"close_rate":0.002700468671679198,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":130,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0024183000000000004,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0024183000000000004,"stop_loss_ratio":0.1,"min_rate":0.002687,"max_rate":0.002700468671679198,"is_open":false,"open_timestamp":1516623900000.0,"close_timestamp":1516631700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":23.99232245681382,"open_date":"2018-01-22 13:15:00+00:00","close_date":"2018-01-22 13:55:00+00:00","open_rate":4.168e-05,"close_rate":4.188892230576441e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":3.7512e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.7512e-05,"stop_loss_ratio":0.1,"min_rate":4.168e-05,"max_rate":4.188892230576441e-05,"is_open":false,"open_timestamp":1516626900000.0,"close_timestamp":1516629300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":11.336583153837434,"open_date":"2018-01-22 14:00:00+00:00","close_date":"2018-01-22 14:30:00+00:00","open_rate":8.821e-05,"close_rate":8.953646616541353e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":7.9389e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":7.9389e-05,"stop_loss_ratio":0.1,"min_rate":8.821e-05,"max_rate":8.953646616541353e-05,"is_open":false,"open_timestamp":1516629600000.0,"close_timestamp":1516631400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.334880123743233,"open_date":"2018-01-22 15:55:00+00:00","close_date":"2018-01-22 16:40:00+00:00","open_rate":5.172e-05,"close_rate":5.1979248120300745e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.6548e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6548e-05,"stop_loss_ratio":0.1,"min_rate":5.172e-05,"max_rate":5.1979248120300745e-05,"is_open":false,"open_timestamp":1516636500000.0,"close_timestamp":1516639200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":33.04692663582287,"open_date":"2018-01-22 16:05:00+00:00","close_date":"2018-01-22 16:25:00+00:00","open_rate":3.026e-05,"close_rate":3.101839598997494e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641604113e-05,"exit_reason":"roi","initial_stop_loss_abs":2.7234e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.7234e-05,"stop_loss_ratio":0.1,"min_rate":3.026e-05,"max_rate":3.101839598997494e-05,"is_open":false,"open_timestamp":1516637100000.0,"close_timestamp":1516638300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014156285390713478,"open_date":"2018-01-22 19:50:00+00:00","close_date":"2018-01-23 00:10:00+00:00","open_rate":0.07064,"close_rate":0.07099408521303258,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":260,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.063576,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.063576,"stop_loss_ratio":0.1,"min_rate":0.07064,"max_rate":0.07099408521303258,"is_open":false,"open_timestamp":1516650600000.0,"close_timestamp":1516666200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.06080938507725528,"open_date":"2018-01-22 21:25:00+00:00","close_date":"2018-01-22 22:05:00+00:00","open_rate":0.01644483,"close_rate":0.01652726022556391,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.014800347,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.014800347,"stop_loss_ratio":0.1,"min_rate":0.01644483,"max_rate":0.01652726022556391,"is_open":false,"open_timestamp":1516656300000.0,"close_timestamp":1516658700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":23.08935580697299,"open_date":"2018-01-23 00:05:00+00:00","close_date":"2018-01-23 00:35:00+00:00","open_rate":4.331e-05,"close_rate":4.3961278195488714e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":3.8979e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.8979e-05,"stop_loss_ratio":0.1,"min_rate":4.331e-05,"max_rate":4.3961278195488714e-05,"is_open":false,"open_timestamp":1516665900000.0,"close_timestamp":1516667700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":31.250000000000004,"open_date":"2018-01-23 01:50:00+00:00","close_date":"2018-01-23 02:15:00+00:00","open_rate":3.2e-05,"close_rate":3.2802005012531326e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":2.5062656641604113e-05,"exit_reason":"roi","initial_stop_loss_abs":2.88e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.88e-05,"stop_loss_ratio":0.1,"min_rate":3.2e-05,"max_rate":3.2802005012531326e-05,"is_open":false,"open_timestamp":1516672200000.0,"close_timestamp":1516673700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010907854156754156,"open_date":"2018-01-23 04:25:00+00:00","close_date":"2018-01-23 05:15:00+00:00","open_rate":0.09167706,"close_rate":0.09213659413533835,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.08250935400000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.08250935400000001,"stop_loss_ratio":0.1,"min_rate":0.09167706,"max_rate":0.09213659413533835,"is_open":false,"open_timestamp":1516681500000.0,"close_timestamp":1516684500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014440474918339117,"open_date":"2018-01-23 07:35:00+00:00","close_date":"2018-01-23 09:00:00+00:00","open_rate":0.0692498,"close_rate":0.06959691679197995,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":85,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.06232482,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.06232482,"stop_loss_ratio":0.1,"min_rate":0.0692498,"max_rate":0.06959691679197995,"is_open":false,"open_timestamp":1516692900000.0,"close_timestamp":1516698000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":31.426775612822127,"open_date":"2018-01-23 10:50:00+00:00","close_date":"2018-01-23 13:05:00+00:00","open_rate":3.182e-05,"close_rate":3.197949874686716e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":135,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":2.8638e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.8638e-05,"stop_loss_ratio":0.1,"min_rate":3.182e-05,"max_rate":3.197949874686716e-05,"is_open":false,"open_timestamp":1516704600000.0,"close_timestamp":1516712700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.024461839530332683,"open_date":"2018-01-23 11:05:00+00:00","close_date":"2018-01-23 16:05:00+00:00","open_rate":0.04088,"close_rate":0.04108491228070175,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":300,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.036792,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.036792,"stop_loss_ratio":0.1,"min_rate":0.04088,"max_rate":0.04108491228070175,"is_open":false,"open_timestamp":1516705500000.0,"close_timestamp":1516723500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.417475728155345,"open_date":"2018-01-23 14:55:00+00:00","close_date":"2018-01-23 15:35:00+00:00","open_rate":5.15e-05,"close_rate":5.175814536340851e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.635e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.635e-05,"stop_loss_ratio":0.1,"min_rate":5.15e-05,"max_rate":5.175814536340851e-05,"is_open":false,"open_timestamp":1516719300000.0,"close_timestamp":1516721700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011023294646713328,"open_date":"2018-01-23 16:35:00+00:00","close_date":"2018-01-24 00:05:00+00:00","open_rate":0.09071698,"close_rate":0.09117170170426064,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":450,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.081645282,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.081645282,"stop_loss_ratio":0.1,"min_rate":0.09071698,"max_rate":0.09117170170426064,"is_open":false,"open_timestamp":1516725300000.0,"close_timestamp":1516752300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":31.969309462915604,"open_date":"2018-01-23 17:25:00+00:00","close_date":"2018-01-23 18:45:00+00:00","open_rate":3.128e-05,"close_rate":3.1436791979949865e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":2.8152e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.8152e-05,"stop_loss_ratio":0.1,"min_rate":3.128e-05,"max_rate":3.1436791979949865e-05,"is_open":false,"open_timestamp":1516728300000.0,"close_timestamp":1516733100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":10.465724751439035,"open_date":"2018-01-23 20:15:00+00:00","close_date":"2018-01-23 22:00:00+00:00","open_rate":9.555e-05,"close_rate":9.602894736842104e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":105,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":8.5995e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.5995e-05,"stop_loss_ratio":0.1,"min_rate":9.555e-05,"max_rate":9.602894736842104e-05,"is_open":false,"open_timestamp":1516738500000.0,"close_timestamp":1516744800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02450979791426522,"open_date":"2018-01-23 22:30:00+00:00","close_date":"2018-01-23 23:10:00+00:00","open_rate":0.04080001,"close_rate":0.0410045213283208,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.036720009,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.036720009,"stop_loss_ratio":0.1,"min_rate":0.04080001,"max_rate":0.0410045213283208,"is_open":false,"open_timestamp":1516746600000.0,"close_timestamp":1516749000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.36858415649816,"open_date":"2018-01-23 23:50:00+00:00","close_date":"2018-01-24 03:35:00+00:00","open_rate":5.163e-05,"close_rate":5.18887969924812e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":225,"profit_ratio":-0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":4.6467e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6467e-05,"stop_loss_ratio":0.1,"min_rate":5.163e-05,"max_rate":5.18887969924812e-05,"is_open":false,"open_timestamp":1516751400000.0,"close_timestamp":1516764900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.024747691102289384,"open_date":"2018-01-24 00:20:00+00:00","close_date":"2018-01-24 01:50:00+00:00","open_rate":0.04040781,"close_rate":0.04061035541353383,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":90,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.036367029,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.036367029,"stop_loss_ratio":0.1,"min_rate":0.04040781,"max_rate":0.04061035541353383,"is_open":false,"open_timestamp":1516753200000.0,"close_timestamp":1516758600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.485580670303975,"open_date":"2018-01-24 06:45:00+00:00","close_date":"2018-01-24 07:25:00+00:00","open_rate":5.132e-05,"close_rate":5.157724310776942e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.6188000000000006e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6188000000000006e-05,"stop_loss_ratio":0.1,"min_rate":5.132e-05,"max_rate":5.157724310776942e-05,"is_open":false,"open_timestamp":1516776300000.0,"close_timestamp":1516778700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.23816852635629,"open_date":"2018-01-24 14:15:00+00:00","close_date":"2018-01-24 14:25:00+00:00","open_rate":5.198e-05,"close_rate":5.432496240601503e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":10,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":4.6782e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6782e-05,"stop_loss_ratio":0.1,"min_rate":5.198e-05,"max_rate":5.432496240601503e-05,"is_open":false,"open_timestamp":1516803300000.0,"close_timestamp":1516803900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":32.74394237066143,"open_date":"2018-01-24 14:50:00+00:00","close_date":"2018-01-24 16:35:00+00:00","open_rate":3.054e-05,"close_rate":3.069308270676692e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":105,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":2.7486000000000004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.7486000000000004e-05,"stop_loss_ratio":0.1,"min_rate":3.054e-05,"max_rate":3.069308270676692e-05,"is_open":false,"open_timestamp":1516805400000.0,"close_timestamp":1516811700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":10.795638562020944,"open_date":"2018-01-24 15:10:00+00:00","close_date":"2018-01-24 16:15:00+00:00","open_rate":9.263e-05,"close_rate":9.309431077694236e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":65,"profit_ratio":0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":8.3367e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.3367e-05,"stop_loss_ratio":0.1,"min_rate":9.263e-05,"max_rate":9.309431077694236e-05,"is_open":false,"open_timestamp":1516806600000.0,"close_timestamp":1516810500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":18.13565469713457,"open_date":"2018-01-24 22:40:00+00:00","close_date":"2018-01-24 23:25:00+00:00","open_rate":5.514e-05,"close_rate":5.54163909774436e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.962599999999999e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.962599999999999e-05,"stop_loss_ratio":0.1,"min_rate":5.514e-05,"max_rate":5.54163909774436e-05,"is_open":false,"open_timestamp":1516833600000.0,"close_timestamp":1516836300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":20.3210729526519,"open_date":"2018-01-25 00:50:00+00:00","close_date":"2018-01-25 01:30:00+00:00","open_rate":4.921e-05,"close_rate":4.9456666666666664e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.4289e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.4289e-05,"stop_loss_ratio":0.1,"min_rate":4.921e-05,"max_rate":4.9456666666666664e-05,"is_open":false,"open_timestamp":1516841400000.0,"close_timestamp":1516843800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.38461538461538464,"open_date":"2018-01-25 08:15:00+00:00","close_date":"2018-01-25 12:15:00+00:00","open_rate":0.0026,"close_rate":0.002613032581453634,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":240,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.00234,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.00234,"stop_loss_ratio":0.1,"min_rate":0.0026,"max_rate":0.002613032581453634,"is_open":false,"open_timestamp":1516868100000.0,"close_timestamp":1516882500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03571593119825878,"open_date":"2018-01-25 10:25:00+00:00","close_date":"2018-01-25 16:15:00+00:00","open_rate":0.02799871,"close_rate":0.028139054411027563,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":350,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.025198839,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025198839,"stop_loss_ratio":0.1,"min_rate":0.02799871,"max_rate":0.028139054411027563,"is_open":false,"open_timestamp":1516875900000.0,"close_timestamp":1516896900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.024516401717913305,"open_date":"2018-01-25 11:00:00+00:00","close_date":"2018-01-25 11:45:00+00:00","open_rate":0.04078902,"close_rate":0.0409934762406015,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.036710118,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.036710118,"stop_loss_ratio":0.1,"min_rate":0.04078902,"max_rate":0.0409934762406015,"is_open":false,"open_timestamp":1516878000000.0,"close_timestamp":1516880700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":34.602076124567475,"open_date":"2018-01-25 13:05:00+00:00","close_date":"2018-01-25 13:45:00+00:00","open_rate":2.89e-05,"close_rate":2.904486215538847e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":2.601e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.601e-05,"stop_loss_ratio":0.1,"min_rate":2.89e-05,"max_rate":2.904486215538847e-05,"is_open":false,"open_timestamp":1516885500000.0,"close_timestamp":1516887900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02432912439481303,"open_date":"2018-01-25 13:20:00+00:00","close_date":"2018-01-25 14:05:00+00:00","open_rate":0.041103,"close_rate":0.04130903007518797,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0369927,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0369927,"stop_loss_ratio":0.1,"min_rate":0.041103,"max_rate":0.04130903007518797,"is_open":false,"open_timestamp":1516886400000.0,"close_timestamp":1516889100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":18.42299189388357,"open_date":"2018-01-25 15:45:00+00:00","close_date":"2018-01-25 16:15:00+00:00","open_rate":5.428e-05,"close_rate":5.509624060150376e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":4.8852000000000006e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.8852000000000006e-05,"stop_loss_ratio":0.1,"min_rate":5.428e-05,"max_rate":5.509624060150376e-05,"is_open":false,"open_timestamp":1516895100000.0,"close_timestamp":1516896900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":18.47063169560399,"open_date":"2018-01-25 17:45:00+00:00","close_date":"2018-01-25 23:15:00+00:00","open_rate":5.414e-05,"close_rate":5.441137844611528e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":330,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.8726e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.8726e-05,"stop_loss_ratio":0.1,"min_rate":5.414e-05,"max_rate":5.441137844611528e-05,"is_open":false,"open_timestamp":1516902300000.0,"close_timestamp":1516922100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02415005686130888,"open_date":"2018-01-25 21:15:00+00:00","close_date":"2018-01-25 21:55:00+00:00","open_rate":0.04140777,"close_rate":0.0416153277443609,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.037266993000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.037266993000000005,"stop_loss_ratio":0.1,"min_rate":0.04140777,"max_rate":0.0416153277443609,"is_open":false,"open_timestamp":1516914900000.0,"close_timestamp":1516917300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.3932224183965176,"open_date":"2018-01-26 02:05:00+00:00","close_date":"2018-01-26 02:45:00+00:00","open_rate":0.00254309,"close_rate":0.002555837318295739,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002288781,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002288781,"stop_loss_ratio":0.1,"min_rate":0.00254309,"max_rate":0.002555837318295739,"is_open":false,"open_timestamp":1516932300000.0,"close_timestamp":1516934700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":17.834849295523455,"open_date":"2018-01-26 02:55:00+00:00","close_date":"2018-01-26 15:10:00+00:00","open_rate":5.607e-05,"close_rate":5.6351052631578935e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":735,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":5.0463e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.0463e-05,"stop_loss_ratio":0.1,"min_rate":5.607e-05,"max_rate":5.6351052631578935e-05,"is_open":false,"open_timestamp":1516935300000.0,"close_timestamp":1516979400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.39400171784748983,"open_date":"2018-01-26 06:10:00+00:00","close_date":"2018-01-26 09:25:00+00:00","open_rate":0.00253806,"close_rate":0.0025507821052631577,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":195,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002284254,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002284254,"stop_loss_ratio":0.1,"min_rate":0.00253806,"max_rate":0.0025507821052631577,"is_open":false,"open_timestamp":1516947000000.0,"close_timestamp":1516958700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.024096385542168672,"open_date":"2018-01-26 07:25:00+00:00","close_date":"2018-01-26 09:55:00+00:00","open_rate":0.0415,"close_rate":0.04170802005012531,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":150,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.03735,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.03735,"stop_loss_ratio":0.1,"min_rate":0.0415,"max_rate":0.04170802005012531,"is_open":false,"open_timestamp":1516951500000.0,"close_timestamp":1516960500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":18.793459875963165,"open_date":"2018-01-26 09:55:00+00:00","close_date":"2018-01-26 10:25:00+00:00","open_rate":5.321e-05,"close_rate":5.401015037593984e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":4.7889e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7889e-05,"stop_loss_ratio":0.1,"min_rate":5.321e-05,"max_rate":5.401015037593984e-05,"is_open":false,"open_timestamp":1516960500000.0,"close_timestamp":1516962300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.036074437437185386,"open_date":"2018-01-26 16:05:00+00:00","close_date":"2018-01-26 16:45:00+00:00","open_rate":0.02772046,"close_rate":0.02785940967418546,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.024948414,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.024948414,"stop_loss_ratio":0.1,"min_rate":0.02772046,"max_rate":0.02785940967418546,"is_open":false,"open_timestamp":1516982700000.0,"close_timestamp":1516985100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010569326272036914,"open_date":"2018-01-26 23:35:00+00:00","close_date":"2018-01-27 00:15:00+00:00","open_rate":0.09461341,"close_rate":0.09508766268170424,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.085152069,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.085152069,"stop_loss_ratio":0.1,"min_rate":0.09461341,"max_rate":0.09508766268170424,"is_open":false,"open_timestamp":1517009700000.0,"close_timestamp":1517012100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":17.809439002671414,"open_date":"2018-01-27 00:35:00+00:00","close_date":"2018-01-27 01:30:00+00:00","open_rate":5.615e-05,"close_rate":5.643145363408521e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":5.0535e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.0535e-05,"stop_loss_ratio":0.1,"min_rate":5.615e-05,"max_rate":5.643145363408521e-05,"is_open":false,"open_timestamp":1517013300000.0,"close_timestamp":1517016600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":17.998560115190784,"open_date":"2018-01-27 00:45:00+00:00","close_date":"2018-01-30 04:45:00+00:00","open_rate":5.556e-05,"close_rate":5.144e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":4560,"profit_ratio":-0.07877175,"profit_abs":-7.415406767458598e-05,"exit_reason":"force_exit","initial_stop_loss_abs":5.0004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.0004e-05,"stop_loss_ratio":0.1,"min_rate":5.144e-05,"max_rate":5.556e-05,"is_open":false,"open_timestamp":1517013900000.0,"close_timestamp":1517287500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014492751522789634,"open_date":"2018-01-27 02:30:00+00:00","close_date":"2018-01-27 11:25:00+00:00","open_rate":0.06900001,"close_rate":0.06934587471177944,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":535,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.062100009000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.062100009000000005,"stop_loss_ratio":0.1,"min_rate":0.06900001,"max_rate":0.06934587471177944,"is_open":false,"open_timestamp":1517020200000.0,"close_timestamp":1517052300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010582027378879436,"open_date":"2018-01-27 06:25:00+00:00","close_date":"2018-01-27 07:05:00+00:00","open_rate":0.09449985,"close_rate":0.0949735334586466,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.085049865,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.085049865,"stop_loss_ratio":0.1,"min_rate":0.09449985,"max_rate":0.0949735334586466,"is_open":false,"open_timestamp":1517034300000.0,"close_timestamp":1517036700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02434885085598385,"open_date":"2018-01-27 09:40:00+00:00","close_date":"2018-01-30 04:40:00+00:00","open_rate":0.0410697,"close_rate":0.03928809,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":4020,"profit_ratio":-0.04815133,"profit_abs":-4.338015617352949e-05,"exit_reason":"force_exit","initial_stop_loss_abs":0.03696273,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.03696273,"stop_loss_ratio":0.1,"min_rate":0.03928809,"max_rate":0.0410697,"is_open":false,"open_timestamp":1517046000000.0,"close_timestamp":1517287200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03508771929824561,"open_date":"2018-01-27 11:45:00+00:00","close_date":"2018-01-27 12:30:00+00:00","open_rate":0.0285,"close_rate":0.02864285714285714,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.025650000000000003,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025650000000000003,"stop_loss_ratio":0.1,"min_rate":0.0285,"max_rate":0.02864285714285714,"is_open":false,"open_timestamp":1517053500000.0,"close_timestamp":1517056200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.034887307020861215,"open_date":"2018-01-27 12:35:00+00:00","close_date":"2018-01-27 15:25:00+00:00","open_rate":0.02866372,"close_rate":0.02880739779448621,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":170,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.025797348,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025797348,"stop_loss_ratio":0.1,"min_rate":0.02866372,"max_rate":0.02880739779448621,"is_open":false,"open_timestamp":1517056500000.0,"close_timestamp":1517066700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010484268355332824,"open_date":"2018-01-27 15:50:00+00:00","close_date":"2018-01-27 16:50:00+00:00","open_rate":0.095381,"close_rate":0.09585910025062656,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0858429,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0858429,"stop_loss_ratio":0.1,"min_rate":0.095381,"max_rate":0.09585910025062656,"is_open":false,"open_timestamp":1517068200000.0,"close_timestamp":1517071800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014794886650455415,"open_date":"2018-01-27 17:05:00+00:00","close_date":"2018-01-27 17:45:00+00:00","open_rate":0.06759092,"close_rate":0.06792972160401002,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.060831828,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.060831828,"stop_loss_ratio":0.1,"min_rate":0.06759092,"max_rate":0.06792972160401002,"is_open":false,"open_timestamp":1517072700000.0,"close_timestamp":1517075100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.38684569885609726,"open_date":"2018-01-27 23:40:00+00:00","close_date":"2018-01-28 01:05:00+00:00","open_rate":0.00258501,"close_rate":0.002597967443609022,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":85,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002326509,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002326509,"stop_loss_ratio":0.1,"min_rate":0.00258501,"max_rate":0.002597967443609022,"is_open":false,"open_timestamp":1517096400000.0,"close_timestamp":1517101500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014928710926711672,"open_date":"2018-01-28 02:25:00+00:00","close_date":"2018-01-28 08:10:00+00:00","open_rate":0.06698502,"close_rate":0.0673207845112782,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":345,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.060286518,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.060286518,"stop_loss_ratio":0.1,"min_rate":0.06698502,"max_rate":0.0673207845112782,"is_open":false,"open_timestamp":1517106300000.0,"close_timestamp":1517127000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014767187899175548,"open_date":"2018-01-28 10:25:00+00:00","close_date":"2018-01-28 16:30:00+00:00","open_rate":0.0677177,"close_rate":0.06805713709273183,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":365,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.06094593000000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.06094593000000001,"stop_loss_ratio":0.1,"min_rate":0.0677177,"max_rate":0.06805713709273183,"is_open":false,"open_timestamp":1517135100000.0,"close_timestamp":1517157000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":19.175455417066157,"open_date":"2018-01-28 20:35:00+00:00","close_date":"2018-01-28 21:35:00+00:00","open_rate":5.215e-05,"close_rate":5.2411403508771925e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.6935e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6935e-05,"stop_loss_ratio":0.1,"min_rate":5.215e-05,"max_rate":5.2411403508771925e-05,"is_open":false,"open_timestamp":1517171700000.0,"close_timestamp":1517175300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.36521808998243305,"open_date":"2018-01-28 22:00:00+00:00","close_date":"2018-01-28 22:30:00+00:00","open_rate":0.00273809,"close_rate":0.002779264285714285,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002464281,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002464281,"stop_loss_ratio":0.1,"min_rate":0.00273809,"max_rate":0.002779264285714285,"is_open":false,"open_timestamp":1517176800000.0,"close_timestamp":1517178600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.3641236272539253,"open_date":"2018-01-29 00:00:00+00:00","close_date":"2018-01-29 00:30:00+00:00","open_rate":0.00274632,"close_rate":0.002787618045112782,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002471688,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002471688,"stop_loss_ratio":0.1,"min_rate":0.00274632,"max_rate":0.002787618045112782,"is_open":false,"open_timestamp":1517184000000.0,"close_timestamp":1517185800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.061634117689115045,"open_date":"2018-01-29 02:15:00+00:00","close_date":"2018-01-29 03:00:00+00:00","open_rate":0.01622478,"close_rate":0.016306107218045113,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.014602302,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.014602302,"stop_loss_ratio":0.1,"min_rate":0.01622478,"max_rate":0.016306107218045113,"is_open":false,"open_timestamp":1517192100000.0,"close_timestamp":1517194800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014492753623188404,"open_date":"2018-01-29 03:05:00+00:00","close_date":"2018-01-29 03:45:00+00:00","open_rate":0.069,"close_rate":0.06934586466165413,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.06210000000000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.06210000000000001,"stop_loss_ratio":0.1,"min_rate":0.069,"max_rate":0.06934586466165413,"is_open":false,"open_timestamp":1517195100000.0,"close_timestamp":1517197500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":11.42204454597373,"open_date":"2018-01-29 05:20:00+00:00","close_date":"2018-01-29 06:55:00+00:00","open_rate":8.755e-05,"close_rate":8.798884711779448e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":95,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":7.879500000000001e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":7.879500000000001e-05,"stop_loss_ratio":0.1,"min_rate":8.755e-05,"max_rate":8.798884711779448e-05,"is_open":false,"open_timestamp":1517203200000.0,"close_timestamp":1517208900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014650376815016871,"open_date":"2018-01-29 07:00:00+00:00","close_date":"2018-01-29 19:25:00+00:00","open_rate":0.06825763,"close_rate":0.06859977350877192,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":745,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.061431867,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.061431867,"stop_loss_ratio":0.1,"min_rate":0.06825763,"max_rate":0.06859977350877192,"is_open":false,"open_timestamp":1517209200000.0,"close_timestamp":1517253900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014894490408841846,"open_date":"2018-01-29 19:45:00+00:00","close_date":"2018-01-29 20:25:00+00:00","open_rate":0.06713892,"close_rate":0.06747545593984962,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.060425028000000006,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.060425028000000006,"stop_loss_ratio":0.1,"min_rate":0.06713892,"max_rate":0.06747545593984962,"is_open":false,"open_timestamp":1517255100000.0,"close_timestamp":1517257500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":11.193194537721066,"open_date":"2018-01-29 23:30:00+00:00","close_date":"2018-01-30 04:45:00+00:00","open_rate":8.934e-05,"close_rate":8.8e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":315,"profit_ratio":-0.0199116,"profit_abs":-1.4998880680546292e-05,"exit_reason":"force_exit","initial_stop_loss_abs":8.0406e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.0406e-05,"stop_loss_ratio":0.1,"min_rate":8.8e-05,"max_rate":8.934e-05,"is_open":false,"open_timestamp":1517268600000.0,"close_timestamp":1517287500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null}],"locks":[],"best_pair":{"key":"LTC/BTC","trades":8,"profit_mean":0.00748129625,"profit_mean_pct":0.748129625,"profit_sum":0.05985037,"profit_sum_pct":5.99,"profit_total_abs":0.00010025062656641558,"profit_total":0.010025062656641558,"profit_total_pct":1.0,"duration_avg":"1:59:00","wins":8,"draws":0,"losses":0},"worst_pair":{"key":"XMR/BTC","trades":16,"profit_mean":-0.0027899012500000007,"profit_mean_pct":-0.2789901250000001,"profit_sum":-0.04463842000000001,"profit_sum_pct":-4.46,"profit_total_abs":3.533834586465928e-05,"profit_total":0.003533834586465928,"profit_total_pct":0.35,"duration_avg":"8:41:00","wins":15,"draws":0,"losses":1},"results_per_pair":[{"key":"XLM/BTC","trades":21,"profit_mean":0.0026243899999999994,"profit_mean_pct":0.2624389999999999,"profit_sum":0.05511218999999999,"profit_sum_pct":5.51,"profit_total_abs":0.00016065162907268006,"profit_total":0.016065162907268005,"profit_total_pct":1.61,"duration_avg":"3:21:00","wins":20,"draws":0,"losses":1},{"key":"ETC/BTC","trades":20,"profit_mean":0.0022568569999999997,"profit_mean_pct":0.22568569999999996,"profit_sum":0.04513713999999999,"profit_sum_pct":4.51,"profit_total_abs":0.00014561403508771753,"profit_total":0.014561403508771753,"profit_total_pct":1.46,"duration_avg":"1:45:00","wins":19,"draws":0,"losses":1},{"key":"ETH/BTC","trades":21,"profit_mean":0.0009500057142857142,"profit_mean_pct":0.09500057142857142,"profit_sum":0.01995012,"profit_sum_pct":2.0,"profit_total_abs":0.00012531328320801774,"profit_total":0.012531328320801774,"profit_total_pct":1.25,"duration_avg":"2:17:00","wins":21,"draws":0,"losses":0},{"key":"ADA/BTC","trades":29,"profit_mean":-0.0011598141379310352,"profit_mean_pct":-0.11598141379310352,"profit_sum":-0.03363461000000002,"profit_sum_pct":-3.36,"profit_total_abs":0.00011156021803969656,"profit_total":0.011156021803969657,"profit_total_pct":1.12,"duration_avg":"5:35:00","wins":27,"draws":0,"losses":2},{"key":"TRX/BTC","trades":15,"profit_mean":0.0023467073333333323,"profit_mean_pct":0.23467073333333321,"profit_sum":0.035200609999999986,"profit_sum_pct":3.52,"profit_total_abs":0.00011056502909388873,"profit_total":0.011056502909388873,"profit_total_pct":1.11,"duration_avg":"2:28:00","wins":13,"draws":0,"losses":2},{"key":"DASH/BTC","trades":16,"profit_mean":0.0018703237499999997,"profit_mean_pct":0.18703237499999997,"profit_sum":0.029925179999999996,"profit_sum_pct":2.99,"profit_total_abs":0.0001102756892230564,"profit_total":0.01102756892230564,"profit_total_pct":1.1,"duration_avg":"3:03:00","wins":16,"draws":0,"losses":0},{"key":"LTC/BTC","trades":8,"profit_mean":0.00748129625,"profit_mean_pct":0.748129625,"profit_sum":0.05985037,"profit_sum_pct":5.99,"profit_total_abs":0.00010025062656641558,"profit_total":0.010025062656641558,"profit_total_pct":1.0,"duration_avg":"1:59:00","wins":8,"draws":0,"losses":0},{"key":"ZEC/BTC","trades":21,"profit_mean":-0.00039290904761904774,"profit_mean_pct":-0.03929090476190478,"profit_sum":-0.008251090000000003,"profit_sum_pct":-0.83,"profit_total_abs":9.697072101945111e-05,"profit_total":0.009697072101945111,"profit_total_pct":0.97,"duration_avg":"4:17:00","wins":20,"draws":0,"losses":1},{"key":"NXT/BTC","trades":12,"profit_mean":-0.0012261025000000006,"profit_mean_pct":-0.12261025000000006,"profit_sum":-0.014713230000000008,"profit_sum_pct":-1.47,"profit_total_abs":4.536340852130151e-05,"profit_total":0.004536340852130151,"profit_total_pct":0.45,"duration_avg":"0:57:00","wins":11,"draws":0,"losses":1},{"key":"XMR/BTC","trades":16,"profit_mean":-0.0027899012500000007,"profit_mean_pct":-0.2789901250000001,"profit_sum":-0.04463842000000001,"profit_sum_pct":-4.46,"profit_total_abs":3.533834586465928e-05,"profit_total":0.003533834586465928,"profit_total_pct":0.35,"duration_avg":"8:41:00","wins":15,"draws":0,"losses":1},{"key":"TOTAL","trades":179,"profit_mean":0.0008041243575418989,"profit_mean_pct":0.0804124357541899,"profit_sum":0.1439382599999999,"profit_sum_pct":14.39,"profit_total_abs":0.0010419029856968845,"profit_total":0.10419029856968845,"profit_total_pct":10.42,"duration_avg":"3:40:00","wins":170,"draws":0,"losses":9}],"results_per_enter_tag":[{"key":"buy_tag","trades":1,"profit_mean":0.03990025,"profit_mean_pct":3.9900249999999997,"profit_sum":0.03990025,"profit_sum_pct":3.99,"profit_total_abs":4.5112781954887056e-05,"profit_total":0.004511278195488706,"profit_total_pct":0.45,"duration_avg":"0:15:00","wins":1,"draws":0,"losses":0},{"key":"TOTAL","trades":179,"profit_mean":0.0008041243575418989,"profit_mean_pct":0.0804124357541899,"profit_sum":0.1439382599999999,"profit_sum_pct":14.39,"profit_total_abs":0.0010419029856968845,"profit_total":0.10419029856968845,"profit_total_pct":10.42,"duration_avg":"3:40:00","wins":170,"draws":0,"losses":9}],"exit_reason_summary":[{"exit_reason":"roi","trades":170,"wins":170,"draws":0,"losses":0,"profit_mean":0.005398268352941177,"profit_mean_pct":0.54,"profit_sum":0.91770562,"profit_sum_pct":91.77,"profit_total_abs":0.0017744360902255465,"profit_total":0.30590187333333335,"profit_total_pct":30.59},{"exit_reason":"stop_loss","trades":6,"wins":0,"draws":0,"losses":6,"profit_mean":-0.10448878000000002,"profit_mean_pct":-10.45,"profit_sum":-0.6269326800000001,"profit_sum_pct":-62.69,"profit_total_abs":-0.0006000000000000003,"profit_total":-0.20897756000000003,"profit_total_pct":-20.9},{"exit_reason":"force_exit","trades":3,"wins":0,"draws":0,"losses":3,"profit_mean":-0.04894489333333333,"profit_mean_pct":-4.89,"profit_sum":-0.14683468,"profit_sum_pct":-14.68,"profit_total_abs":-0.00013253310452866177,"profit_total":-0.04894489333333333,"profit_total_pct":-4.89}],"left_open_trades":[{"key":"TRX/BTC","trades":1,"profit_mean":-0.0199116,"profit_mean_pct":-1.9911600000000003,"profit_sum":-0.0199116,"profit_sum_pct":-1.99,"profit_total_abs":-1.4998880680546292e-05,"profit_total":-0.0014998880680546292,"profit_total_pct":-0.15,"duration_avg":"5:15:00","wins":0,"draws":0,"losses":1},{"key":"ZEC/BTC","trades":1,"profit_mean":-0.04815133,"profit_mean_pct":-4.815133,"profit_sum":-0.04815133,"profit_sum_pct":-4.82,"profit_total_abs":-4.338015617352949e-05,"profit_total":-0.004338015617352949,"profit_total_pct":-0.43,"duration_avg":"2 days, 19:00:00","wins":0,"draws":0,"losses":1},{"key":"ADA/BTC","trades":1,"profit_mean":-0.07877175,"profit_mean_pct":-7.877175,"profit_sum":-0.07877175,"profit_sum_pct":-7.88,"profit_total_abs":-7.415406767458598e-05,"profit_total":-0.007415406767458598,"profit_total_pct":-0.74,"duration_avg":"3 days, 4:00:00","wins":0,"draws":0,"losses":1},{"key":"TOTAL","trades":3,"profit_mean":-0.04894489333333333,"profit_mean_pct":-4.894489333333333,"profit_sum":-0.14683468,"profit_sum_pct":-14.68,"profit_total_abs":-0.00013253310452866177,"profit_total":-0.013253310452866176,"profit_total_pct":-1.33,"duration_avg":"2 days, 1:25:00","wins":0,"draws":0,"losses":3}],"total_trades":179,"trade_count_long":179,"trade_count_short":0,"total_volume":0.17900000000000005,"avg_stake_amount":0.0010000000000000002,"profit_mean":0.0008041243575418989,"profit_median":0.0,"profit_total":0.10419029856968845,"profit_total_long":0.10419029856968845,"profit_total_short":0.0,"profit_total_abs":0.0010419029856968845,"profit_total_long_abs":0.0010419029856968845,"profit_total_short_abs":0.0,"cagr":5.712688499973264,"profit_factor":2.4223288739520954,"backtest_start":"2018-01-10 07:15:00","backtest_start_ts":1515568500000,"backtest_end":"2018-01-30 04:45:00","backtest_end_ts":1517287500000,"backtest_days":19,"backtest_run_start_ts":"2020-10-01 18:00:00+00:00","backtest_run_end_ts":"2020-10-01 18:01:00+00:00","trades_per_day":9.42,"market_change":1.22,"pairlist":["TRX/BTC","ADA/BTC","XLM/BTC","ETH/BTC","XMR/BTC","ZEC/BTC","NXT/BTC","LTC/BTC","ETC/BTC","DASH/BTC"],"stake_amount":0.001,"stake_currency":"BTC","stake_currency_decimals":8,"starting_balance":0.01,"dry_run_wallet":0.01,"final_balance":0.011041902985696884,"rejected_signals":0,"timedout_entry_orders":0,"timedout_exit_orders":0,"canceled_trade_entries":0,"canceled_entry_orders":0,"replaced_entry_orders":0,"max_open_trades":3,"max_open_trades_setting":3,"timeframe":"5m","timeframe_detail":"","timerange":"","enable_protections":false,"strategy_name":"StrategyTestV3","stoploss":0.1,"trailing_stop":false,"trailing_stop_positive":null,"trailing_stop_positive_offset":0.0,"trailing_only_offset_is_reached":false,"use_custom_stoploss":false,"minimal_roi":{},"use_exit_signal":true,"exit_profit_only":false,"exit_profit_offset":false,"ignore_roi_if_entry_signal":false,"backtest_best_day":0.17955111999999998,"backtest_worst_day":-0.14683468,"backtest_best_day_abs":0.000245614,"backtest_worst_day_abs":-0.0001325331,"winning_days":19,"draw_days":0,"losing_days":2,"daily_profit":[["2018-01-10",0.000245614],["2018-01-11",0.0001055138],["2018-01-12",4.51128e-05],["2018-01-13",3.00752e-05],["2018-01-14",3.50877e-05],["2018-01-15",6.51629e-05],["2018-01-16",5.11278e-05],["2018-01-17",7.01754e-05],["2018-01-18",8.5213e-05],["2018-01-19",3.00752e-05],["2018-01-20",2.50627e-05],["2018-01-21",4.01003e-05],["2018-01-22",7.01754e-05],["2018-01-23",8.5213e-05],["2018-01-24",8.02005e-05],["2018-01-25",-4.48622e-05],["2018-01-26",4.01003e-05],["2018-01-27",4.01003e-05],["2018-01-28",3.50877e-05],["2018-01-29",4.01003e-05],["2018-01-30",-0.0001325331]],"wins":48,"losses":9,"draws":122,"holding_avg":"3:40:00","holding_avg_s":13200.0,"winner_holding_avg":"0:24:00","winner_holding_avg_s":1440.0,"loser_holding_avg":"1 day, 5:57:00","loser_holding_avg_s":107820.0,"max_drawdown":0.21142322000000008,"max_drawdown_account":0.018740312808228732,"max_relative_drawdown":0.018740312808228732,"max_drawdown_abs":0.0002000000000000001,"drawdown_start":"2018-01-16 19:30:00","drawdown_start_ts":1516131000000.0,"drawdown_end":"2018-01-16 22:25:00","drawdown_end_ts":1516141500000.0,"max_drawdown_low":0.0004721804511278108,"max_drawdown_high":0.0006721804511278109,"csum_min":0.010045112781954888,"csum_max":0.011069172932330812}},"strategy_comparison":[{"key":"StrategyTestV3","trades":179,"profit_mean":0.0008041243575418989,"profit_mean_pct":0.0804124357541899,"profit_sum":0.1439382599999999,"profit_sum_pct":14.39,"profit_total_abs":0.0010419029856968845,"profit_total":0.10419029856968845,"profit_total_pct":10.42,"duration_avg":"3:40:00","wins":170,"draws":0,"losses":9,"max_drawdown_account":0.018740312808228732,"max_drawdown_abs":"0.0002"}]} From 882e68c68b8fc0ec2d501d58a01a404c1b7d9084 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 26 Dec 2022 15:30:39 +0100 Subject: [PATCH 147/795] Rename backtest-result from new to "not new". --- tests/commands/test_commands.py | 2 +- tests/data/test_btanalysis.py | 16 ++++++++-------- tests/optimize/test_optimize_reports.py | 8 ++++---- tests/rpc/test_rpc_apiserver.py | 2 +- tests/test_plotting.py | 12 ++++++------ .../testdata/backtest_results/.last_result.json | 2 +- ...test-result_new.json => backtest-result.json} | 0 ...t_new.meta.json => backtest-result.meta.json} | 0 8 files changed, 21 insertions(+), 21 deletions(-) rename tests/testdata/backtest_results/{backtest-result_new.json => backtest-result.json} (100%) rename tests/testdata/backtest_results/{backtest-result_new.meta.json => backtest-result.meta.json} (100%) diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index a1d73f7ef..d568f48f6 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -1529,7 +1529,7 @@ def test_backtesting_show(mocker, testdatadir, capsys): args = [ "backtesting-show", "--export-filename", - f"{testdatadir / 'backtest_results/backtest-result_new.json'}", + f"{testdatadir / 'backtest_results/backtest-result.json'}", "--show-pair-list" ] pargs = get_args(args) diff --git a/tests/data/test_btanalysis.py b/tests/data/test_btanalysis.py index 95de6b53e..1cc1aa0c9 100644 --- a/tests/data/test_btanalysis.py +++ b/tests/data/test_btanalysis.py @@ -30,10 +30,10 @@ def test_get_latest_backtest_filename(testdatadir, mocker): testdir_bt = testdatadir / "backtest_results" res = get_latest_backtest_filename(testdir_bt) - assert res == 'backtest-result_new.json' + assert res == 'backtest-result.json' res = get_latest_backtest_filename(str(testdir_bt)) - assert res == 'backtest-result_new.json' + assert res == 'backtest-result.json' mocker.patch("freqtrade.data.btanalysis.json_load", return_value={}) @@ -81,7 +81,7 @@ def test_load_backtest_data_old_format(testdatadir, mocker): def test_load_backtest_data_new_format(testdatadir): - filename = testdatadir / "backtest_results/backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result.json" bt_data = load_backtest_data(filename) assert isinstance(bt_data, DataFrame) assert set(bt_data.columns) == set(BT_DATA_COLUMNS) @@ -182,7 +182,7 @@ def test_extract_trades_of_period(testdatadir): def test_analyze_trade_parallelism(testdatadir): - filename = testdatadir / "backtest_results/backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result.json" bt_data = load_backtest_data(filename) res = analyze_trade_parallelism(bt_data, "5m") @@ -256,7 +256,7 @@ def test_combine_dataframes_with_mean_no_data(testdatadir): def test_create_cum_profit(testdatadir): - filename = testdatadir / "backtest_results/backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result.json" bt_data = load_backtest_data(filename) timerange = TimeRange.parse_timerange("20180110-20180112") @@ -272,7 +272,7 @@ def test_create_cum_profit(testdatadir): def test_create_cum_profit1(testdatadir): - filename = testdatadir / "backtest_results/backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result.json" bt_data = load_backtest_data(filename) # Move close-time to "off" the candle, to make sure the logic still works bt_data['close_date'] = bt_data.loc[:, 'close_date'] + DateOffset(seconds=20) @@ -294,7 +294,7 @@ def test_create_cum_profit1(testdatadir): def test_calculate_max_drawdown(testdatadir): - filename = testdatadir / "backtest_results/backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result.json" bt_data = load_backtest_data(filename) _, hdate, lowdate, hval, lval, drawdown = calculate_max_drawdown( bt_data, value_col="profit_abs") @@ -318,7 +318,7 @@ def test_calculate_max_drawdown(testdatadir): def test_calculate_csum(testdatadir): - filename = testdatadir / "backtest_results/backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result.json" bt_data = load_backtest_data(filename) csum_min, csum_max = calculate_csum(bt_data) diff --git a/tests/optimize/test_optimize_reports.py b/tests/optimize/test_optimize_reports.py index 403075795..549202284 100644 --- a/tests/optimize/test_optimize_reports.py +++ b/tests/optimize/test_optimize_reports.py @@ -308,7 +308,7 @@ def test_generate_pair_metrics(): def test_generate_daily_stats(testdatadir): - filename = testdatadir / "backtest_results/backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result.json" bt_data = load_backtest_data(filename) res = generate_daily_stats(bt_data) assert isinstance(res, dict) @@ -328,7 +328,7 @@ def test_generate_daily_stats(testdatadir): def test_generate_trading_stats(testdatadir): - filename = testdatadir / "backtest_results/backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result.json" bt_data = load_backtest_data(filename) res = generate_trading_stats(bt_data) assert isinstance(res, dict) @@ -444,7 +444,7 @@ def test_generate_edge_table(): def test_generate_periodic_breakdown_stats(testdatadir): - filename = testdatadir / "backtest_results/backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result.json" bt_data = load_backtest_data(filename).to_dict(orient='records') res = generate_periodic_breakdown_stats(bt_data, 'day') @@ -472,7 +472,7 @@ def test__get_resample_from_period(): def test_show_sorted_pairlist(testdatadir, default_conf, capsys): - filename = testdatadir / "backtest_results/backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result.json" bt_data = load_backtest_stats(filename) default_conf['backtest_show_pair_list'] = True diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index aea8ea059..2a2a38196 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1709,7 +1709,7 @@ def test_api_backtest_history(botclient, mocker, testdatadir): mocker.patch('freqtrade.data.btanalysis._get_backtest_files', return_value=[ testdatadir / 'backtest_results/backtest-result_multistrat.json', - testdatadir / 'backtest_results/backtest-result_new.json' + testdatadir / 'backtest_results/backtest-result.json' ]) rc = client_get(client, f"{BASE_URI}/backtest/history") diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 64089c4c6..7662ea7f1 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -46,7 +46,7 @@ def test_init_plotscript(default_conf, mocker, testdatadir): default_conf['trade_source'] = "file" default_conf['timeframe'] = "5m" default_conf["datadir"] = testdatadir - default_conf['exportfilename'] = testdatadir / "backtest-result_new.json" + default_conf['exportfilename'] = testdatadir / "backtest-result.json" supported_markets = ["TRX/BTC", "ADA/BTC"] ret = init_plotscript(default_conf, supported_markets) assert "ohlcv" in ret @@ -158,7 +158,7 @@ def test_plot_trades(testdatadir, caplog): assert fig == fig1 assert log_has("No trades found.", caplog) pair = "ADA/BTC" - filename = testdatadir / "backtest_results/backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result.json" trades = load_backtest_data(filename) trades = trades.loc[trades['pair'] == pair] @@ -299,7 +299,7 @@ def test_generate_plot_file(mocker, caplog): def test_add_profit(testdatadir): - filename = testdatadir / "backtest_results/backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result.json" bt_data = load_backtest_data(filename) timerange = TimeRange.parse_timerange("20180110-20180112") @@ -319,7 +319,7 @@ def test_add_profit(testdatadir): def test_generate_profit_graph(testdatadir): - filename = testdatadir / "backtest_results/backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result.json" trades = load_backtest_data(filename) timerange = TimeRange.parse_timerange("20180110-20180112") pairs = ["TRX/BTC", "XLM/BTC"] @@ -395,7 +395,7 @@ def test_load_and_plot_trades(default_conf, mocker, caplog, testdatadir): default_conf['trade_source'] = 'file' default_conf["datadir"] = testdatadir - default_conf['exportfilename'] = testdatadir / "backtest-result_new.json" + default_conf['exportfilename'] = testdatadir / "backtest-result.json" default_conf['indicators1'] = ["sma5", "ema10"] default_conf['indicators2'] = ["macd"] default_conf['pairs'] = ["ETH/BTC", "LTC/BTC"] @@ -466,7 +466,7 @@ def test_plot_profit(default_conf, mocker, testdatadir): match=r"No trades found, cannot generate Profit-plot.*"): plot_profit(default_conf) - default_conf['exportfilename'] = testdatadir / "backtest_results/backtest-result_new.json" + default_conf['exportfilename'] = testdatadir / "backtest_results/backtest-result.json" plot_profit(default_conf) diff --git a/tests/testdata/backtest_results/.last_result.json b/tests/testdata/backtest_results/.last_result.json index 98448e10f..7ebab4613 100644 --- a/tests/testdata/backtest_results/.last_result.json +++ b/tests/testdata/backtest_results/.last_result.json @@ -1 +1 @@ -{"latest_backtest":"backtest-result_new.json"} +{"latest_backtest":"backtest-result.json"} diff --git a/tests/testdata/backtest_results/backtest-result_new.json b/tests/testdata/backtest_results/backtest-result.json similarity index 100% rename from tests/testdata/backtest_results/backtest-result_new.json rename to tests/testdata/backtest_results/backtest-result.json diff --git a/tests/testdata/backtest_results/backtest-result_new.meta.json b/tests/testdata/backtest_results/backtest-result.meta.json similarity index 100% rename from tests/testdata/backtest_results/backtest-result_new.meta.json rename to tests/testdata/backtest_results/backtest-result.meta.json From 20901c833adaa272ce8d9802521188daac13acdd Mon Sep 17 00:00:00 2001 From: Robert Caulk Date: Tue, 27 Dec 2022 10:08:09 +0100 Subject: [PATCH 148/795] Improve `purge_old_models` explanation --- docs/freqai-parameter-table.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/freqai-parameter-table.md b/docs/freqai-parameter-table.md index d05ce80f3..72ee1e6b3 100644 --- a/docs/freqai-parameter-table.md +++ b/docs/freqai-parameter-table.md @@ -15,7 +15,7 @@ Mandatory parameters are marked as **Required** and have to be set in one of the | `identifier` | **Required.**
A unique ID for the current model. If models are saved to disk, the `identifier` allows for reloading specific pre-trained models/data.
**Datatype:** String. | `live_retrain_hours` | Frequency of retraining during dry/live runs.
**Datatype:** Float > 0.
Default: `0` (models retrain as often as possible). | `expiration_hours` | Avoid making predictions if a model is more than `expiration_hours` old.
**Datatype:** Positive integer.
Default: `0` (models never expire). -| `purge_old_models` | Delete obsolete models.
**Datatype:** Boolean.
Default: `False` (all historic models remain on disk). +| `purge_old_models` | Delete all unused models during live runs (not relevant to backtesting). If set to false (not default), dry/live runs will accumulate all unused models to disk. If
**Datatype:** Boolean.
Default: `True`. | `save_backtest_models` | Save models to disk when running backtesting. Backtesting operates most efficiently by saving the prediction data and reusing them directly for subsequent runs (when you wish to tune entry/exit parameters). Saving backtesting models to disk also allows to use the same model files for starting a dry/live instance with the same model `identifier`.
**Datatype:** Boolean.
Default: `False` (no models are saved). | `fit_live_predictions_candles` | Number of historical candles to use for computing target (label) statistics from prediction data, instead of from the training dataset (more information can be found [here](freqai-configuration.md#creating-a-dynamic-target-threshold)).
**Datatype:** Positive integer. | `follow_mode` | Use a `follower` that will look for models associated with a specific `identifier` and load those for inferencing. A `follower` will **not** train new models.
**Datatype:** Boolean.
Default: `False`. From 6f2c3e2528bccafb1cb61f13f55a6af3b8767b30 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 27 Dec 2022 13:41:51 +0100 Subject: [PATCH 149/795] Split migration and persistence tests --- tests/persistence/test_migrations.py | 411 ++++++++++++++++++++++++++ tests/persistence/test_persistence.py | 403 +------------------------ 2 files changed, 413 insertions(+), 401 deletions(-) create mode 100644 tests/persistence/test_migrations.py diff --git a/tests/persistence/test_migrations.py b/tests/persistence/test_migrations.py new file mode 100644 index 000000000..1cd236005 --- /dev/null +++ b/tests/persistence/test_migrations.py @@ -0,0 +1,411 @@ +# pragma pylint: disable=missing-docstring, C0103 +import logging +from pathlib import Path +from unittest.mock import MagicMock + +import pytest +from sqlalchemy import create_engine, text + +from freqtrade.constants import DEFAULT_DB_PROD_URL +from freqtrade.enums import TradingMode +from freqtrade.exceptions import OperationalException +from freqtrade.persistence import Trade, init_db +from freqtrade.persistence.migrations import get_last_sequence_ids, set_sequence_ids +from freqtrade.persistence.models import PairLock +from tests.conftest import log_has + + +spot, margin, futures = TradingMode.SPOT, TradingMode.MARGIN, TradingMode.FUTURES + + +def test_init_create_session(default_conf): + # Check if init create a session + init_db(default_conf['db_url']) + assert hasattr(Trade, '_session') + assert 'scoped_session' in type(Trade._session).__name__ + + +def test_init_custom_db_url(default_conf, tmpdir): + # Update path to a value other than default, but still in-memory + filename = f"{tmpdir}/freqtrade2_test.sqlite" + assert not Path(filename).is_file() + + default_conf.update({'db_url': f'sqlite:///{filename}'}) + + init_db(default_conf['db_url']) + assert Path(filename).is_file() + r = Trade._session.execute(text("PRAGMA journal_mode")) + assert r.first() == ('wal',) + + +def test_init_invalid_db_url(): + # Update path to a value other than default, but still in-memory + with pytest.raises(OperationalException, match=r'.*no valid database URL*'): + init_db('unknown:///some.url') + + with pytest.raises(OperationalException, match=r'Bad db-url.*For in-memory database, pl.*'): + init_db('sqlite:///') + + +def test_init_prod_db(default_conf, mocker): + default_conf.update({'dry_run': False}) + default_conf.update({'db_url': DEFAULT_DB_PROD_URL}) + + create_engine_mock = mocker.patch('freqtrade.persistence.models.create_engine', MagicMock()) + + init_db(default_conf['db_url']) + assert create_engine_mock.call_count == 1 + assert create_engine_mock.mock_calls[0][1][0] == 'sqlite:///tradesv3.sqlite' + + +def test_init_dryrun_db(default_conf, tmpdir): + filename = f"{tmpdir}/freqtrade2_prod.sqlite" + assert not Path(filename).is_file() + default_conf.update({ + 'dry_run': True, + 'db_url': f'sqlite:///{filename}' + }) + + init_db(default_conf['db_url']) + assert Path(filename).is_file() + + +def test_migrate_new(mocker, default_conf, fee, caplog): + """ + Test Database migration (starting with new pairformat) + """ + caplog.set_level(logging.DEBUG) + amount = 103.223 + # Always create all columns apart from the last! + create_table_old = """CREATE TABLE IF NOT EXISTS "trades" ( + id INTEGER NOT NULL, + exchange VARCHAR NOT NULL, + pair VARCHAR NOT NULL, + is_open BOOLEAN NOT NULL, + fee FLOAT NOT NULL, + open_rate FLOAT, + close_rate FLOAT, + close_profit FLOAT, + stake_amount FLOAT NOT NULL, + amount FLOAT, + open_date DATETIME NOT NULL, + close_date DATETIME, + open_order_id VARCHAR, + stop_loss FLOAT, + initial_stop_loss FLOAT, + max_rate FLOAT, + sell_reason VARCHAR, + strategy VARCHAR, + ticker_interval INTEGER, + stoploss_order_id VARCHAR, + PRIMARY KEY (id), + CHECK (is_open IN (0, 1)) + );""" + create_table_order = """CREATE TABLE orders ( + id INTEGER NOT NULL, + ft_trade_id INTEGER, + ft_order_side VARCHAR(25) NOT NULL, + ft_pair VARCHAR(25) NOT NULL, + ft_is_open BOOLEAN NOT NULL, + order_id VARCHAR(255) NOT NULL, + status VARCHAR(255), + symbol VARCHAR(25), + order_type VARCHAR(50), + side VARCHAR(25), + price FLOAT, + amount FLOAT, + filled FLOAT, + remaining FLOAT, + cost FLOAT, + order_date DATETIME, + order_filled_date DATETIME, + order_update_date DATETIME, + PRIMARY KEY (id) + );""" + insert_table_old = """INSERT INTO trades (exchange, pair, is_open, fee, + open_rate, stake_amount, amount, open_date, + stop_loss, initial_stop_loss, max_rate, ticker_interval, + open_order_id, stoploss_order_id) + VALUES ('binance', 'ETC/BTC', 1, {fee}, + 0.00258580, {stake}, {amount}, + '2019-11-28 12:44:24.000000', + 0.0, 0.0, 0.0, '5m', + 'buy_order', 'dry_stop_order_id222') + """.format(fee=fee.return_value, + stake=default_conf.get("stake_amount"), + amount=amount + ) + insert_orders = f""" + insert into orders ( + ft_trade_id, + ft_order_side, + ft_pair, + ft_is_open, + order_id, + status, + symbol, + order_type, + side, + price, + amount, + filled, + remaining, + cost) + values ( + 1, + 'buy', + 'ETC/BTC', + 0, + 'dry_buy_order', + 'closed', + 'ETC/BTC', + 'limit', + 'buy', + 0.00258580, + {amount}, + {amount}, + 0, + {amount * 0.00258580} + ), + ( + 1, + 'buy', + 'ETC/BTC', + 1, + 'dry_buy_order22', + 'canceled', + 'ETC/BTC', + 'limit', + 'buy', + 0.00258580, + {amount}, + {amount}, + 0, + {amount * 0.00258580} + ), + ( + 1, + 'stoploss', + 'ETC/BTC', + 1, + 'dry_stop_order_id11X', + 'canceled', + 'ETC/BTC', + 'limit', + 'sell', + 0.00258580, + {amount}, + {amount}, + 0, + {amount * 0.00258580} + ), + ( + 1, + 'stoploss', + 'ETC/BTC', + 1, + 'dry_stop_order_id222', + 'open', + 'ETC/BTC', + 'limit', + 'sell', + 0.00258580, + {amount}, + {amount}, + 0, + {amount * 0.00258580} + ) + """ + engine = create_engine('sqlite://') + mocker.patch('freqtrade.persistence.models.create_engine', lambda *args, **kwargs: engine) + + # Create table using the old format + with engine.begin() as connection: + connection.execute(text(create_table_old)) + connection.execute(text(create_table_order)) + connection.execute(text("create index ix_trades_is_open on trades(is_open)")) + connection.execute(text("create index ix_trades_pair on trades(pair)")) + connection.execute(text(insert_table_old)) + connection.execute(text(insert_orders)) + + # fake previous backup + connection.execute(text("create table trades_bak as select * from trades")) + + connection.execute(text("create table trades_bak1 as select * from trades")) + # Run init to test migration + init_db(default_conf['db_url']) + + assert len(Trade.query.filter(Trade.id == 1).all()) == 1 + trade = Trade.query.filter(Trade.id == 1).first() + assert trade.fee_open == fee.return_value + assert trade.fee_close == fee.return_value + assert trade.open_rate_requested is None + assert trade.close_rate_requested is None + assert trade.is_open == 1 + assert trade.amount == amount + assert trade.amount_requested == amount + assert trade.stake_amount == default_conf.get("stake_amount") + assert trade.pair == "ETC/BTC" + assert trade.exchange == "binance" + assert trade.max_rate == 0.0 + assert trade.min_rate is None + assert trade.stop_loss == 0.0 + assert trade.initial_stop_loss == 0.0 + assert trade.exit_reason is None + assert trade.strategy is None + assert trade.timeframe == '5m' + assert trade.stoploss_order_id == 'dry_stop_order_id222' + assert trade.stoploss_last_update is None + assert log_has("trying trades_bak1", caplog) + assert log_has("trying trades_bak2", caplog) + assert log_has("Running database migration for trades - backup: trades_bak2, orders_bak0", + caplog) + assert log_has("Database migration finished.", caplog) + assert pytest.approx(trade.open_trade_value) == trade._calc_open_trade_value( + trade.amount, trade.open_rate) + assert trade.close_profit_abs is None + + orders = trade.orders + assert len(orders) == 4 + assert orders[0].order_id == 'dry_buy_order' + assert orders[0].ft_order_side == 'buy' + + assert orders[-1].order_id == 'dry_stop_order_id222' + assert orders[-1].ft_order_side == 'stoploss' + assert orders[-1].ft_is_open is True + + assert orders[1].order_id == 'dry_buy_order22' + assert orders[1].ft_order_side == 'buy' + assert orders[1].ft_is_open is False + + assert orders[2].order_id == 'dry_stop_order_id11X' + assert orders[2].ft_order_side == 'stoploss' + assert orders[2].ft_is_open is False + + +def test_migrate_too_old(mocker, default_conf, fee, caplog): + """ + Test Database migration (starting with new pairformat) + """ + caplog.set_level(logging.DEBUG) + amount = 103.223 + create_table_old = """CREATE TABLE IF NOT EXISTS "trades" ( + id INTEGER NOT NULL, + exchange VARCHAR NOT NULL, + pair VARCHAR NOT NULL, + is_open BOOLEAN NOT NULL, + fee_open FLOAT NOT NULL, + fee_close FLOAT NOT NULL, + open_rate FLOAT, + close_rate FLOAT, + close_profit FLOAT, + stake_amount FLOAT NOT NULL, + amount FLOAT, + open_date DATETIME NOT NULL, + close_date DATETIME, + open_order_id VARCHAR, + PRIMARY KEY (id), + CHECK (is_open IN (0, 1)) + );""" + + insert_table_old = """INSERT INTO trades (exchange, pair, is_open, fee_open, fee_close, + open_rate, stake_amount, amount, open_date) + VALUES ('binance', 'ETC/BTC', 1, {fee}, {fee}, + 0.00258580, {stake}, {amount}, + '2019-11-28 12:44:24.000000') + """.format(fee=fee.return_value, + stake=default_conf.get("stake_amount"), + amount=amount + ) + engine = create_engine('sqlite://') + mocker.patch('freqtrade.persistence.models.create_engine', lambda *args, **kwargs: engine) + + # Create table using the old format + with engine.begin() as connection: + connection.execute(text(create_table_old)) + connection.execute(text(insert_table_old)) + + # Run init to test migration + with pytest.raises(OperationalException, match=r'Your database seems to be very old'): + init_db(default_conf['db_url']) + + +def test_migrate_get_last_sequence_ids(): + engine = MagicMock() + engine.begin = MagicMock() + engine.name = 'postgresql' + get_last_sequence_ids(engine, 'trades_bak', 'orders_bak') + + assert engine.begin.call_count == 2 + engine.reset_mock() + engine.begin.reset_mock() + + engine.name = 'somethingelse' + get_last_sequence_ids(engine, 'trades_bak', 'orders_bak') + + assert engine.begin.call_count == 0 + + +def test_migrate_set_sequence_ids(): + engine = MagicMock() + engine.begin = MagicMock() + engine.name = 'postgresql' + set_sequence_ids(engine, 22, 55, 5) + + assert engine.begin.call_count == 1 + engine.reset_mock() + engine.begin.reset_mock() + + engine.name = 'somethingelse' + set_sequence_ids(engine, 22, 55, 6) + + assert engine.begin.call_count == 0 + + +def test_migrate_pairlocks(mocker, default_conf, fee, caplog): + """ + Test Database migration (starting with new pairformat) + """ + caplog.set_level(logging.DEBUG) + # Always create all columns apart from the last! + create_table_old = """CREATE TABLE pairlocks ( + id INTEGER NOT NULL, + pair VARCHAR(25) NOT NULL, + reason VARCHAR(255), + lock_time DATETIME NOT NULL, + lock_end_time DATETIME NOT NULL, + active BOOLEAN NOT NULL, + PRIMARY KEY (id) + ) + """ + create_index1 = "CREATE INDEX ix_pairlocks_pair ON pairlocks (pair)" + create_index2 = "CREATE INDEX ix_pairlocks_lock_end_time ON pairlocks (lock_end_time)" + create_index3 = "CREATE INDEX ix_pairlocks_active ON pairlocks (active)" + insert_table_old = """INSERT INTO pairlocks ( + id, pair, reason, lock_time, lock_end_time, active) + VALUES (1, 'ETH/BTC', 'Auto lock', '2021-07-12 18:41:03', '2021-07-11 18:45:00', 1) + """ + insert_table_old2 = """INSERT INTO pairlocks ( + id, pair, reason, lock_time, lock_end_time, active) + VALUES (2, '*', 'Lock all', '2021-07-12 18:41:03', '2021-07-12 19:00:00', 1) + """ + engine = create_engine('sqlite://') + mocker.patch('freqtrade.persistence.models.create_engine', lambda *args, **kwargs: engine) + # Create table using the old format + with engine.begin() as connection: + connection.execute(text(create_table_old)) + + connection.execute(text(insert_table_old)) + connection.execute(text(insert_table_old2)) + connection.execute(text(create_index1)) + connection.execute(text(create_index2)) + connection.execute(text(create_index3)) + + init_db(default_conf['db_url']) + + assert len(PairLock.query.all()) == 2 + assert len(PairLock.query.filter(PairLock.pair == '*').all()) == 1 + pairlocks = PairLock.query.filter(PairLock.pair == 'ETH/BTC').all() + assert len(pairlocks) == 1 + pairlocks[0].pair == 'ETH/BTC' + pairlocks[0].side == '*' diff --git a/tests/persistence/test_persistence.py b/tests/persistence/test_persistence.py index fbb639d50..984f85c0d 100644 --- a/tests/persistence/test_persistence.py +++ b/tests/persistence/test_persistence.py @@ -1,78 +1,20 @@ # pragma pylint: disable=missing-docstring, C0103 -import logging from datetime import datetime, timedelta, timezone -from pathlib import Path from types import FunctionType -from unittest.mock import MagicMock import arrow import pytest -from sqlalchemy import create_engine, text -from freqtrade.constants import DATETIME_PRINT_FORMAT, DEFAULT_DB_PROD_URL +from freqtrade.constants import DATETIME_PRINT_FORMAT from freqtrade.enums import TradingMode -from freqtrade.exceptions import DependencyException, OperationalException +from freqtrade.exceptions import DependencyException from freqtrade.persistence import LocalTrade, Order, Trade, init_db -from freqtrade.persistence.migrations import get_last_sequence_ids, set_sequence_ids -from freqtrade.persistence.models import PairLock from tests.conftest import create_mock_trades, create_mock_trades_with_leverage, log_has, log_has_re spot, margin, futures = TradingMode.SPOT, TradingMode.MARGIN, TradingMode.FUTURES -def test_init_create_session(default_conf): - # Check if init create a session - init_db(default_conf['db_url']) - assert hasattr(Trade, '_session') - assert 'scoped_session' in type(Trade._session).__name__ - - -def test_init_custom_db_url(default_conf, tmpdir): - # Update path to a value other than default, but still in-memory - filename = f"{tmpdir}/freqtrade2_test.sqlite" - assert not Path(filename).is_file() - - default_conf.update({'db_url': f'sqlite:///{filename}'}) - - init_db(default_conf['db_url']) - assert Path(filename).is_file() - r = Trade._session.execute(text("PRAGMA journal_mode")) - assert r.first() == ('wal',) - - -def test_init_invalid_db_url(): - # Update path to a value other than default, but still in-memory - with pytest.raises(OperationalException, match=r'.*no valid database URL*'): - init_db('unknown:///some.url') - - with pytest.raises(OperationalException, match=r'Bad db-url.*For in-memory database, pl.*'): - init_db('sqlite:///') - - -def test_init_prod_db(default_conf, mocker): - default_conf.update({'dry_run': False}) - default_conf.update({'db_url': DEFAULT_DB_PROD_URL}) - - create_engine_mock = mocker.patch('freqtrade.persistence.models.create_engine', MagicMock()) - - init_db(default_conf['db_url']) - assert create_engine_mock.call_count == 1 - assert create_engine_mock.mock_calls[0][1][0] == 'sqlite:///tradesv3.sqlite' - - -def test_init_dryrun_db(default_conf, tmpdir): - filename = f"{tmpdir}/freqtrade2_prod.sqlite" - assert not Path(filename).is_file() - default_conf.update({ - 'dry_run': True, - 'db_url': f'sqlite:///{filename}' - }) - - init_db(default_conf['db_url']) - assert Path(filename).is_file() - - @pytest.mark.parametrize('is_short', [False, True]) @pytest.mark.usefixtures("init_persistence") def test_enter_exit_side(fee, is_short): @@ -1204,347 +1146,6 @@ def test_calc_profit( trade.open_rate)) == round(profit_ratio, 8) -def test_migrate_new(mocker, default_conf, fee, caplog): - """ - Test Database migration (starting with new pairformat) - """ - caplog.set_level(logging.DEBUG) - amount = 103.223 - # Always create all columns apart from the last! - create_table_old = """CREATE TABLE IF NOT EXISTS "trades" ( - id INTEGER NOT NULL, - exchange VARCHAR NOT NULL, - pair VARCHAR NOT NULL, - is_open BOOLEAN NOT NULL, - fee FLOAT NOT NULL, - open_rate FLOAT, - close_rate FLOAT, - close_profit FLOAT, - stake_amount FLOAT NOT NULL, - amount FLOAT, - open_date DATETIME NOT NULL, - close_date DATETIME, - open_order_id VARCHAR, - stop_loss FLOAT, - initial_stop_loss FLOAT, - max_rate FLOAT, - sell_reason VARCHAR, - strategy VARCHAR, - ticker_interval INTEGER, - stoploss_order_id VARCHAR, - PRIMARY KEY (id), - CHECK (is_open IN (0, 1)) - );""" - create_table_order = """CREATE TABLE orders ( - id INTEGER NOT NULL, - ft_trade_id INTEGER, - ft_order_side VARCHAR(25) NOT NULL, - ft_pair VARCHAR(25) NOT NULL, - ft_is_open BOOLEAN NOT NULL, - order_id VARCHAR(255) NOT NULL, - status VARCHAR(255), - symbol VARCHAR(25), - order_type VARCHAR(50), - side VARCHAR(25), - price FLOAT, - amount FLOAT, - filled FLOAT, - remaining FLOAT, - cost FLOAT, - order_date DATETIME, - order_filled_date DATETIME, - order_update_date DATETIME, - PRIMARY KEY (id) - );""" - insert_table_old = """INSERT INTO trades (exchange, pair, is_open, fee, - open_rate, stake_amount, amount, open_date, - stop_loss, initial_stop_loss, max_rate, ticker_interval, - open_order_id, stoploss_order_id) - VALUES ('binance', 'ETC/BTC', 1, {fee}, - 0.00258580, {stake}, {amount}, - '2019-11-28 12:44:24.000000', - 0.0, 0.0, 0.0, '5m', - 'buy_order', 'dry_stop_order_id222') - """.format(fee=fee.return_value, - stake=default_conf.get("stake_amount"), - amount=amount - ) - insert_orders = f""" - insert into orders ( - ft_trade_id, - ft_order_side, - ft_pair, - ft_is_open, - order_id, - status, - symbol, - order_type, - side, - price, - amount, - filled, - remaining, - cost) - values ( - 1, - 'buy', - 'ETC/BTC', - 0, - 'dry_buy_order', - 'closed', - 'ETC/BTC', - 'limit', - 'buy', - 0.00258580, - {amount}, - {amount}, - 0, - {amount * 0.00258580} - ), - ( - 1, - 'buy', - 'ETC/BTC', - 1, - 'dry_buy_order22', - 'canceled', - 'ETC/BTC', - 'limit', - 'buy', - 0.00258580, - {amount}, - {amount}, - 0, - {amount * 0.00258580} - ), - ( - 1, - 'stoploss', - 'ETC/BTC', - 1, - 'dry_stop_order_id11X', - 'canceled', - 'ETC/BTC', - 'limit', - 'sell', - 0.00258580, - {amount}, - {amount}, - 0, - {amount * 0.00258580} - ), - ( - 1, - 'stoploss', - 'ETC/BTC', - 1, - 'dry_stop_order_id222', - 'open', - 'ETC/BTC', - 'limit', - 'sell', - 0.00258580, - {amount}, - {amount}, - 0, - {amount * 0.00258580} - ) - """ - engine = create_engine('sqlite://') - mocker.patch('freqtrade.persistence.models.create_engine', lambda *args, **kwargs: engine) - - # Create table using the old format - with engine.begin() as connection: - connection.execute(text(create_table_old)) - connection.execute(text(create_table_order)) - connection.execute(text("create index ix_trades_is_open on trades(is_open)")) - connection.execute(text("create index ix_trades_pair on trades(pair)")) - connection.execute(text(insert_table_old)) - connection.execute(text(insert_orders)) - - # fake previous backup - connection.execute(text("create table trades_bak as select * from trades")) - - connection.execute(text("create table trades_bak1 as select * from trades")) - # Run init to test migration - init_db(default_conf['db_url']) - - assert len(Trade.query.filter(Trade.id == 1).all()) == 1 - trade = Trade.query.filter(Trade.id == 1).first() - assert trade.fee_open == fee.return_value - assert trade.fee_close == fee.return_value - assert trade.open_rate_requested is None - assert trade.close_rate_requested is None - assert trade.is_open == 1 - assert trade.amount == amount - assert trade.amount_requested == amount - assert trade.stake_amount == default_conf.get("stake_amount") - assert trade.pair == "ETC/BTC" - assert trade.exchange == "binance" - assert trade.max_rate == 0.0 - assert trade.min_rate is None - assert trade.stop_loss == 0.0 - assert trade.initial_stop_loss == 0.0 - assert trade.exit_reason is None - assert trade.strategy is None - assert trade.timeframe == '5m' - assert trade.stoploss_order_id == 'dry_stop_order_id222' - assert trade.stoploss_last_update is None - assert log_has("trying trades_bak1", caplog) - assert log_has("trying trades_bak2", caplog) - assert log_has("Running database migration for trades - backup: trades_bak2, orders_bak0", - caplog) - assert log_has("Database migration finished.", caplog) - assert pytest.approx(trade.open_trade_value) == trade._calc_open_trade_value( - trade.amount, trade.open_rate) - assert trade.close_profit_abs is None - - orders = trade.orders - assert len(orders) == 4 - assert orders[0].order_id == 'dry_buy_order' - assert orders[0].ft_order_side == 'buy' - - assert orders[-1].order_id == 'dry_stop_order_id222' - assert orders[-1].ft_order_side == 'stoploss' - assert orders[-1].ft_is_open is True - - assert orders[1].order_id == 'dry_buy_order22' - assert orders[1].ft_order_side == 'buy' - assert orders[1].ft_is_open is False - - assert orders[2].order_id == 'dry_stop_order_id11X' - assert orders[2].ft_order_side == 'stoploss' - assert orders[2].ft_is_open is False - - -def test_migrate_too_old(mocker, default_conf, fee, caplog): - """ - Test Database migration (starting with new pairformat) - """ - caplog.set_level(logging.DEBUG) - amount = 103.223 - create_table_old = """CREATE TABLE IF NOT EXISTS "trades" ( - id INTEGER NOT NULL, - exchange VARCHAR NOT NULL, - pair VARCHAR NOT NULL, - is_open BOOLEAN NOT NULL, - fee_open FLOAT NOT NULL, - fee_close FLOAT NOT NULL, - open_rate FLOAT, - close_rate FLOAT, - close_profit FLOAT, - stake_amount FLOAT NOT NULL, - amount FLOAT, - open_date DATETIME NOT NULL, - close_date DATETIME, - open_order_id VARCHAR, - PRIMARY KEY (id), - CHECK (is_open IN (0, 1)) - );""" - - insert_table_old = """INSERT INTO trades (exchange, pair, is_open, fee_open, fee_close, - open_rate, stake_amount, amount, open_date) - VALUES ('binance', 'ETC/BTC', 1, {fee}, {fee}, - 0.00258580, {stake}, {amount}, - '2019-11-28 12:44:24.000000') - """.format(fee=fee.return_value, - stake=default_conf.get("stake_amount"), - amount=amount - ) - engine = create_engine('sqlite://') - mocker.patch('freqtrade.persistence.models.create_engine', lambda *args, **kwargs: engine) - - # Create table using the old format - with engine.begin() as connection: - connection.execute(text(create_table_old)) - connection.execute(text(insert_table_old)) - - # Run init to test migration - with pytest.raises(OperationalException, match=r'Your database seems to be very old'): - init_db(default_conf['db_url']) - - -def test_migrate_get_last_sequence_ids(): - engine = MagicMock() - engine.begin = MagicMock() - engine.name = 'postgresql' - get_last_sequence_ids(engine, 'trades_bak', 'orders_bak') - - assert engine.begin.call_count == 2 - engine.reset_mock() - engine.begin.reset_mock() - - engine.name = 'somethingelse' - get_last_sequence_ids(engine, 'trades_bak', 'orders_bak') - - assert engine.begin.call_count == 0 - - -def test_migrate_set_sequence_ids(): - engine = MagicMock() - engine.begin = MagicMock() - engine.name = 'postgresql' - set_sequence_ids(engine, 22, 55, 5) - - assert engine.begin.call_count == 1 - engine.reset_mock() - engine.begin.reset_mock() - - engine.name = 'somethingelse' - set_sequence_ids(engine, 22, 55, 6) - - assert engine.begin.call_count == 0 - - -def test_migrate_pairlocks(mocker, default_conf, fee, caplog): - """ - Test Database migration (starting with new pairformat) - """ - caplog.set_level(logging.DEBUG) - # Always create all columns apart from the last! - create_table_old = """CREATE TABLE pairlocks ( - id INTEGER NOT NULL, - pair VARCHAR(25) NOT NULL, - reason VARCHAR(255), - lock_time DATETIME NOT NULL, - lock_end_time DATETIME NOT NULL, - active BOOLEAN NOT NULL, - PRIMARY KEY (id) - ) - """ - create_index1 = "CREATE INDEX ix_pairlocks_pair ON pairlocks (pair)" - create_index2 = "CREATE INDEX ix_pairlocks_lock_end_time ON pairlocks (lock_end_time)" - create_index3 = "CREATE INDEX ix_pairlocks_active ON pairlocks (active)" - insert_table_old = """INSERT INTO pairlocks ( - id, pair, reason, lock_time, lock_end_time, active) - VALUES (1, 'ETH/BTC', 'Auto lock', '2021-07-12 18:41:03', '2021-07-11 18:45:00', 1) - """ - insert_table_old2 = """INSERT INTO pairlocks ( - id, pair, reason, lock_time, lock_end_time, active) - VALUES (2, '*', 'Lock all', '2021-07-12 18:41:03', '2021-07-12 19:00:00', 1) - """ - engine = create_engine('sqlite://') - mocker.patch('freqtrade.persistence.models.create_engine', lambda *args, **kwargs: engine) - # Create table using the old format - with engine.begin() as connection: - connection.execute(text(create_table_old)) - - connection.execute(text(insert_table_old)) - connection.execute(text(insert_table_old2)) - connection.execute(text(create_index1)) - connection.execute(text(create_index2)) - connection.execute(text(create_index3)) - - init_db(default_conf['db_url']) - - assert len(PairLock.query.all()) == 2 - assert len(PairLock.query.filter(PairLock.pair == '*').all()) == 1 - pairlocks = PairLock.query.filter(PairLock.pair == 'ETH/BTC').all() - assert len(pairlocks) == 1 - pairlocks[0].pair == 'ETH/BTC' - pairlocks[0].side == '*' - - def test_adjust_stop_loss(fee): trade = Trade( pair='ADA/USDT', From 55001bf321db562fd6592dcd5e8612835033cc1d Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 27 Dec 2022 13:42:56 +0100 Subject: [PATCH 150/795] Keep max_stake_amount (only relevant for DCA orders). --- freqtrade/persistence/migrations.py | 15 ++++++++------- freqtrade/persistence/trade_model.py | 3 +++ tests/persistence/test_migrations.py | 1 + 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py index edbcd6be3..44a6756d1 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -109,11 +109,10 @@ def migrate_trades_and_orders_table( else: is_short = get_column_def(cols, 'is_short', '0') - # Margin Properties + # Futures Properties interest_rate = get_column_def(cols, 'interest_rate', '0.0') - - # Futures properties funding_fees = get_column_def(cols, 'funding_fees', '0.0') + max_stake_amount = get_column_def(cols, 'max_stake_amount', 'stake_amount') # If ticker-interval existed use that, else null. if has_column(cols, 'ticker_interval'): @@ -162,7 +161,8 @@ def migrate_trades_and_orders_table( timeframe, open_trade_value, close_profit_abs, trading_mode, leverage, liquidation_price, is_short, interest_rate, funding_fees, realized_profit, - amount_precision, price_precision, precision_mode, contract_size + amount_precision, price_precision, precision_mode, contract_size, + max_stake_amount ) select id, lower(exchange), pair, {base_currency} base_currency, {stake_currency} stake_currency, @@ -190,7 +190,8 @@ def migrate_trades_and_orders_table( {is_short} is_short, {interest_rate} interest_rate, {funding_fees} funding_fees, {realized_profit} realized_profit, {amount_precision} amount_precision, {price_precision} price_precision, - {precision_mode} precision_mode, {contract_size} contract_size + {precision_mode} precision_mode, {contract_size} contract_size, + {max_stake_amount} max_stake_amount from {trade_back_name} """)) @@ -310,8 +311,8 @@ def check_migrate(engine, decl_base, previous_tables) -> None: # if ('orders' not in previous_tables # or not has_column(cols_orders, 'funding_fee')): migrating = False - # if not has_column(cols_trades, 'contract_size'): - if not has_column(cols_orders, 'funding_fee'): + # if not has_column(cols_orders, 'funding_fee'): + if not has_column(cols_trades, 'max_stake_amount'): migrating = True logger.info(f"Running database migration for trades - " f"backup: {table_back_name}, {order_table_bak_name}") diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index 186a1e584..ad3f9e3b9 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -293,6 +293,7 @@ class LocalTrade(): close_profit: Optional[float] = None close_profit_abs: Optional[float] = None stake_amount: float = 0.0 + max_stake_amount: float = 0.0 amount: float = 0.0 amount_requested: Optional[float] = None open_date: datetime @@ -918,6 +919,7 @@ class LocalTrade(): else: total_stake = total_stake + self._calc_open_trade_value(tmp_amount, price) self.funding_fees = funding_fees + self.max_stake_amount = total_stake if close_profit: self.close_profit = close_profit @@ -1169,6 +1171,7 @@ class Trade(_DECL_BASE, LocalTrade): close_profit = Column(Float) close_profit_abs = Column(Float) stake_amount = Column(Float, nullable=False) + max_stake_amount = Column(Float) amount = Column(Float) amount_requested = Column(Float) open_date = Column(DateTime, nullable=False, default=datetime.utcnow) diff --git a/tests/persistence/test_migrations.py b/tests/persistence/test_migrations.py index 1cd236005..2a6959d58 100644 --- a/tests/persistence/test_migrations.py +++ b/tests/persistence/test_migrations.py @@ -264,6 +264,7 @@ def test_migrate_new(mocker, default_conf, fee, caplog): assert pytest.approx(trade.open_trade_value) == trade._calc_open_trade_value( trade.amount, trade.open_rate) assert trade.close_profit_abs is None + assert trade.stake_amount == trade.max_stake_amount orders = trade.orders assert len(orders) == 4 From cb66663fd2505c1410280636d57c3b9b504ef2f8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 27 Dec 2022 13:46:27 +0100 Subject: [PATCH 151/795] show max_stake_amount in API --- freqtrade/persistence/trade_model.py | 1 + freqtrade/rpc/api_server/api_schemas.py | 1 + tests/persistence/test_persistence.py | 2 ++ tests/rpc/test_rpc.py | 1 + tests/rpc/test_rpc_apiserver.py | 2 ++ 5 files changed, 7 insertions(+) diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index ad3f9e3b9..8d2c3f10e 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -470,6 +470,7 @@ class LocalTrade(): 'amount': round(self.amount, 8), 'amount_requested': round(self.amount_requested, 8) if self.amount_requested else None, 'stake_amount': round(self.stake_amount, 8), + 'max_stake_amount': round(self.max_stake_amount, 8) if self.max_stake_amount else None, 'strategy': self.strategy, 'buy_tag': self.enter_tag, 'enter_tag': self.enter_tag, diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 17dff222d..59018aa50 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -217,6 +217,7 @@ class TradeSchema(BaseModel): amount: float amount_requested: float stake_amount: float + max_stake_amount: Optional[float] strategy: str buy_tag: Optional[str] # Deprecated enter_tag: Optional[str] diff --git a/tests/persistence/test_persistence.py b/tests/persistence/test_persistence.py index 984f85c0d..499fefce5 100644 --- a/tests/persistence/test_persistence.py +++ b/tests/persistence/test_persistence.py @@ -1359,6 +1359,7 @@ def test_to_json(fee): 'amount': 123.0, 'amount_requested': 123.0, 'stake_amount': 0.001, + 'max_stake_amount': None, 'trade_duration': None, 'trade_duration_s': None, 'realized_profit': 0.0, @@ -1427,6 +1428,7 @@ def test_to_json(fee): 'amount': 100.0, 'amount_requested': 101.0, 'stake_amount': 0.001, + 'max_stake_amount': None, 'trade_duration': 60, 'trade_duration_s': 3600, 'stop_loss_abs': None, diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 24b5f1cbe..fd04e5c85 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -64,6 +64,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'amount': 91.07468123, 'amount_requested': 91.07468124, 'stake_amount': 0.001, + 'max_stake_amount': ANY, 'trade_duration': None, 'trade_duration_s': None, 'close_profit': None, diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 2a2a38196..16e2a6737 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -985,6 +985,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets, is_short, 'base_currency': 'ETH', 'quote_currency': 'BTC', 'stake_amount': 0.001, + 'max_stake_amount': ANY, 'stop_loss_abs': ANY, 'stop_loss_pct': ANY, 'stop_loss_ratio': ANY, @@ -1188,6 +1189,7 @@ def test_api_force_entry(botclient, mocker, fee, endpoint): 'base_currency': 'ETH', 'quote_currency': 'BTC', 'stake_amount': 1, + 'max_stake_amount': ANY, 'stop_loss_abs': None, 'stop_loss_pct': None, 'stop_loss_ratio': None, From 62c4675e295e178320550c50d9fbf5126ca8b23f Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 27 Dec 2022 13:55:46 +0100 Subject: [PATCH 152/795] Remove some deprecated fields from the API --- freqtrade/persistence/trade_model.py | 2 -- freqtrade/rpc/api_server/api_schemas.py | 2 -- tests/persistence/test_persistence.py | 7 +------ tests/rpc/test_rpc.py | 2 -- tests/rpc/test_rpc_apiserver.py | 4 ---- 5 files changed, 1 insertion(+), 16 deletions(-) diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index 8d2c3f10e..e954fd263 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -472,7 +472,6 @@ class LocalTrade(): 'stake_amount': round(self.stake_amount, 8), 'max_stake_amount': round(self.max_stake_amount, 8) if self.max_stake_amount else None, 'strategy': self.strategy, - 'buy_tag': self.enter_tag, 'enter_tag': self.enter_tag, 'timeframe': self.timeframe, @@ -509,7 +508,6 @@ class LocalTrade(): 'profit_pct': round(self.close_profit * 100, 2) if self.close_profit else None, 'profit_abs': self.close_profit_abs, - 'sell_reason': self.exit_reason, # Deprecated 'exit_reason': self.exit_reason, 'exit_order_status': self.exit_order_status, 'stop_loss_abs': self.stop_loss, diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 59018aa50..404d64d16 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -219,7 +219,6 @@ class TradeSchema(BaseModel): stake_amount: float max_stake_amount: Optional[float] strategy: str - buy_tag: Optional[str] # Deprecated enter_tag: Optional[str] timeframe: int fee_open: Optional[float] @@ -244,7 +243,6 @@ class TradeSchema(BaseModel): profit_pct: Optional[float] profit_abs: Optional[float] profit_fiat: Optional[float] - sell_reason: Optional[str] # Deprecated exit_reason: Optional[str] exit_order_status: Optional[str] stop_loss_abs: Optional[float] diff --git a/tests/persistence/test_persistence.py b/tests/persistence/test_persistence.py index 499fefce5..830d84288 100644 --- a/tests/persistence/test_persistence.py +++ b/tests/persistence/test_persistence.py @@ -258,8 +258,7 @@ def test_interest(fee, exchange, is_short, lev, minutes, rate, interest, (True, 3.0, 30.0, margin), ]) @pytest.mark.usefixtures("init_persistence") -def test_borrowed(limit_buy_order_usdt, limit_sell_order_usdt, fee, - caplog, is_short, lev, borrowed, trading_mode): +def test_borrowed(fee, is_short, lev, borrowed, trading_mode): """ 10 minute limit trade on Binance/Kraken at 1x, 3x leverage fee: 0.25% quote @@ -1369,7 +1368,6 @@ def test_to_json(fee): 'profit_ratio': None, 'profit_pct': None, 'profit_abs': None, - 'sell_reason': None, 'exit_reason': None, 'exit_order_status': None, 'stop_loss_abs': None, @@ -1384,7 +1382,6 @@ def test_to_json(fee): 'min_rate': None, 'max_rate': None, 'strategy': None, - 'buy_tag': None, 'enter_tag': None, 'timeframe': None, 'exchange': 'binance', @@ -1460,11 +1457,9 @@ def test_to_json(fee): 'open_order_id': None, 'open_rate_requested': None, 'open_trade_value': 12.33075, - 'sell_reason': None, 'exit_reason': None, 'exit_order_status': None, 'strategy': None, - 'buy_tag': 'buys_signal_001', 'enter_tag': 'buys_signal_001', 'timeframe': None, 'exchange': 'binance', diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index fd04e5c85..4871d9b24 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -46,13 +46,11 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'open_rate_requested': ANY, 'open_trade_value': 0.0010025, 'close_rate_requested': ANY, - 'sell_reason': ANY, 'exit_reason': ANY, 'exit_order_status': ANY, 'min_rate': ANY, 'max_rate': ANY, 'strategy': ANY, - 'buy_tag': ANY, 'enter_tag': ANY, 'timeframe': 5, 'open_order_id': ANY, diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 16e2a6737..c130e9373 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1015,11 +1015,9 @@ def test_api_status(botclient, mocker, ticker, fee, markets, is_short, 'open_order_id': open_order_id, 'open_rate_requested': ANY, 'open_trade_value': open_trade_value, - 'sell_reason': None, 'exit_reason': None, 'exit_order_status': None, 'strategy': CURRENT_TEST_STRATEGY, - 'buy_tag': None, 'enter_tag': None, 'timeframe': 5, 'exchange': 'binance', @@ -1220,11 +1218,9 @@ def test_api_force_entry(botclient, mocker, fee, endpoint): 'open_order_id': '123456', 'open_rate_requested': None, 'open_trade_value': 0.24605460, - 'sell_reason': None, 'exit_reason': None, 'exit_order_status': None, 'strategy': CURRENT_TEST_STRATEGY, - 'buy_tag': None, 'enter_tag': None, 'timeframe': 5, 'exchange': 'binance', From 8227b4aafe51b30e5942d293e8d0052c968442dd Mon Sep 17 00:00:00 2001 From: Wagner Costa Date: Tue, 27 Dec 2022 11:37:01 -0300 Subject: [PATCH 153/795] freqAI Strategy - improve user experience --- freqtrade/freqai/data_kitchen.py | 183 ++++++++++++++++++- freqtrade/strategy/interface.py | 40 ++++ freqtrade/templates/FreqaiExampleStrategy.py | 90 ++++++++- 3 files changed, 306 insertions(+), 7 deletions(-) diff --git a/freqtrade/freqai/data_kitchen.py b/freqtrade/freqai/data_kitchen.py index 9c8158c8a..c3e5929de 100644 --- a/freqtrade/freqai/data_kitchen.py +++ b/freqtrade/freqai/data_kitchen.py @@ -1,4 +1,5 @@ import copy +import inspect import logging import shutil from datetime import datetime, timezone @@ -23,6 +24,7 @@ from freqtrade.constants import Config from freqtrade.data.converter import reduce_dataframe_footprint from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_seconds +from freqtrade.strategy import merge_informative_pair from freqtrade.strategy.interface import IStrategy @@ -1176,6 +1178,103 @@ class FreqaiDataKitchen: return dataframe + def get_pair_data_for_features(self, + pair: str, + tf: str, + strategy: IStrategy, + corr_dataframes: dict = {}, + base_dataframes: dict = {}, + is_corr_pairs: bool = False) -> DataFrame: + """ + Get the data for the pair. If it's not in the dictionary, get it from the data provider + :param pair: str = pair to get data for + :param tf: str = timeframe to get data for + :param strategy: IStrategy = user defined strategy object + :param corr_dataframes: dict = dict containing the df pair dataframes + (for user defined timeframes) + :param base_dataframes: dict = dict containing the current pair dataframes + (for user defined timeframes) + :param is_corr_pairs: bool = whether the pair is a corr pair or not + :return: dataframe = dataframe containing the pair data + """ + if is_corr_pairs: + dataframe = corr_dataframes[pair][tf] + if not dataframe.empty: + return dataframe + else: + dataframe = strategy.dp.get_pair_dataframe(pair=pair, timeframe=tf) + return dataframe + else: + dataframe = base_dataframes[tf] + if not dataframe.empty: + return dataframe + else: + dataframe = strategy.dp.get_pair_dataframe(pair=pair, timeframe=tf) + return dataframe + + def merge_features(self, df_main: DataFrame, df_to_merge: DataFrame, + tf: str, timeframe_inf: str, suffix: str) -> DataFrame: + """ + Merge the features of the dataframe and remove HLCV and date added columns + :param df_main: DataFrame = main dataframe + :param df_to_merge: DataFrame = dataframe to merge + :param tf: str = timeframe of the main dataframe + :param timeframe_inf: str = timeframe of the dataframe to merge + :param suffix: str = suffix to add to the columns of the dataframe to merge + :return: dataframe = merged dataframe + """ + dataframe = merge_informative_pair(df_main, df_to_merge, tf, timeframe_inf=timeframe_inf, + append_timeframe=False, suffix=suffix, ffill=True) + skip_columns = [ + (f"{s}_{suffix}") for s in ["date", "open", "high", "low", "close", "volume"] + ] + dataframe = dataframe.drop(columns=skip_columns) + return dataframe + + def populate_features(self, dataframe: DataFrame, pair: str, strategy: IStrategy, + corr_dataframes: dict, base_dataframes: dict, + is_corr_pairs: bool = False) -> DataFrame: + """ + Use the user defined strategy functions for populating features + :param dataframe: DataFrame = dataframe to populate + :param pair: str = pair to populate + :param strategy: IStrategy = user defined strategy object + :param corr_dataframes: dict = dict containing the df pair dataframes + :param base_dataframes: dict = dict containing the current pair dataframes + :param is_corr_pairs: bool = whether the pair is a corr pair or not + :return: dataframe = populated dataframe + """ + tfs: List[str] = self.freqai_config["feature_parameters"].get("include_timeframes") + + for tf in tfs: + informative_df = self.get_pair_data_for_features( + pair, tf, strategy, corr_dataframes, base_dataframes, is_corr_pairs) + informative_copy = informative_df.copy() + + for t in self.freqai_config["feature_parameters"]["indicator_periods_candles"]: + df_features = strategy.freqai_feature_engineering_indicator_periods( + informative_copy.copy(), t) + suffix = f"{t}" + informative_df = self.merge_features(informative_df, df_features, tf, tf, suffix) + + generic_df = strategy.freqai_feature_engineering_generic(informative_copy.copy()) + suffix = "gen" + + informative_df = self.merge_features(informative_df, generic_df, tf, tf, suffix) + + indicators = [col for col in informative_df if col.startswith("%")] + for n in range(self.freqai_config["feature_parameters"]["include_shifted_candles"] + 1): + if n == 0: + continue + df_shift = informative_df[indicators].shift(n) + df_shift = df_shift.add_suffix("_shift-" + str(n)) + informative_df = pd.concat((informative_df, df_shift), axis=1) + + dataframe = self.merge_features(dataframe.copy(), informative_df, + self.config["timeframe"], tf, f'{pair}_{tf}') + + return dataframe + def use_strategy_to_populate_indicators( self, strategy: IStrategy, @@ -1188,7 +1287,88 @@ class FreqaiDataKitchen: """ Use the user defined strategy for populating indicators during retrain :param strategy: IStrategy = user defined strategy object - :param corr_dataframes: dict = dict containing the informative pair dataframes + :param corr_dataframes: dict = dict containing the df pair dataframes + (for user defined timeframes) + :param base_dataframes: dict = dict containing the current pair dataframes + (for user defined timeframes) + :param pair: str = pair to populate + :param prediction_dataframe: DataFrame = dataframe containing the pair data + used for prediction + :param do_corr_pairs: bool = whether to populate corr pairs or not + :return: + dataframe: DataFrame = dataframe containing populated indicators + """ + + # this is a hack to check if the user is using the populate_any_indicators function + new_version = inspect.getsource(strategy.populate_any_indicators) == ( + inspect.getsource(IStrategy.populate_any_indicators)) + + if new_version: + tfs: List[str] = self.freqai_config["feature_parameters"].get("include_timeframes") + pairs: List[str] = self.freqai_config["feature_parameters"].get( + "include_corr_pairlist", []) + + if not prediction_dataframe.empty: + dataframe = prediction_dataframe.copy() + for tf in tfs: + base_dataframes[tf] = pd.DataFrame() + for p in pairs: + if p not in corr_dataframes: + corr_dataframes[p] = {} + corr_dataframes[p][tf] = pd.DataFrame() + else: + dataframe = base_dataframes[self.config["timeframe"]].copy() + + corr_pairs: List[str] = self.freqai_config["feature_parameters"].get( + "include_corr_pairlist", []) + dataframe = self.populate_features(dataframe.copy(), pair, strategy, + corr_dataframes, base_dataframes) + + # ensure corr pairs are always last + for corr_pair in corr_pairs: + if pair == corr_pair: + continue # dont repeat anything from whitelist + if corr_pairs and do_corr_pairs: + dataframe = self.populate_features(dataframe.copy(), corr_pair, strategy, + corr_dataframes, base_dataframes, True) + + dataframe = strategy.freqai_feature_engineering_generalized_indicators(dataframe.copy()) + dataframe = strategy.freqai_set_targets(dataframe.copy()) + + self.get_unique_classes_from_labels(dataframe) + + dataframe = self.remove_special_chars_from_feature_names(dataframe) + + if self.config.get('reduce_df_footprint', False): + dataframe = reduce_dataframe_footprint(dataframe) + + return dataframe + + else: + # the user is using the populate_any_indicators functions which is deprecated + logger.warning("DEPRECATION WARNING: " + "You are using the deprecated populate_any_indicators function. " + "Please update your strategy to use " + "the new feature_engineering functions.") + + df = self.use_strategy_to_populate_indicators_old_version( + strategy, corr_dataframes, base_dataframes, pair, + prediction_dataframe, do_corr_pairs) + return df + + def use_strategy_to_populate_indicators_old_version( + self, + strategy: IStrategy, + corr_dataframes: dict = {}, + base_dataframes: dict = {}, + pair: str = "", + prediction_dataframe: DataFrame = pd.DataFrame(), + do_corr_pairs: bool = True, + ) -> DataFrame: + """ + Use the user defined strategy for populating indicators during retrain + :param strategy: IStrategy = user defined strategy object + :param corr_dataframes: dict = dict containing the df pair dataframes (for user defined timeframes) :param base_dataframes: dict = dict containing the current pair dataframes (for user defined timeframes) @@ -1212,6 +1392,7 @@ class FreqaiDataKitchen: corr_dataframes[p][tf] = None else: dataframe = base_dataframes[self.config["timeframe"]].copy() + # dataframe = strategy.dp.get_pair_dataframe(pair, self.config["timeframe"]) sgi = False for tf in tfs: diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 781ae6c5c..6bcc2a23f 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -598,6 +598,7 @@ class IStrategy(ABC, HyperStrategyMixin): informative: DataFrame = None, set_generalized_indicators: bool = False) -> DataFrame: """ + DEPRECATED - USE FEATURE ENGINEERING FUNCTIONS INSTEAD Function designed to automatically generate, name and merge features from user indicated timeframes in the configuration file. User can add additional features here, but must follow the naming convention. @@ -610,6 +611,45 @@ class IStrategy(ABC, HyperStrategyMixin): """ return df + def freqai_feature_engineering_indicator_periods(self, dataframe: DataFrame, + period: int, **kwargs): + """ + This function will be called for all include_timeframes in each indicator_periods_candles + (including corr_pairs). + After that, the features will be shifted by the number of candles in the + include_shifted_candles. + :param df: strategy dataframe which will receive the features + :param period: period of the indicator - usage example: + dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period) + """ + return dataframe + + def freqai_feature_engineering_generic(self, dataframe: DataFrame, **kwargs): + """ + This optional function will be called for all include_timeframes (including corr_pairs). + After that, the features will be shifted by the number of candles in the + include_shifted_candles. + :param df: strategy dataframe which will receive the features + dataframe["%-pct-change"] = dataframe["close"].pct_change() + """ + return dataframe + + def freqai_feature_engineering_generalized_indicators(self, dataframe: DataFrame, **kwargs): + """ + This optional function will be called once with the dataframe of the main timeframe. + :param df: strategy dataframe which will receive the features + usage example: dataframe["%-day_of_week"] = (dataframe["date"].dt.dayofweek + 1) / 7 + """ + return dataframe + + def freqai_set_targets(self, dataframe, **kwargs): + """ + Required function to set the targets for the model. + :param df: strategy dataframe which will receive the targets + usage example: dataframe["&-target"] = dataframe["close"].shift(-1) / dataframe["close"] + """ + return dataframe + ### # END - Intended to be overridden by strategy ### diff --git a/freqtrade/templates/FreqaiExampleStrategy.py b/freqtrade/templates/FreqaiExampleStrategy.py index fc39b0ab4..323919a47 100644 --- a/freqtrade/templates/FreqaiExampleStrategy.py +++ b/freqtrade/templates/FreqaiExampleStrategy.py @@ -47,16 +47,94 @@ class FreqaiExampleStrategy(IStrategy): std_dev_multiplier_sell = CategoricalParameter( [0.75, 1, 1.25, 1.5, 1.75], space="sell", default=1.25, optimize=True) - def populate_any_indicators( + def freqai_feature_engineering_indicator_periods(self, dataframe, period, **kwargs): + """ + This function will be called for all include_timeframes in each indicator_periods_candles + (including corr_pairs). + After that, the features will be shifted by the number of candles in the + include_shifted_candles. + :param df: strategy dataframe which will receive the features + :param period: period of the indicator - usage example: + dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period) + """ + dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period) + dataframe["%-mfi-period"] = ta.MFI(dataframe, timeperiod=period) + dataframe["%-adx-period"] = ta.ADX(dataframe, timeperiod=period) + dataframe["%-sma-period"] = ta.SMA(dataframe, timeperiod=period) + dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period) + + bollinger = qtpylib.bollinger_bands( + qtpylib.typical_price(dataframe), window=period, stds=2.2 + ) + dataframe["bb_lowerband-period"] = bollinger["lower"] + dataframe["bb_middleband-period"] = bollinger["mid"] + dataframe["bb_upperband-period"] = bollinger["upper"] + + dataframe["%-bb_width-period"] = ( + dataframe["bb_upperband-period"] + - dataframe["bb_lowerband-period"] + ) / dataframe["bb_middleband-period"] + dataframe["%-close-bb_lower-period"] = ( + dataframe["close"] / dataframe["bb_lowerband-period"] + ) + + dataframe["%-roc-period"] = ta.ROC(dataframe, timeperiod=period) + + dataframe["%-relative_volume-period"] = ( + dataframe["volume"] / dataframe["volume"].rolling(period).mean() + ) + + return dataframe + + def freqai_feature_engineering_generic(self, dataframe, **kwargs): + """ + This optional function will be called for all include_timeframes (including corr_pairs). + After that, the features will be shifted by the number of candles in the + include_shifted_candles. + :param df: strategy dataframe which will receive the features + dataframe["%-pct-change"] = dataframe["close"].pct_change() + """ + dataframe["%-pct-change"] = dataframe["close"].pct_change() + dataframe["%-raw_volume"] = dataframe["volume"] + dataframe["%-raw_price"] = dataframe["close"] + return dataframe + + def freqai_feature_engineering_generalized_indicators(self, dataframe, **kwargs): + """ + This optional function will be called once with the dataframe of the main timeframe. + :param df: strategy dataframe which will receive the features + usage example: dataframe["%-day_of_week"] = (dataframe["date"].dt.dayofweek + 1) / 7 + """ + dataframe["%-day_of_week"] = (dataframe["date"].dt.dayofweek + 1) / 7 + dataframe["%-hour_of_day"] = (dataframe["date"].dt.hour + 1) / 25 + return dataframe + + def freqai_set_targets(self, dataframe, **kwargs): + """ + Required function to set the targets for the model. + :param df: strategy dataframe which will receive the targets + usage example: dataframe["&-target"] = dataframe["close"].shift(-1) / dataframe["close"] + """ + dataframe["&-s_close"] = ( + dataframe["close"] + .shift(-self.freqai_info["feature_parameters"]["label_period_candles"]) + .rolling(self.freqai_info["feature_parameters"]["label_period_candles"]) + .mean() + / dataframe["close"] + - 1 + ) + return dataframe + + def populate_any_indicators_old( self, pair, df, tf, informative=None, set_generalized_indicators=False ): """ + DEPRECATED - USE FEATURE ENGINEERING FUNCTIONS INSTEAD Function designed to automatically generate, name and merge features - from user indicated timeframes in the configuration file. User controls the indicators - passed to the training/prediction by prepending indicators with `f'%-{pair}` - (see convention below). I.e. user should not prepend any supporting metrics - (e.g. bb_lowerband below) with % unless they explicitly want to pass that metric to the - model. + from user indicated timeframes in the configuration file. User can add + additional features here, but must follow the naming convention. + This method is *only* used in FreqaiDataKitchen class and therefore + it is only called if FreqAI is active. :param pair: pair to be used as informative :param df: strategy dataframe which will receive merges from informatives :param tf: timeframe of the dataframe which will modify the feature names From cd4faa9c59b710c34a6a3f78e1dec161e4a2a3bb Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 27 Dec 2022 18:08:20 +0100 Subject: [PATCH 154/795] keep max_stake_amount through backtests --- freqtrade/data/btanalysis.py | 50 +++++++++++-------- freqtrade/persistence/trade_model.py | 4 +- tests/optimize/test_backtesting.py | 1 + .../test_backtesting_adjust_position.py | 1 + .../backtest_results/backtest-result.json | 2 +- 5 files changed, 36 insertions(+), 22 deletions(-) diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index 6350aca55..3102683b2 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -20,8 +20,8 @@ from freqtrade.persistence import LocalTrade, Trade, init_db logger = logging.getLogger(__name__) # Newest format -BT_DATA_COLUMNS = ['pair', 'stake_amount', 'amount', 'open_date', 'close_date', - 'open_rate', 'close_rate', +BT_DATA_COLUMNS = ['pair', 'stake_amount', 'max_stake_amount', 'amount', + 'open_date', 'close_date', 'open_rate', 'close_rate', 'fee_open', 'fee_close', 'trade_duration', 'profit_ratio', 'profit_abs', 'exit_reason', 'initial_stop_loss_abs', 'initial_stop_loss_ratio', 'stop_loss_abs', @@ -241,6 +241,33 @@ def find_existing_backtest_stats(dirname: Union[Path, str], run_ids: Dict[str, s return results +def _load_backtest_data_df_compatibility(df: pd.DataFrame) -> pd.DataFrame: + """ + Compatibility support for older backtest data. + """ + df['open_date'] = pd.to_datetime(df['open_date'], + utc=True, + infer_datetime_format=True + ) + df['close_date'] = pd.to_datetime(df['close_date'], + utc=True, + infer_datetime_format=True + ) + # Compatibility support for pre short Columns + if 'is_short' not in df.columns: + df['is_short'] = False + if 'leverage' not in df.columns: + df['leverage'] = 1.0 + if 'enter_tag' not in df.columns: + df['enter_tag'] = df['buy_tag'] + df = df.drop(['buy_tag'], axis=1) + if 'max_stake_amount' not in df.columns: + df['max_stake_amount'] = df['stake_amount'] + if 'orders' not in df.columns: + df['orders'] = None + return df + + def load_backtest_data(filename: Union[Path, str], strategy: Optional[str] = None) -> pd.DataFrame: """ Load backtest data file. @@ -269,24 +296,7 @@ def load_backtest_data(filename: Union[Path, str], strategy: Optional[str] = Non data = data['strategy'][strategy]['trades'] df = pd.DataFrame(data) if not df.empty: - df['open_date'] = pd.to_datetime(df['open_date'], - utc=True, - infer_datetime_format=True - ) - df['close_date'] = pd.to_datetime(df['close_date'], - utc=True, - infer_datetime_format=True - ) - # Compatibility support for pre short Columns - if 'is_short' not in df.columns: - df['is_short'] = False - if 'leverage' not in df.columns: - df['leverage'] = 1.0 - if 'enter_tag' not in df.columns: - df['enter_tag'] = df['buy_tag'] - df = df.drop(['buy_tag'], axis=1) - if 'orders' not in df.columns: - df['orders'] = None + df = _load_backtest_data_df_compatibility(df) else: # old format - only with lists. diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index e954fd263..0c36d2378 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -876,6 +876,7 @@ class LocalTrade(): ZERO = FtPrecise(0.0) current_amount = FtPrecise(0.0) current_stake = FtPrecise(0.0) + max_stake_amount = FtPrecise(0.0) total_stake = 0.0 # Total stake after all buy orders (does not subtract!) avg_price = FtPrecise(0.0) close_profit = 0.0 @@ -917,8 +918,9 @@ class LocalTrade(): exit_rate, amount=exit_amount, open_rate=avg_price) else: total_stake = total_stake + self._calc_open_trade_value(tmp_amount, price) + max_stake_amount += (tmp_amount * price) self.funding_fees = funding_fees - self.max_stake_amount = total_stake + self.max_stake_amount = float(max_stake_amount) if close_profit: self.close_profit = close_profit diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index ad6242b0e..fc14a0f88 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -710,6 +710,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: expected = pd.DataFrame( {'pair': [pair, pair], 'stake_amount': [0.001, 0.001], + 'max_stake_amount': [0.001, 0.001], 'amount': [0.00957442, 0.0097064], 'open_date': pd.to_datetime([Arrow(2018, 1, 29, 18, 40, 0).datetime, Arrow(2018, 1, 30, 3, 30, 0).datetime], utc=True diff --git a/tests/optimize/test_backtesting_adjust_position.py b/tests/optimize/test_backtesting_adjust_position.py index b97b45e26..5c740458f 100644 --- a/tests/optimize/test_backtesting_adjust_position.py +++ b/tests/optimize/test_backtesting_adjust_position.py @@ -50,6 +50,7 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) -> expected = pd.DataFrame( {'pair': [pair, pair], 'stake_amount': [500.0, 100.0], + 'max_stake_amount': [500.0, 100], 'amount': [4806.87657523, 970.63960782], 'open_date': pd.to_datetime([Arrow(2018, 1, 29, 18, 40, 0).datetime, Arrow(2018, 1, 30, 3, 30, 0).datetime], utc=True diff --git a/tests/testdata/backtest_results/backtest-result.json b/tests/testdata/backtest_results/backtest-result.json index f16f95c33..96440fdf5 100644 --- a/tests/testdata/backtest_results/backtest-result.json +++ b/tests/testdata/backtest_results/backtest-result.json @@ -1 +1 @@ -{"metadata":{"StrategyTestV3":{"run_id":"asdf","backtest_start_time":"2020-10-01 18:00:00+00:00"}},"strategy":{"StrategyTestV3":{"trades":[{"pair":"TRX/BTC","stake_amount":0.001,"amount":10.37344398340249,"open_date":"2018-01-10 07:15:00+00:00","close_date":"2018-01-10 07:20:00+00:00","open_rate":9.64e-05,"close_rate":0.00010074887218045112,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":8.676e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.676e-05,"stop_loss_ratio":0.1,"min_rate":9.64e-05,"max_rate":0.00010074887218045112,"is_open":false,"open_timestamp":1515568500000.0,"close_timestamp":1515568800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":21.026072329688816,"open_date":"2018-01-10 07:15:00+00:00","close_date":"2018-01-10 07:30:00+00:00","open_rate":4.756e-05,"close_rate":4.9705563909774425e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":15,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":4.2804e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.2804e-05,"stop_loss_ratio":0.1,"min_rate":4.756e-05,"max_rate":4.9705563909774425e-05,"is_open":false,"open_timestamp":1515568500000.0,"close_timestamp":1515569400000.0,"is_short":false,"leverage":1.0,"enter_tag":"buy_tag","orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":29.94908655286014,"open_date":"2018-01-10 07:25:00+00:00","close_date":"2018-01-10 07:35:00+00:00","open_rate":3.339e-05,"close_rate":3.489631578947368e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":10,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":3.0050999999999997e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.0050999999999997e-05,"stop_loss_ratio":0.1,"min_rate":3.339e-05,"max_rate":3.489631578947368e-05,"is_open":false,"open_timestamp":1515569100000.0,"close_timestamp":1515569700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":10.313531353135314,"open_date":"2018-01-10 07:25:00+00:00","close_date":"2018-01-10 07:40:00+00:00","open_rate":9.696e-05,"close_rate":0.00010133413533834584,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":15,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":8.7264e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.7264e-05,"stop_loss_ratio":0.1,"min_rate":9.696e-05,"max_rate":0.00010133413533834584,"is_open":false,"open_timestamp":1515569100000.0,"close_timestamp":1515570000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010604453870625663,"open_date":"2018-01-10 07:35:00+00:00","close_date":"2018-01-10 08:35:00+00:00","open_rate":0.0943,"close_rate":0.09477268170426063,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.08487,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.08487,"stop_loss_ratio":0.1,"min_rate":0.0943,"max_rate":0.09477268170426063,"is_open":false,"open_timestamp":1515569700000.0,"close_timestamp":1515573300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03677001860930642,"open_date":"2018-01-10 07:40:00+00:00","close_date":"2018-01-10 08:10:00+00:00","open_rate":0.02719607,"close_rate":0.02760503345864661,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.024476463,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.024476463,"stop_loss_ratio":0.1,"min_rate":0.02719607,"max_rate":0.02760503345864661,"is_open":false,"open_timestamp":1515570000000.0,"close_timestamp":1515571800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.021575196463739,"open_date":"2018-01-10 08:15:00+00:00","close_date":"2018-01-10 09:55:00+00:00","open_rate":0.04634952,"close_rate":0.046581848421052625,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":100,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.041714568,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.041714568,"stop_loss_ratio":0.1,"min_rate":0.04634952,"max_rate":0.046581848421052625,"is_open":false,"open_timestamp":1515572100000.0,"close_timestamp":1515578100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":32.615786040443574,"open_date":"2018-01-10 14:45:00+00:00","close_date":"2018-01-10 15:50:00+00:00","open_rate":3.066e-05,"close_rate":3.081368421052631e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":65,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":2.7594e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.7594e-05,"stop_loss_ratio":0.1,"min_rate":3.066e-05,"max_rate":3.081368421052631e-05,"is_open":false,"open_timestamp":1515595500000.0,"close_timestamp":1515599400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.05917194776300452,"open_date":"2018-01-10 16:35:00+00:00","close_date":"2018-01-10 17:15:00+00:00","open_rate":0.0168999,"close_rate":0.016984611278195488,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.01520991,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.01520991,"stop_loss_ratio":0.1,"min_rate":0.0168999,"max_rate":0.016984611278195488,"is_open":false,"open_timestamp":1515602100000.0,"close_timestamp":1515604500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010949822656672253,"open_date":"2018-01-10 16:40:00+00:00","close_date":"2018-01-10 17:20:00+00:00","open_rate":0.09132568,"close_rate":0.0917834528320802,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.08219311200000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.08219311200000001,"stop_loss_ratio":0.1,"min_rate":0.09132568,"max_rate":0.0917834528320802,"is_open":false,"open_timestamp":1515602400000.0,"close_timestamp":1515604800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011238476768326556,"open_date":"2018-01-10 18:50:00+00:00","close_date":"2018-01-10 19:45:00+00:00","open_rate":0.08898003,"close_rate":0.08942604518796991,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.080082027,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.080082027,"stop_loss_ratio":0.1,"min_rate":0.08898003,"max_rate":0.08942604518796991,"is_open":false,"open_timestamp":1515610200000.0,"close_timestamp":1515613500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011682232072680309,"open_date":"2018-01-10 22:15:00+00:00","close_date":"2018-01-10 23:00:00+00:00","open_rate":0.08560008,"close_rate":0.08602915308270676,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.077040072,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.077040072,"stop_loss_ratio":0.1,"min_rate":0.08560008,"max_rate":0.08602915308270676,"is_open":false,"open_timestamp":1515622500000.0,"close_timestamp":1515625200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.4014726015023105,"open_date":"2018-01-10 22:50:00+00:00","close_date":"2018-01-10 23:20:00+00:00","open_rate":0.00249083,"close_rate":0.0025282860902255634,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002241747,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002241747,"stop_loss_ratio":0.1,"min_rate":0.00249083,"max_rate":0.0025282860902255634,"is_open":false,"open_timestamp":1515624600000.0,"close_timestamp":1515626400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":33.090668431502316,"open_date":"2018-01-10 23:15:00+00:00","close_date":"2018-01-11 00:15:00+00:00","open_rate":3.022e-05,"close_rate":3.037147869674185e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":2.7198e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.7198e-05,"stop_loss_ratio":0.1,"min_rate":3.022e-05,"max_rate":3.037147869674185e-05,"is_open":false,"open_timestamp":1515626100000.0,"close_timestamp":1515629700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.41034058268362744,"open_date":"2018-01-10 23:40:00+00:00","close_date":"2018-01-11 00:05:00+00:00","open_rate":0.002437,"close_rate":0.0024980776942355883,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":0.0021933,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0021933,"stop_loss_ratio":0.1,"min_rate":0.002437,"max_rate":0.0024980776942355883,"is_open":false,"open_timestamp":1515627600000.0,"close_timestamp":1515629100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02095643931654345,"open_date":"2018-01-11 00:00:00+00:00","close_date":"2018-01-11 00:35:00+00:00","open_rate":0.04771803,"close_rate":0.04843559436090225,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.042946227,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.042946227,"stop_loss_ratio":0.1,"min_rate":0.04771803,"max_rate":0.04843559436090225,"is_open":false,"open_timestamp":1515628800000.0,"close_timestamp":1515630900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":27.389756231169542,"open_date":"2018-01-11 03:40:00+00:00","close_date":"2018-01-11 04:25:00+00:00","open_rate":3.651e-05,"close_rate":3.2859000000000005e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.10448878,"profit_abs":-9.999999999999994e-05,"exit_reason":"stop_loss","initial_stop_loss_abs":3.2859000000000005e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.2859000000000005e-05,"stop_loss_ratio":0.1,"min_rate":3.2859000000000005e-05,"max_rate":3.651e-05,"is_open":false,"open_timestamp":1515642000000.0,"close_timestamp":1515644700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011332594070446804,"open_date":"2018-01-11 03:55:00+00:00","close_date":"2018-01-11 04:25:00+00:00","open_rate":0.08824105,"close_rate":0.08956798308270676,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.079416945,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.079416945,"stop_loss_ratio":0.1,"min_rate":0.08824105,"max_rate":0.08956798308270676,"is_open":false,"open_timestamp":1515642900000.0,"close_timestamp":1515644700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.411522633744856,"open_date":"2018-01-11 04:00:00+00:00","close_date":"2018-01-11 04:50:00+00:00","open_rate":0.00243,"close_rate":0.002442180451127819,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002187,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002187,"stop_loss_ratio":0.1,"min_rate":0.00243,"max_rate":0.002442180451127819,"is_open":false,"open_timestamp":1515643200000.0,"close_timestamp":1515646200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.022001890402423376,"open_date":"2018-01-11 04:30:00+00:00","close_date":"2018-01-11 04:55:00+00:00","open_rate":0.04545064,"close_rate":0.046589753784461146,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":0.040905576,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.040905576,"stop_loss_ratio":0.1,"min_rate":0.04545064,"max_rate":0.046589753784461146,"is_open":false,"open_timestamp":1515645000000.0,"close_timestamp":1515646500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":29.655990510083036,"open_date":"2018-01-11 04:30:00+00:00","close_date":"2018-01-11 04:50:00+00:00","open_rate":3.372e-05,"close_rate":3.456511278195488e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":3.0348e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.0348e-05,"stop_loss_ratio":0.1,"min_rate":3.372e-05,"max_rate":3.456511278195488e-05,"is_open":false,"open_timestamp":1515645000000.0,"close_timestamp":1515646200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.037821482602118005,"open_date":"2018-01-11 04:55:00+00:00","close_date":"2018-01-11 05:15:00+00:00","open_rate":0.02644,"close_rate":0.02710265664160401,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":0.023796,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.023796,"stop_loss_ratio":0.1,"min_rate":0.02644,"max_rate":0.02710265664160401,"is_open":false,"open_timestamp":1515646500000.0,"close_timestamp":1515647700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011348161597821153,"open_date":"2018-01-11 11:20:00+00:00","close_date":"2018-01-11 12:00:00+00:00","open_rate":0.08812,"close_rate":0.08856170426065162,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.079308,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.079308,"stop_loss_ratio":0.1,"min_rate":0.08812,"max_rate":0.08856170426065162,"is_open":false,"open_timestamp":1515669600000.0,"close_timestamp":1515672000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.037263696923919086,"open_date":"2018-01-11 11:35:00+00:00","close_date":"2018-01-11 12:15:00+00:00","open_rate":0.02683577,"close_rate":0.026970285137844607,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.024152193,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.024152193,"stop_loss_ratio":0.1,"min_rate":0.02683577,"max_rate":0.026970285137844607,"is_open":false,"open_timestamp":1515670500000.0,"close_timestamp":1515672900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":20.329335230737954,"open_date":"2018-01-11 14:00:00+00:00","close_date":"2018-01-11 14:25:00+00:00","open_rate":4.919e-05,"close_rate":5.04228320802005e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":4.4271e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.4271e-05,"stop_loss_ratio":0.1,"min_rate":4.919e-05,"max_rate":5.04228320802005e-05,"is_open":false,"open_timestamp":1515679200000.0,"close_timestamp":1515680700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.01138317402960718,"open_date":"2018-01-11 19:25:00+00:00","close_date":"2018-01-11 20:35:00+00:00","open_rate":0.08784896,"close_rate":0.08828930566416039,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":70,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.079064064,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.079064064,"stop_loss_ratio":0.1,"min_rate":0.08784896,"max_rate":0.08828930566416039,"is_open":false,"open_timestamp":1515698700000.0,"close_timestamp":1515702900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.58863858961802,"open_date":"2018-01-11 22:35:00+00:00","close_date":"2018-01-11 23:30:00+00:00","open_rate":5.105e-05,"close_rate":5.130588972431077e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.5945e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.5945e-05,"stop_loss_ratio":0.1,"min_rate":5.105e-05,"max_rate":5.130588972431077e-05,"is_open":false,"open_timestamp":1515710100000.0,"close_timestamp":1515713400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":25.252525252525253,"open_date":"2018-01-11 22:55:00+00:00","close_date":"2018-01-11 23:25:00+00:00","open_rate":3.96e-05,"close_rate":4.019548872180451e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":3.5640000000000004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.5640000000000004e-05,"stop_loss_ratio":0.1,"min_rate":3.96e-05,"max_rate":4.019548872180451e-05,"is_open":false,"open_timestamp":1515711300000.0,"close_timestamp":1515713100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":34.66204506065858,"open_date":"2018-01-11 22:55:00+00:00","close_date":"2018-01-11 23:35:00+00:00","open_rate":2.885e-05,"close_rate":2.899461152882205e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":2.5965e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.5965e-05,"stop_loss_ratio":0.1,"min_rate":2.885e-05,"max_rate":2.899461152882205e-05,"is_open":false,"open_timestamp":1515711300000.0,"close_timestamp":1515713700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03780718336483932,"open_date":"2018-01-11 23:30:00+00:00","close_date":"2018-01-12 00:05:00+00:00","open_rate":0.02645,"close_rate":0.026847744360902256,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":0.023805000000000003,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.023805000000000003,"stop_loss_ratio":0.1,"min_rate":0.02645,"max_rate":0.026847744360902256,"is_open":false,"open_timestamp":1515713400000.0,"close_timestamp":1515715500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.020833333333333332,"open_date":"2018-01-11 23:55:00+00:00","close_date":"2018-01-12 01:15:00+00:00","open_rate":0.048,"close_rate":0.04824060150375939,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0432,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0432,"stop_loss_ratio":0.1,"min_rate":0.048,"max_rate":0.04824060150375939,"is_open":false,"open_timestamp":1515714900000.0,"close_timestamp":1515719700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":21.31287297527707,"open_date":"2018-01-12 21:15:00+00:00","close_date":"2018-01-12 21:40:00+00:00","open_rate":4.692e-05,"close_rate":4.809593984962405e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":4.2228e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.2228e-05,"stop_loss_ratio":0.1,"min_rate":4.692e-05,"max_rate":4.809593984962405e-05,"is_open":false,"open_timestamp":1515791700000.0,"close_timestamp":1515793200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.38915654211062944,"open_date":"2018-01-13 00:55:00+00:00","close_date":"2018-01-13 06:20:00+00:00","open_rate":0.00256966,"close_rate":0.0025825405012531327,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":325,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002312694,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002312694,"stop_loss_ratio":0.1,"min_rate":0.00256966,"max_rate":0.0025825405012531327,"is_open":false,"open_timestamp":1515804900000.0,"close_timestamp":1515824400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":15.96933886937081,"open_date":"2018-01-13 10:55:00+00:00","close_date":"2018-01-13 11:35:00+00:00","open_rate":6.262e-05,"close_rate":6.293388471177944e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":5.6358e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.6358e-05,"stop_loss_ratio":0.1,"min_rate":6.262e-05,"max_rate":6.293388471177944e-05,"is_open":false,"open_timestamp":1515840900000.0,"close_timestamp":1515843300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":21.14164904862579,"open_date":"2018-01-13 13:05:00+00:00","close_date":"2018-01-15 14:10:00+00:00","open_rate":4.73e-05,"close_rate":4.753709273182957e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":2945,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.257e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.257e-05,"stop_loss_ratio":0.1,"min_rate":4.73e-05,"max_rate":4.753709273182957e-05,"is_open":false,"open_timestamp":1515848700000.0,"close_timestamp":1516025400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":16.49348507339601,"open_date":"2018-01-13 13:30:00+00:00","close_date":"2018-01-13 14:45:00+00:00","open_rate":6.063e-05,"close_rate":6.0933909774436085e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":75,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":5.4567e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.4567e-05,"stop_loss_ratio":0.1,"min_rate":6.063e-05,"max_rate":6.0933909774436085e-05,"is_open":false,"open_timestamp":1515850200000.0,"close_timestamp":1515854700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":9.023641941887746,"open_date":"2018-01-13 13:40:00+00:00","close_date":"2018-01-13 23:30:00+00:00","open_rate":0.00011082,"close_rate":0.00011137548872180448,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":590,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":9.9738e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":9.9738e-05,"stop_loss_ratio":0.1,"min_rate":0.00011082,"max_rate":0.00011137548872180448,"is_open":false,"open_timestamp":1515850800000.0,"close_timestamp":1515886200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":16.863406408094438,"open_date":"2018-01-13 15:15:00+00:00","close_date":"2018-01-13 15:55:00+00:00","open_rate":5.93e-05,"close_rate":5.9597243107769415e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":5.337e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.337e-05,"stop_loss_ratio":0.1,"min_rate":5.93e-05,"max_rate":5.9597243107769415e-05,"is_open":false,"open_timestamp":1515856500000.0,"close_timestamp":1515858900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.020618543947292404,"open_date":"2018-01-13 16:30:00+00:00","close_date":"2018-01-13 17:10:00+00:00","open_rate":0.04850003,"close_rate":0.04874313791979949,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.043650027,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.043650027,"stop_loss_ratio":0.1,"min_rate":0.04850003,"max_rate":0.04874313791979949,"is_open":false,"open_timestamp":1515861000000.0,"close_timestamp":1515863400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010178097365511457,"open_date":"2018-01-13 22:05:00+00:00","close_date":"2018-01-14 06:25:00+00:00","open_rate":0.09825019,"close_rate":0.09874267215538848,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":500,"profit_ratio":-0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.088425171,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.088425171,"stop_loss_ratio":0.1,"min_rate":0.09825019,"max_rate":0.09874267215538848,"is_open":false,"open_timestamp":1515881100000.0,"close_timestamp":1515911100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":16.616816218012627,"open_date":"2018-01-14 00:20:00+00:00","close_date":"2018-01-14 22:55:00+00:00","open_rate":6.018e-05,"close_rate":6.048165413533834e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":1355,"profit_ratio":0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":5.4162e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.4162e-05,"stop_loss_ratio":0.1,"min_rate":6.018e-05,"max_rate":6.048165413533834e-05,"is_open":false,"open_timestamp":1515889200000.0,"close_timestamp":1515970500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010246952581919518,"open_date":"2018-01-14 12:45:00+00:00","close_date":"2018-01-14 13:25:00+00:00","open_rate":0.09758999,"close_rate":0.0980791628822055,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.087830991,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.087830991,"stop_loss_ratio":0.1,"min_rate":0.09758999,"max_rate":0.0980791628822055,"is_open":false,"open_timestamp":1515933900000.0,"close_timestamp":1515936300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.3215434083601286,"open_date":"2018-01-14 15:30:00+00:00","close_date":"2018-01-14 16:00:00+00:00","open_rate":0.00311,"close_rate":0.0031567669172932328,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002799,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002799,"stop_loss_ratio":0.1,"min_rate":0.00311,"max_rate":0.0031567669172932328,"is_open":false,"open_timestamp":1515943800000.0,"close_timestamp":1515945600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.32010140812609433,"open_date":"2018-01-14 20:45:00+00:00","close_date":"2018-01-14 22:15:00+00:00","open_rate":0.00312401,"close_rate":0.003139669197994987,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":90,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002811609,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002811609,"stop_loss_ratio":0.1,"min_rate":0.00312401,"max_rate":0.003139669197994987,"is_open":false,"open_timestamp":1515962700000.0,"close_timestamp":1515968100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.057247866085791646,"open_date":"2018-01-14 23:35:00+00:00","close_date":"2018-01-15 00:30:00+00:00","open_rate":0.0174679,"close_rate":0.017555458395989976,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.015721110000000003,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.015721110000000003,"stop_loss_ratio":0.1,"min_rate":0.0174679,"max_rate":0.017555458395989976,"is_open":false,"open_timestamp":1515972900000.0,"close_timestamp":1515976200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.013611282991367995,"open_date":"2018-01-14 23:45:00+00:00","close_date":"2018-01-15 00:25:00+00:00","open_rate":0.07346846,"close_rate":0.07383672295739348,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.066121614,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.066121614,"stop_loss_ratio":0.1,"min_rate":0.07346846,"max_rate":0.07383672295739348,"is_open":false,"open_timestamp":1515973500000.0,"close_timestamp":1515975900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010204706410596568,"open_date":"2018-01-15 02:25:00+00:00","close_date":"2018-01-15 03:05:00+00:00","open_rate":0.097994,"close_rate":0.09848519799498744,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0881946,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0881946,"stop_loss_ratio":0.1,"min_rate":0.097994,"max_rate":0.09848519799498744,"is_open":false,"open_timestamp":1515983100000.0,"close_timestamp":1515985500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010353038616834042,"open_date":"2018-01-15 07:20:00+00:00","close_date":"2018-01-15 08:00:00+00:00","open_rate":0.09659,"close_rate":0.09707416040100247,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.086931,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.086931,"stop_loss_ratio":0.1,"min_rate":0.09659,"max_rate":0.09707416040100247,"is_open":false,"open_timestamp":1516000800000.0,"close_timestamp":1516003200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":10.0130169219986,"open_date":"2018-01-15 08:20:00+00:00","close_date":"2018-01-15 08:55:00+00:00","open_rate":9.987e-05,"close_rate":0.00010137180451127818,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":8.9883e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.9883e-05,"stop_loss_ratio":0.1,"min_rate":9.987e-05,"max_rate":0.00010137180451127818,"is_open":false,"open_timestamp":1516004400000.0,"close_timestamp":1516006500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010537752023511832,"open_date":"2018-01-15 12:10:00+00:00","close_date":"2018-01-16 02:50:00+00:00","open_rate":0.0948969,"close_rate":0.09537257368421052,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":880,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.08540721000000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.08540721000000001,"stop_loss_ratio":0.1,"min_rate":0.0948969,"max_rate":0.09537257368421052,"is_open":false,"open_timestamp":1516018200000.0,"close_timestamp":1516071000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014084507042253523,"open_date":"2018-01-15 14:10:00+00:00","close_date":"2018-01-15 17:40:00+00:00","open_rate":0.071,"close_rate":0.07135588972431077,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":210,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0639,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0639,"stop_loss_ratio":0.1,"min_rate":0.071,"max_rate":0.07135588972431077,"is_open":false,"open_timestamp":1516025400000.0,"close_timestamp":1516038000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.021736763017766975,"open_date":"2018-01-15 14:30:00+00:00","close_date":"2018-01-15 15:10:00+00:00","open_rate":0.04600501,"close_rate":0.046235611553884705,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.041404509,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.041404509,"stop_loss_ratio":0.1,"min_rate":0.04600501,"max_rate":0.046235611553884705,"is_open":false,"open_timestamp":1516026600000.0,"close_timestamp":1516029000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":10.595465140919686,"open_date":"2018-01-15 18:10:00+00:00","close_date":"2018-01-15 19:25:00+00:00","open_rate":9.438e-05,"close_rate":9.485308270676693e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":75,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":8.4942e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.4942e-05,"stop_loss_ratio":0.1,"min_rate":9.438e-05,"max_rate":9.485308270676693e-05,"is_open":false,"open_timestamp":1516039800000.0,"close_timestamp":1516044300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.032894726021471705,"open_date":"2018-01-15 18:35:00+00:00","close_date":"2018-01-15 19:15:00+00:00","open_rate":0.03040001,"close_rate":0.030552391002506264,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.027360009,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.027360009,"stop_loss_ratio":0.1,"min_rate":0.03040001,"max_rate":0.030552391002506264,"is_open":false,"open_timestamp":1516041300000.0,"close_timestamp":1516043700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":17.13208840157615,"open_date":"2018-01-15 20:25:00+00:00","close_date":"2018-01-16 08:25:00+00:00","open_rate":5.837e-05,"close_rate":5.2533e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":720,"profit_ratio":-0.10448878,"profit_abs":-0.00010000000000000005,"exit_reason":"stop_loss","initial_stop_loss_abs":5.2533e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.2533e-05,"stop_loss_ratio":0.1,"min_rate":5.2533e-05,"max_rate":5.837e-05,"is_open":false,"open_timestamp":1516047900000.0,"close_timestamp":1516091100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.021722130506560085,"open_date":"2018-01-15 20:40:00+00:00","close_date":"2018-01-15 22:00:00+00:00","open_rate":0.046036,"close_rate":0.04626675689223057,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0414324,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0414324,"stop_loss_ratio":0.1,"min_rate":0.046036,"max_rate":0.04626675689223057,"is_open":false,"open_timestamp":1516048800000.0,"close_timestamp":1516053600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.34861425832316545,"open_date":"2018-01-16 00:30:00+00:00","close_date":"2018-01-16 01:10:00+00:00","open_rate":0.0028685,"close_rate":0.0028828784461152877,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.00258165,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.00258165,"stop_loss_ratio":0.1,"min_rate":0.0028685,"max_rate":0.0028828784461152877,"is_open":false,"open_timestamp":1516062600000.0,"close_timestamp":1516065000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014854967241083492,"open_date":"2018-01-16 01:15:00+00:00","close_date":"2018-01-16 02:35:00+00:00","open_rate":0.06731755,"close_rate":0.0676549813283208,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.060585795000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.060585795000000005,"stop_loss_ratio":0.1,"min_rate":0.06731755,"max_rate":0.0676549813283208,"is_open":false,"open_timestamp":1516065300000.0,"close_timestamp":1516070100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010848794492804754,"open_date":"2018-01-16 07:45:00+00:00","close_date":"2018-01-16 08:40:00+00:00","open_rate":0.09217614,"close_rate":0.09263817578947368,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.082958526,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.082958526,"stop_loss_ratio":0.1,"min_rate":0.09217614,"max_rate":0.09263817578947368,"is_open":false,"open_timestamp":1516088700000.0,"close_timestamp":1516092000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.06060606060606061,"open_date":"2018-01-16 08:35:00+00:00","close_date":"2018-01-16 08:55:00+00:00","open_rate":0.0165,"close_rate":0.016913533834586467,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641604113e-05,"exit_reason":"roi","initial_stop_loss_abs":0.01485,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.01485,"stop_loss_ratio":0.1,"min_rate":0.0165,"max_rate":0.016913533834586467,"is_open":false,"open_timestamp":1516091700000.0,"close_timestamp":1516092900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":12.57387149503332,"open_date":"2018-01-16 08:35:00+00:00","close_date":"2018-01-16 08:40:00+00:00","open_rate":7.953e-05,"close_rate":8.311781954887218e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":7.157700000000001e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":7.157700000000001e-05,"stop_loss_ratio":0.1,"min_rate":7.953e-05,"max_rate":8.311781954887218e-05,"is_open":false,"open_timestamp":1516091700000.0,"close_timestamp":1516092000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.022122914915269236,"open_date":"2018-01-16 08:45:00+00:00","close_date":"2018-01-16 09:50:00+00:00","open_rate":0.045202,"close_rate":0.04542857644110275,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":65,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0406818,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0406818,"stop_loss_ratio":0.1,"min_rate":0.045202,"max_rate":0.04542857644110275,"is_open":false,"open_timestamp":1516092300000.0,"close_timestamp":1516096200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.054878048780488,"open_date":"2018-01-16 09:15:00+00:00","close_date":"2018-01-16 09:45:00+00:00","open_rate":5.248e-05,"close_rate":5.326917293233082e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":4.7232e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7232e-05,"stop_loss_ratio":0.1,"min_rate":5.248e-05,"max_rate":5.326917293233082e-05,"is_open":false,"open_timestamp":1516094100000.0,"close_timestamp":1516095900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03457434486802627,"open_date":"2018-01-16 09:15:00+00:00","close_date":"2018-01-16 09:55:00+00:00","open_rate":0.02892318,"close_rate":0.02906815834586466,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.026030862,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.026030862,"stop_loss_ratio":0.1,"min_rate":0.02892318,"max_rate":0.02906815834586466,"is_open":false,"open_timestamp":1516094100000.0,"close_timestamp":1516096500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.38735944164405,"open_date":"2018-01-16 09:50:00+00:00","close_date":"2018-01-16 10:10:00+00:00","open_rate":5.158e-05,"close_rate":5.287273182957392e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":4.6422e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6422e-05,"stop_loss_ratio":0.1,"min_rate":5.158e-05,"max_rate":5.287273182957392e-05,"is_open":false,"open_timestamp":1516096200000.0,"close_timestamp":1516097400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.022948496230938985,"open_date":"2018-01-16 10:05:00+00:00","close_date":"2018-01-16 10:40:00+00:00","open_rate":0.04357584,"close_rate":0.044231115789473675,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.039218256,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.039218256,"stop_loss_ratio":0.1,"min_rate":0.04357584,"max_rate":0.044231115789473675,"is_open":false,"open_timestamp":1516097100000.0,"close_timestamp":1516099200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.035357778286929785,"open_date":"2018-01-16 10:05:00+00:00","close_date":"2018-01-16 10:35:00+00:00","open_rate":0.02828232,"close_rate":0.02870761804511278,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.025454088,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025454088,"stop_loss_ratio":0.1,"min_rate":0.02828232,"max_rate":0.02870761804511278,"is_open":false,"open_timestamp":1516097100000.0,"close_timestamp":1516098900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":18.64975755315181,"open_date":"2018-01-16 13:45:00+00:00","close_date":"2018-01-16 14:20:00+00:00","open_rate":5.362e-05,"close_rate":5.442631578947368e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":4.8258e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.8258e-05,"stop_loss_ratio":0.1,"min_rate":5.362e-05,"max_rate":5.442631578947368e-05,"is_open":false,"open_timestamp":1516110300000.0,"close_timestamp":1516112400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":18.86080724254998,"open_date":"2018-01-16 17:30:00+00:00","close_date":"2018-01-16 18:25:00+00:00","open_rate":5.302e-05,"close_rate":5.328576441102756e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.7718e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7718e-05,"stop_loss_ratio":0.1,"min_rate":5.302e-05,"max_rate":5.328576441102756e-05,"is_open":false,"open_timestamp":1516123800000.0,"close_timestamp":1516127100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010952903718828448,"open_date":"2018-01-16 18:15:00+00:00","close_date":"2018-01-16 18:45:00+00:00","open_rate":0.09129999,"close_rate":0.09267292218045112,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":0.082169991,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.082169991,"stop_loss_ratio":0.1,"min_rate":0.09129999,"max_rate":0.09267292218045112,"is_open":false,"open_timestamp":1516126500000.0,"close_timestamp":1516128300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":26.26050420168067,"open_date":"2018-01-16 18:15:00+00:00","close_date":"2018-01-16 18:35:00+00:00","open_rate":3.808e-05,"close_rate":3.903438596491228e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":3.4272e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.4272e-05,"stop_loss_ratio":0.1,"min_rate":3.808e-05,"max_rate":3.903438596491228e-05,"is_open":false,"open_timestamp":1516126500000.0,"close_timestamp":1516127700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.035574376772493324,"open_date":"2018-01-16 19:00:00+00:00","close_date":"2018-01-16 19:30:00+00:00","open_rate":0.02811012,"close_rate":0.028532828571428567,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.025299108,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025299108,"stop_loss_ratio":0.1,"min_rate":0.02811012,"max_rate":0.028532828571428567,"is_open":false,"open_timestamp":1516129200000.0,"close_timestamp":1516131000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.387028357567759,"open_date":"2018-01-16 21:25:00+00:00","close_date":"2018-01-16 22:25:00+00:00","open_rate":0.00258379,"close_rate":0.002325411,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":-0.10448878,"profit_abs":-0.00010000000000000005,"exit_reason":"stop_loss","initial_stop_loss_abs":0.002325411,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002325411,"stop_loss_ratio":0.1,"min_rate":0.002325411,"max_rate":0.00258379,"is_open":false,"open_timestamp":1516137900000.0,"close_timestamp":1516141500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":39.07776475185619,"open_date":"2018-01-16 21:25:00+00:00","close_date":"2018-01-16 22:45:00+00:00","open_rate":2.559e-05,"close_rate":2.3031e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":-0.10448878,"profit_abs":-0.00010000000000000005,"exit_reason":"stop_loss","initial_stop_loss_abs":2.3031e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.3031e-05,"stop_loss_ratio":0.1,"min_rate":2.3031e-05,"max_rate":2.559e-05,"is_open":false,"open_timestamp":1516137900000.0,"close_timestamp":1516142700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":13.123359580052494,"open_date":"2018-01-16 21:35:00+00:00","close_date":"2018-01-16 22:25:00+00:00","open_rate":7.62e-05,"close_rate":6.858e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":-0.10448878,"profit_abs":-0.00010000000000000005,"exit_reason":"stop_loss","initial_stop_loss_abs":6.858e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":6.858e-05,"stop_loss_ratio":0.1,"min_rate":6.858e-05,"max_rate":7.62e-05,"is_open":false,"open_timestamp":1516138500000.0,"close_timestamp":1516141500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.06622516556291391,"open_date":"2018-01-16 22:30:00+00:00","close_date":"2018-01-16 22:40:00+00:00","open_rate":0.0151,"close_rate":0.015781203007518795,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":10,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":0.01359,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.01359,"stop_loss_ratio":0.1,"min_rate":0.0151,"max_rate":0.015781203007518795,"is_open":false,"open_timestamp":1516141800000.0,"close_timestamp":1516142400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.4350777048780912,"open_date":"2018-01-16 22:30:00+00:00","close_date":"2018-01-16 22:35:00+00:00","open_rate":0.00229844,"close_rate":0.002402129022556391,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":4.511278195488727e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002068596,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002068596,"stop_loss_ratio":0.1,"min_rate":0.00229844,"max_rate":0.002402129022556391,"is_open":false,"open_timestamp":1516141800000.0,"close_timestamp":1516142100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.4243113426908128,"open_date":"2018-01-16 22:40:00+00:00","close_date":"2018-01-16 22:45:00+00:00","open_rate":0.00235676,"close_rate":0.00246308,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":4.511278195488727e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002121084,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002121084,"stop_loss_ratio":0.1,"min_rate":0.00235676,"max_rate":0.00246308,"is_open":false,"open_timestamp":1516142400000.0,"close_timestamp":1516142700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.01585559988076589,"open_date":"2018-01-16 22:45:00+00:00","close_date":"2018-01-16 23:05:00+00:00","open_rate":0.0630692,"close_rate":0.06464988170426066,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641604113e-05,"exit_reason":"roi","initial_stop_loss_abs":0.056762280000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.056762280000000005,"stop_loss_ratio":0.1,"min_rate":0.0630692,"max_rate":0.06464988170426066,"is_open":false,"open_timestamp":1516142700000.0,"close_timestamp":1516143900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":45.45454545454545,"open_date":"2018-01-16 22:50:00+00:00","close_date":"2018-01-16 22:55:00+00:00","open_rate":2.2e-05,"close_rate":2.299248120300751e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":4.511278195488684e-05,"exit_reason":"roi","initial_stop_loss_abs":1.98e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":1.98e-05,"stop_loss_ratio":0.1,"min_rate":2.2e-05,"max_rate":2.299248120300751e-05,"is_open":false,"open_timestamp":1516143000000.0,"close_timestamp":1516143300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":20.10454362685967,"open_date":"2018-01-17 03:30:00+00:00","close_date":"2018-01-17 04:00:00+00:00","open_rate":4.974e-05,"close_rate":5.048796992481203e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":4.4766000000000005e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.4766000000000005e-05,"stop_loss_ratio":0.1,"min_rate":4.974e-05,"max_rate":5.048796992481203e-05,"is_open":false,"open_timestamp":1516159800000.0,"close_timestamp":1516161600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":14.068655036578503,"open_date":"2018-01-17 03:55:00+00:00","close_date":"2018-01-17 04:15:00+00:00","open_rate":7.108e-05,"close_rate":7.28614536340852e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":6.3972e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":6.3972e-05,"stop_loss_ratio":0.1,"min_rate":7.108e-05,"max_rate":7.28614536340852e-05,"is_open":false,"open_timestamp":1516161300000.0,"close_timestamp":1516162500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.0231107002542177,"open_date":"2018-01-17 09:35:00+00:00","close_date":"2018-01-17 10:15:00+00:00","open_rate":0.04327,"close_rate":0.04348689223057644,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.038943000000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.038943000000000005,"stop_loss_ratio":0.1,"min_rate":0.04327,"max_rate":0.04348689223057644,"is_open":false,"open_timestamp":1516181700000.0,"close_timestamp":1516184100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":20.012007204322593,"open_date":"2018-01-17 10:20:00+00:00","close_date":"2018-01-17 17:00:00+00:00","open_rate":4.997e-05,"close_rate":5.022047619047618e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":400,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":4.4973e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.4973e-05,"stop_loss_ratio":0.1,"min_rate":4.997e-05,"max_rate":5.022047619047618e-05,"is_open":false,"open_timestamp":1516184400000.0,"close_timestamp":1516208400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014626687444363738,"open_date":"2018-01-17 10:30:00+00:00","close_date":"2018-01-17 11:25:00+00:00","open_rate":0.06836818,"close_rate":0.06871087764411027,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.061531362,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.061531362,"stop_loss_ratio":0.1,"min_rate":0.06836818,"max_rate":0.06871087764411027,"is_open":false,"open_timestamp":1516185000000.0,"close_timestamp":1516188300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":27.548209366391184,"open_date":"2018-01-17 10:30:00+00:00","close_date":"2018-01-17 11:10:00+00:00","open_rate":3.63e-05,"close_rate":3.648195488721804e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":3.2670000000000004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.2670000000000004e-05,"stop_loss_ratio":0.1,"min_rate":3.63e-05,"max_rate":3.648195488721804e-05,"is_open":false,"open_timestamp":1516185000000.0,"close_timestamp":1516187400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03558718861209965,"open_date":"2018-01-17 12:30:00+00:00","close_date":"2018-01-17 22:05:00+00:00","open_rate":0.0281,"close_rate":0.02824085213032581,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":575,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.02529,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.02529,"stop_loss_ratio":0.1,"min_rate":0.0281,"max_rate":0.02824085213032581,"is_open":false,"open_timestamp":1516192200000.0,"close_timestamp":1516226700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011559355963546878,"open_date":"2018-01-17 12:35:00+00:00","close_date":"2018-01-17 16:55:00+00:00","open_rate":0.08651001,"close_rate":0.08694364413533832,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":260,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.077859009,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.077859009,"stop_loss_ratio":0.1,"min_rate":0.08651001,"max_rate":0.08694364413533832,"is_open":false,"open_timestamp":1516192500000.0,"close_timestamp":1516208100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":17.752529735487308,"open_date":"2018-01-18 05:00:00+00:00","close_date":"2018-01-18 05:55:00+00:00","open_rate":5.633e-05,"close_rate":5.6612355889724306e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":5.0697e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.0697e-05,"stop_loss_ratio":0.1,"min_rate":5.633e-05,"max_rate":5.6612355889724306e-05,"is_open":false,"open_timestamp":1516251600000.0,"close_timestamp":1516254900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.01430923457900944,"open_date":"2018-01-18 05:20:00+00:00","close_date":"2018-01-18 05:55:00+00:00","open_rate":0.06988494,"close_rate":0.07093584135338346,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":0.06289644600000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.06289644600000001,"stop_loss_ratio":0.1,"min_rate":0.06988494,"max_rate":0.07093584135338346,"is_open":false,"open_timestamp":1516252800000.0,"close_timestamp":1516254900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":18.034265103697024,"open_date":"2018-01-18 07:35:00+00:00","close_date":"2018-01-18 08:15:00+00:00","open_rate":5.545e-05,"close_rate":5.572794486215538e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":4.9905e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.9905e-05,"stop_loss_ratio":0.1,"min_rate":5.545e-05,"max_rate":5.572794486215538e-05,"is_open":false,"open_timestamp":1516260900000.0,"close_timestamp":1516263300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.06121723118136401,"open_date":"2018-01-18 09:00:00+00:00","close_date":"2018-01-18 09:40:00+00:00","open_rate":0.01633527,"close_rate":0.016417151052631574,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.014701743,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.014701743,"stop_loss_ratio":0.1,"min_rate":0.01633527,"max_rate":0.016417151052631574,"is_open":false,"open_timestamp":1516266000000.0,"close_timestamp":1516268400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.3707356136045141,"open_date":"2018-01-18 16:40:00+00:00","close_date":"2018-01-18 17:20:00+00:00","open_rate":0.00269734,"close_rate":0.002710860501253133,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002427606,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002427606,"stop_loss_ratio":0.1,"min_rate":0.00269734,"max_rate":0.002710860501253133,"is_open":false,"open_timestamp":1516293600000.0,"close_timestamp":1516296000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":22.3463687150838,"open_date":"2018-01-18 18:05:00+00:00","close_date":"2018-01-18 18:30:00+00:00","open_rate":4.475e-05,"close_rate":4.587155388471177e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":2.5062656641604113e-05,"exit_reason":"roi","initial_stop_loss_abs":4.0275e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.0275e-05,"stop_loss_ratio":0.1,"min_rate":4.475e-05,"max_rate":4.587155388471177e-05,"is_open":false,"open_timestamp":1516298700000.0,"close_timestamp":1516300200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":35.842293906810035,"open_date":"2018-01-18 18:25:00+00:00","close_date":"2018-01-18 18:55:00+00:00","open_rate":2.79e-05,"close_rate":2.8319548872180444e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":2.511e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.511e-05,"stop_loss_ratio":0.1,"min_rate":2.79e-05,"max_rate":2.8319548872180444e-05,"is_open":false,"open_timestamp":1516299900000.0,"close_timestamp":1516301700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.022525942001105578,"open_date":"2018-01-18 20:10:00+00:00","close_date":"2018-01-18 20:50:00+00:00","open_rate":0.04439326,"close_rate":0.04461578260651629,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.039953934,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.039953934,"stop_loss_ratio":0.1,"min_rate":0.04439326,"max_rate":0.04461578260651629,"is_open":false,"open_timestamp":1516306200000.0,"close_timestamp":1516308600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":22.271714922048996,"open_date":"2018-01-18 21:30:00+00:00","close_date":"2018-01-19 00:35:00+00:00","open_rate":4.49e-05,"close_rate":4.51250626566416e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":185,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.041e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.041e-05,"stop_loss_ratio":0.1,"min_rate":4.49e-05,"max_rate":4.51250626566416e-05,"is_open":false,"open_timestamp":1516311000000.0,"close_timestamp":1516322100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03502626970227671,"open_date":"2018-01-18 21:55:00+00:00","close_date":"2018-01-19 05:05:00+00:00","open_rate":0.02855,"close_rate":0.028693107769423555,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":430,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.025695,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025695,"stop_loss_ratio":0.1,"min_rate":0.02855,"max_rate":0.028693107769423555,"is_open":false,"open_timestamp":1516312500000.0,"close_timestamp":1516338300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":17.25327812284334,"open_date":"2018-01-18 22:10:00+00:00","close_date":"2018-01-18 22:50:00+00:00","open_rate":5.796e-05,"close_rate":5.8250526315789473e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":5.2164e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.2164e-05,"stop_loss_ratio":0.1,"min_rate":5.796e-05,"max_rate":5.8250526315789473e-05,"is_open":false,"open_timestamp":1516313400000.0,"close_timestamp":1516315800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02303975994413319,"open_date":"2018-01-18 23:50:00+00:00","close_date":"2018-01-19 00:30:00+00:00","open_rate":0.04340323,"close_rate":0.04362079005012531,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.039062907,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.039062907,"stop_loss_ratio":0.1,"min_rate":0.04340323,"max_rate":0.04362079005012531,"is_open":false,"open_timestamp":1516319400000.0,"close_timestamp":1516321800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02244943545282195,"open_date":"2018-01-19 16:45:00+00:00","close_date":"2018-01-19 17:35:00+00:00","open_rate":0.04454455,"close_rate":0.04476783095238095,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.040090095000000006,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.040090095000000006,"stop_loss_ratio":0.1,"min_rate":0.04454455,"max_rate":0.04476783095238095,"is_open":false,"open_timestamp":1516380300000.0,"close_timestamp":1516383300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":17.793594306049823,"open_date":"2018-01-19 17:15:00+00:00","close_date":"2018-01-19 19:55:00+00:00","open_rate":5.62e-05,"close_rate":5.648170426065162e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":160,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":5.058e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.058e-05,"stop_loss_ratio":0.1,"min_rate":5.62e-05,"max_rate":5.648170426065162e-05,"is_open":false,"open_timestamp":1516382100000.0,"close_timestamp":1516391700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":23.04678497349619,"open_date":"2018-01-19 17:20:00+00:00","close_date":"2018-01-19 20:15:00+00:00","open_rate":4.339e-05,"close_rate":4.360749373433584e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":175,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":3.9051e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.9051e-05,"stop_loss_ratio":0.1,"min_rate":4.339e-05,"max_rate":4.360749373433584e-05,"is_open":false,"open_timestamp":1516382400000.0,"close_timestamp":1516392900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":9.910802775024775,"open_date":"2018-01-20 04:45:00+00:00","close_date":"2018-01-20 17:35:00+00:00","open_rate":0.0001009,"close_rate":0.00010140576441102755,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":770,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":9.081e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":9.081e-05,"stop_loss_ratio":0.1,"min_rate":0.0001009,"max_rate":0.00010140576441102755,"is_open":false,"open_timestamp":1516423500000.0,"close_timestamp":1516469700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.3696789338459548,"open_date":"2018-01-20 04:50:00+00:00","close_date":"2018-01-20 15:15:00+00:00","open_rate":0.00270505,"close_rate":0.002718609147869674,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":625,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002434545,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002434545,"stop_loss_ratio":0.1,"min_rate":0.00270505,"max_rate":0.002718609147869674,"is_open":false,"open_timestamp":1516423800000.0,"close_timestamp":1516461300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.033333311111125925,"open_date":"2018-01-20 04:50:00+00:00","close_date":"2018-01-20 07:00:00+00:00","open_rate":0.03000002,"close_rate":0.030150396040100245,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":130,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.027000018,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.027000018,"stop_loss_ratio":0.1,"min_rate":0.03000002,"max_rate":0.030150396040100245,"is_open":false,"open_timestamp":1516423800000.0,"close_timestamp":1516431600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":18.315018315018317,"open_date":"2018-01-20 09:00:00+00:00","close_date":"2018-01-20 09:40:00+00:00","open_rate":5.46e-05,"close_rate":5.4873684210526304e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.914e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.914e-05,"stop_loss_ratio":0.1,"min_rate":5.46e-05,"max_rate":5.4873684210526304e-05,"is_open":false,"open_timestamp":1516438800000.0,"close_timestamp":1516441200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03244412634781012,"open_date":"2018-01-20 18:25:00+00:00","close_date":"2018-01-25 03:50:00+00:00","open_rate":0.03082222,"close_rate":0.027739998,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":6325,"profit_ratio":-0.10448878,"profit_abs":-0.00010000000000000015,"exit_reason":"stop_loss","initial_stop_loss_abs":0.027739998,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.027739998,"stop_loss_ratio":0.1,"min_rate":0.027739998,"max_rate":0.03082222,"is_open":false,"open_timestamp":1516472700000.0,"close_timestamp":1516852200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011148273260677063,"open_date":"2018-01-20 22:25:00+00:00","close_date":"2018-01-20 23:15:00+00:00","open_rate":0.08969999,"close_rate":0.09014961401002504,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.080729991,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.080729991,"stop_loss_ratio":0.1,"min_rate":0.08969999,"max_rate":0.09014961401002504,"is_open":false,"open_timestamp":1516487100000.0,"close_timestamp":1516490100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.06125570520324337,"open_date":"2018-01-21 02:50:00+00:00","close_date":"2018-01-21 14:30:00+00:00","open_rate":0.01632501,"close_rate":0.01640683962406015,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":700,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.014692509,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.014692509,"stop_loss_ratio":0.1,"min_rate":0.01632501,"max_rate":0.01640683962406015,"is_open":false,"open_timestamp":1516503000000.0,"close_timestamp":1516545000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.01417675579120474,"open_date":"2018-01-21 10:20:00+00:00","close_date":"2018-01-21 11:00:00+00:00","open_rate":0.070538,"close_rate":0.07089157393483708,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0634842,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0634842,"stop_loss_ratio":0.1,"min_rate":0.070538,"max_rate":0.07089157393483708,"is_open":false,"open_timestamp":1516530000000.0,"close_timestamp":1516532400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":18.864365214110546,"open_date":"2018-01-21 15:50:00+00:00","close_date":"2018-01-21 18:45:00+00:00","open_rate":5.301e-05,"close_rate":5.327571428571427e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":175,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":4.7709e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7709e-05,"stop_loss_ratio":0.1,"min_rate":5.301e-05,"max_rate":5.327571428571427e-05,"is_open":false,"open_timestamp":1516549800000.0,"close_timestamp":1516560300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":25.284450063211125,"open_date":"2018-01-21 16:20:00+00:00","close_date":"2018-01-21 17:00:00+00:00","open_rate":3.955e-05,"close_rate":3.9748245614035085e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":3.5595e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.5595e-05,"stop_loss_ratio":0.1,"min_rate":3.955e-05,"max_rate":3.9748245614035085e-05,"is_open":false,"open_timestamp":1516551600000.0,"close_timestamp":1516554000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.38683971296493297,"open_date":"2018-01-21 21:15:00+00:00","close_date":"2018-01-21 21:45:00+00:00","open_rate":0.00258505,"close_rate":0.002623922932330827,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002326545,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002326545,"stop_loss_ratio":0.1,"min_rate":0.00258505,"max_rate":0.002623922932330827,"is_open":false,"open_timestamp":1516569300000.0,"close_timestamp":1516571100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":25.621316935690498,"open_date":"2018-01-21 21:15:00+00:00","close_date":"2018-01-21 21:55:00+00:00","open_rate":3.903e-05,"close_rate":3.922563909774435e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":3.5127e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.5127e-05,"stop_loss_ratio":0.1,"min_rate":3.903e-05,"max_rate":3.922563909774435e-05,"is_open":false,"open_timestamp":1516569300000.0,"close_timestamp":1516571700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.098548510313215,"open_date":"2018-01-22 00:35:00+00:00","close_date":"2018-01-22 10:35:00+00:00","open_rate":5.236e-05,"close_rate":5.262245614035087e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":600,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":4.7124e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7124e-05,"stop_loss_ratio":0.1,"min_rate":5.236e-05,"max_rate":5.262245614035087e-05,"is_open":false,"open_timestamp":1516581300000.0,"close_timestamp":1516617300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":11.076650420912715,"open_date":"2018-01-22 01:30:00+00:00","close_date":"2018-01-22 02:10:00+00:00","open_rate":9.028e-05,"close_rate":9.07325313283208e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":8.1252e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.1252e-05,"stop_loss_ratio":0.1,"min_rate":9.028e-05,"max_rate":9.07325313283208e-05,"is_open":false,"open_timestamp":1516584600000.0,"close_timestamp":1516587000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.3721622627465575,"open_date":"2018-01-22 12:25:00+00:00","close_date":"2018-01-22 14:35:00+00:00","open_rate":0.002687,"close_rate":0.002700468671679198,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":130,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0024183000000000004,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0024183000000000004,"stop_loss_ratio":0.1,"min_rate":0.002687,"max_rate":0.002700468671679198,"is_open":false,"open_timestamp":1516623900000.0,"close_timestamp":1516631700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":23.99232245681382,"open_date":"2018-01-22 13:15:00+00:00","close_date":"2018-01-22 13:55:00+00:00","open_rate":4.168e-05,"close_rate":4.188892230576441e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":3.7512e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.7512e-05,"stop_loss_ratio":0.1,"min_rate":4.168e-05,"max_rate":4.188892230576441e-05,"is_open":false,"open_timestamp":1516626900000.0,"close_timestamp":1516629300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":11.336583153837434,"open_date":"2018-01-22 14:00:00+00:00","close_date":"2018-01-22 14:30:00+00:00","open_rate":8.821e-05,"close_rate":8.953646616541353e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":7.9389e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":7.9389e-05,"stop_loss_ratio":0.1,"min_rate":8.821e-05,"max_rate":8.953646616541353e-05,"is_open":false,"open_timestamp":1516629600000.0,"close_timestamp":1516631400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.334880123743233,"open_date":"2018-01-22 15:55:00+00:00","close_date":"2018-01-22 16:40:00+00:00","open_rate":5.172e-05,"close_rate":5.1979248120300745e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.6548e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6548e-05,"stop_loss_ratio":0.1,"min_rate":5.172e-05,"max_rate":5.1979248120300745e-05,"is_open":false,"open_timestamp":1516636500000.0,"close_timestamp":1516639200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":33.04692663582287,"open_date":"2018-01-22 16:05:00+00:00","close_date":"2018-01-22 16:25:00+00:00","open_rate":3.026e-05,"close_rate":3.101839598997494e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641604113e-05,"exit_reason":"roi","initial_stop_loss_abs":2.7234e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.7234e-05,"stop_loss_ratio":0.1,"min_rate":3.026e-05,"max_rate":3.101839598997494e-05,"is_open":false,"open_timestamp":1516637100000.0,"close_timestamp":1516638300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014156285390713478,"open_date":"2018-01-22 19:50:00+00:00","close_date":"2018-01-23 00:10:00+00:00","open_rate":0.07064,"close_rate":0.07099408521303258,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":260,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.063576,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.063576,"stop_loss_ratio":0.1,"min_rate":0.07064,"max_rate":0.07099408521303258,"is_open":false,"open_timestamp":1516650600000.0,"close_timestamp":1516666200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.06080938507725528,"open_date":"2018-01-22 21:25:00+00:00","close_date":"2018-01-22 22:05:00+00:00","open_rate":0.01644483,"close_rate":0.01652726022556391,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.014800347,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.014800347,"stop_loss_ratio":0.1,"min_rate":0.01644483,"max_rate":0.01652726022556391,"is_open":false,"open_timestamp":1516656300000.0,"close_timestamp":1516658700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":23.08935580697299,"open_date":"2018-01-23 00:05:00+00:00","close_date":"2018-01-23 00:35:00+00:00","open_rate":4.331e-05,"close_rate":4.3961278195488714e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":3.8979e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.8979e-05,"stop_loss_ratio":0.1,"min_rate":4.331e-05,"max_rate":4.3961278195488714e-05,"is_open":false,"open_timestamp":1516665900000.0,"close_timestamp":1516667700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":31.250000000000004,"open_date":"2018-01-23 01:50:00+00:00","close_date":"2018-01-23 02:15:00+00:00","open_rate":3.2e-05,"close_rate":3.2802005012531326e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":2.5062656641604113e-05,"exit_reason":"roi","initial_stop_loss_abs":2.88e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.88e-05,"stop_loss_ratio":0.1,"min_rate":3.2e-05,"max_rate":3.2802005012531326e-05,"is_open":false,"open_timestamp":1516672200000.0,"close_timestamp":1516673700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010907854156754156,"open_date":"2018-01-23 04:25:00+00:00","close_date":"2018-01-23 05:15:00+00:00","open_rate":0.09167706,"close_rate":0.09213659413533835,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.08250935400000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.08250935400000001,"stop_loss_ratio":0.1,"min_rate":0.09167706,"max_rate":0.09213659413533835,"is_open":false,"open_timestamp":1516681500000.0,"close_timestamp":1516684500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014440474918339117,"open_date":"2018-01-23 07:35:00+00:00","close_date":"2018-01-23 09:00:00+00:00","open_rate":0.0692498,"close_rate":0.06959691679197995,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":85,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.06232482,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.06232482,"stop_loss_ratio":0.1,"min_rate":0.0692498,"max_rate":0.06959691679197995,"is_open":false,"open_timestamp":1516692900000.0,"close_timestamp":1516698000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":31.426775612822127,"open_date":"2018-01-23 10:50:00+00:00","close_date":"2018-01-23 13:05:00+00:00","open_rate":3.182e-05,"close_rate":3.197949874686716e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":135,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":2.8638e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.8638e-05,"stop_loss_ratio":0.1,"min_rate":3.182e-05,"max_rate":3.197949874686716e-05,"is_open":false,"open_timestamp":1516704600000.0,"close_timestamp":1516712700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.024461839530332683,"open_date":"2018-01-23 11:05:00+00:00","close_date":"2018-01-23 16:05:00+00:00","open_rate":0.04088,"close_rate":0.04108491228070175,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":300,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.036792,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.036792,"stop_loss_ratio":0.1,"min_rate":0.04088,"max_rate":0.04108491228070175,"is_open":false,"open_timestamp":1516705500000.0,"close_timestamp":1516723500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.417475728155345,"open_date":"2018-01-23 14:55:00+00:00","close_date":"2018-01-23 15:35:00+00:00","open_rate":5.15e-05,"close_rate":5.175814536340851e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.635e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.635e-05,"stop_loss_ratio":0.1,"min_rate":5.15e-05,"max_rate":5.175814536340851e-05,"is_open":false,"open_timestamp":1516719300000.0,"close_timestamp":1516721700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.011023294646713328,"open_date":"2018-01-23 16:35:00+00:00","close_date":"2018-01-24 00:05:00+00:00","open_rate":0.09071698,"close_rate":0.09117170170426064,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":450,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.081645282,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.081645282,"stop_loss_ratio":0.1,"min_rate":0.09071698,"max_rate":0.09117170170426064,"is_open":false,"open_timestamp":1516725300000.0,"close_timestamp":1516752300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":31.969309462915604,"open_date":"2018-01-23 17:25:00+00:00","close_date":"2018-01-23 18:45:00+00:00","open_rate":3.128e-05,"close_rate":3.1436791979949865e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":2.8152e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.8152e-05,"stop_loss_ratio":0.1,"min_rate":3.128e-05,"max_rate":3.1436791979949865e-05,"is_open":false,"open_timestamp":1516728300000.0,"close_timestamp":1516733100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":10.465724751439035,"open_date":"2018-01-23 20:15:00+00:00","close_date":"2018-01-23 22:00:00+00:00","open_rate":9.555e-05,"close_rate":9.602894736842104e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":105,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":8.5995e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.5995e-05,"stop_loss_ratio":0.1,"min_rate":9.555e-05,"max_rate":9.602894736842104e-05,"is_open":false,"open_timestamp":1516738500000.0,"close_timestamp":1516744800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02450979791426522,"open_date":"2018-01-23 22:30:00+00:00","close_date":"2018-01-23 23:10:00+00:00","open_rate":0.04080001,"close_rate":0.0410045213283208,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.036720009,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.036720009,"stop_loss_ratio":0.1,"min_rate":0.04080001,"max_rate":0.0410045213283208,"is_open":false,"open_timestamp":1516746600000.0,"close_timestamp":1516749000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.36858415649816,"open_date":"2018-01-23 23:50:00+00:00","close_date":"2018-01-24 03:35:00+00:00","open_rate":5.163e-05,"close_rate":5.18887969924812e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":225,"profit_ratio":-0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":4.6467e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6467e-05,"stop_loss_ratio":0.1,"min_rate":5.163e-05,"max_rate":5.18887969924812e-05,"is_open":false,"open_timestamp":1516751400000.0,"close_timestamp":1516764900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.024747691102289384,"open_date":"2018-01-24 00:20:00+00:00","close_date":"2018-01-24 01:50:00+00:00","open_rate":0.04040781,"close_rate":0.04061035541353383,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":90,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.036367029,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.036367029,"stop_loss_ratio":0.1,"min_rate":0.04040781,"max_rate":0.04061035541353383,"is_open":false,"open_timestamp":1516753200000.0,"close_timestamp":1516758600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.485580670303975,"open_date":"2018-01-24 06:45:00+00:00","close_date":"2018-01-24 07:25:00+00:00","open_rate":5.132e-05,"close_rate":5.157724310776942e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.6188000000000006e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6188000000000006e-05,"stop_loss_ratio":0.1,"min_rate":5.132e-05,"max_rate":5.157724310776942e-05,"is_open":false,"open_timestamp":1516776300000.0,"close_timestamp":1516778700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":19.23816852635629,"open_date":"2018-01-24 14:15:00+00:00","close_date":"2018-01-24 14:25:00+00:00","open_rate":5.198e-05,"close_rate":5.432496240601503e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":10,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":4.6782e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6782e-05,"stop_loss_ratio":0.1,"min_rate":5.198e-05,"max_rate":5.432496240601503e-05,"is_open":false,"open_timestamp":1516803300000.0,"close_timestamp":1516803900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":32.74394237066143,"open_date":"2018-01-24 14:50:00+00:00","close_date":"2018-01-24 16:35:00+00:00","open_rate":3.054e-05,"close_rate":3.069308270676692e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":105,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":2.7486000000000004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.7486000000000004e-05,"stop_loss_ratio":0.1,"min_rate":3.054e-05,"max_rate":3.069308270676692e-05,"is_open":false,"open_timestamp":1516805400000.0,"close_timestamp":1516811700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":10.795638562020944,"open_date":"2018-01-24 15:10:00+00:00","close_date":"2018-01-24 16:15:00+00:00","open_rate":9.263e-05,"close_rate":9.309431077694236e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":65,"profit_ratio":0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":8.3367e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.3367e-05,"stop_loss_ratio":0.1,"min_rate":9.263e-05,"max_rate":9.309431077694236e-05,"is_open":false,"open_timestamp":1516806600000.0,"close_timestamp":1516810500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":18.13565469713457,"open_date":"2018-01-24 22:40:00+00:00","close_date":"2018-01-24 23:25:00+00:00","open_rate":5.514e-05,"close_rate":5.54163909774436e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.962599999999999e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.962599999999999e-05,"stop_loss_ratio":0.1,"min_rate":5.514e-05,"max_rate":5.54163909774436e-05,"is_open":false,"open_timestamp":1516833600000.0,"close_timestamp":1516836300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":20.3210729526519,"open_date":"2018-01-25 00:50:00+00:00","close_date":"2018-01-25 01:30:00+00:00","open_rate":4.921e-05,"close_rate":4.9456666666666664e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.4289e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.4289e-05,"stop_loss_ratio":0.1,"min_rate":4.921e-05,"max_rate":4.9456666666666664e-05,"is_open":false,"open_timestamp":1516841400000.0,"close_timestamp":1516843800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.38461538461538464,"open_date":"2018-01-25 08:15:00+00:00","close_date":"2018-01-25 12:15:00+00:00","open_rate":0.0026,"close_rate":0.002613032581453634,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":240,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.00234,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.00234,"stop_loss_ratio":0.1,"min_rate":0.0026,"max_rate":0.002613032581453634,"is_open":false,"open_timestamp":1516868100000.0,"close_timestamp":1516882500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03571593119825878,"open_date":"2018-01-25 10:25:00+00:00","close_date":"2018-01-25 16:15:00+00:00","open_rate":0.02799871,"close_rate":0.028139054411027563,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":350,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.025198839,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025198839,"stop_loss_ratio":0.1,"min_rate":0.02799871,"max_rate":0.028139054411027563,"is_open":false,"open_timestamp":1516875900000.0,"close_timestamp":1516896900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.024516401717913305,"open_date":"2018-01-25 11:00:00+00:00","close_date":"2018-01-25 11:45:00+00:00","open_rate":0.04078902,"close_rate":0.0409934762406015,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.036710118,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.036710118,"stop_loss_ratio":0.1,"min_rate":0.04078902,"max_rate":0.0409934762406015,"is_open":false,"open_timestamp":1516878000000.0,"close_timestamp":1516880700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"amount":34.602076124567475,"open_date":"2018-01-25 13:05:00+00:00","close_date":"2018-01-25 13:45:00+00:00","open_rate":2.89e-05,"close_rate":2.904486215538847e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":2.601e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.601e-05,"stop_loss_ratio":0.1,"min_rate":2.89e-05,"max_rate":2.904486215538847e-05,"is_open":false,"open_timestamp":1516885500000.0,"close_timestamp":1516887900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02432912439481303,"open_date":"2018-01-25 13:20:00+00:00","close_date":"2018-01-25 14:05:00+00:00","open_rate":0.041103,"close_rate":0.04130903007518797,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0369927,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0369927,"stop_loss_ratio":0.1,"min_rate":0.041103,"max_rate":0.04130903007518797,"is_open":false,"open_timestamp":1516886400000.0,"close_timestamp":1516889100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":18.42299189388357,"open_date":"2018-01-25 15:45:00+00:00","close_date":"2018-01-25 16:15:00+00:00","open_rate":5.428e-05,"close_rate":5.509624060150376e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":4.8852000000000006e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.8852000000000006e-05,"stop_loss_ratio":0.1,"min_rate":5.428e-05,"max_rate":5.509624060150376e-05,"is_open":false,"open_timestamp":1516895100000.0,"close_timestamp":1516896900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":18.47063169560399,"open_date":"2018-01-25 17:45:00+00:00","close_date":"2018-01-25 23:15:00+00:00","open_rate":5.414e-05,"close_rate":5.441137844611528e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":330,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.8726e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.8726e-05,"stop_loss_ratio":0.1,"min_rate":5.414e-05,"max_rate":5.441137844611528e-05,"is_open":false,"open_timestamp":1516902300000.0,"close_timestamp":1516922100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02415005686130888,"open_date":"2018-01-25 21:15:00+00:00","close_date":"2018-01-25 21:55:00+00:00","open_rate":0.04140777,"close_rate":0.0416153277443609,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.037266993000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.037266993000000005,"stop_loss_ratio":0.1,"min_rate":0.04140777,"max_rate":0.0416153277443609,"is_open":false,"open_timestamp":1516914900000.0,"close_timestamp":1516917300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.3932224183965176,"open_date":"2018-01-26 02:05:00+00:00","close_date":"2018-01-26 02:45:00+00:00","open_rate":0.00254309,"close_rate":0.002555837318295739,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002288781,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002288781,"stop_loss_ratio":0.1,"min_rate":0.00254309,"max_rate":0.002555837318295739,"is_open":false,"open_timestamp":1516932300000.0,"close_timestamp":1516934700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":17.834849295523455,"open_date":"2018-01-26 02:55:00+00:00","close_date":"2018-01-26 15:10:00+00:00","open_rate":5.607e-05,"close_rate":5.6351052631578935e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":735,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":5.0463e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.0463e-05,"stop_loss_ratio":0.1,"min_rate":5.607e-05,"max_rate":5.6351052631578935e-05,"is_open":false,"open_timestamp":1516935300000.0,"close_timestamp":1516979400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.39400171784748983,"open_date":"2018-01-26 06:10:00+00:00","close_date":"2018-01-26 09:25:00+00:00","open_rate":0.00253806,"close_rate":0.0025507821052631577,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":195,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002284254,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002284254,"stop_loss_ratio":0.1,"min_rate":0.00253806,"max_rate":0.0025507821052631577,"is_open":false,"open_timestamp":1516947000000.0,"close_timestamp":1516958700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.024096385542168672,"open_date":"2018-01-26 07:25:00+00:00","close_date":"2018-01-26 09:55:00+00:00","open_rate":0.0415,"close_rate":0.04170802005012531,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":150,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.03735,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.03735,"stop_loss_ratio":0.1,"min_rate":0.0415,"max_rate":0.04170802005012531,"is_open":false,"open_timestamp":1516951500000.0,"close_timestamp":1516960500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":18.793459875963165,"open_date":"2018-01-26 09:55:00+00:00","close_date":"2018-01-26 10:25:00+00:00","open_rate":5.321e-05,"close_rate":5.401015037593984e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":4.7889e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7889e-05,"stop_loss_ratio":0.1,"min_rate":5.321e-05,"max_rate":5.401015037593984e-05,"is_open":false,"open_timestamp":1516960500000.0,"close_timestamp":1516962300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.036074437437185386,"open_date":"2018-01-26 16:05:00+00:00","close_date":"2018-01-26 16:45:00+00:00","open_rate":0.02772046,"close_rate":0.02785940967418546,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.024948414,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.024948414,"stop_loss_ratio":0.1,"min_rate":0.02772046,"max_rate":0.02785940967418546,"is_open":false,"open_timestamp":1516982700000.0,"close_timestamp":1516985100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010569326272036914,"open_date":"2018-01-26 23:35:00+00:00","close_date":"2018-01-27 00:15:00+00:00","open_rate":0.09461341,"close_rate":0.09508766268170424,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.085152069,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.085152069,"stop_loss_ratio":0.1,"min_rate":0.09461341,"max_rate":0.09508766268170424,"is_open":false,"open_timestamp":1517009700000.0,"close_timestamp":1517012100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":17.809439002671414,"open_date":"2018-01-27 00:35:00+00:00","close_date":"2018-01-27 01:30:00+00:00","open_rate":5.615e-05,"close_rate":5.643145363408521e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":5.0535e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.0535e-05,"stop_loss_ratio":0.1,"min_rate":5.615e-05,"max_rate":5.643145363408521e-05,"is_open":false,"open_timestamp":1517013300000.0,"close_timestamp":1517016600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"amount":17.998560115190784,"open_date":"2018-01-27 00:45:00+00:00","close_date":"2018-01-30 04:45:00+00:00","open_rate":5.556e-05,"close_rate":5.144e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":4560,"profit_ratio":-0.07877175,"profit_abs":-7.415406767458598e-05,"exit_reason":"force_exit","initial_stop_loss_abs":5.0004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.0004e-05,"stop_loss_ratio":0.1,"min_rate":5.144e-05,"max_rate":5.556e-05,"is_open":false,"open_timestamp":1517013900000.0,"close_timestamp":1517287500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014492751522789634,"open_date":"2018-01-27 02:30:00+00:00","close_date":"2018-01-27 11:25:00+00:00","open_rate":0.06900001,"close_rate":0.06934587471177944,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":535,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.062100009000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.062100009000000005,"stop_loss_ratio":0.1,"min_rate":0.06900001,"max_rate":0.06934587471177944,"is_open":false,"open_timestamp":1517020200000.0,"close_timestamp":1517052300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010582027378879436,"open_date":"2018-01-27 06:25:00+00:00","close_date":"2018-01-27 07:05:00+00:00","open_rate":0.09449985,"close_rate":0.0949735334586466,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.085049865,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.085049865,"stop_loss_ratio":0.1,"min_rate":0.09449985,"max_rate":0.0949735334586466,"is_open":false,"open_timestamp":1517034300000.0,"close_timestamp":1517036700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"amount":0.02434885085598385,"open_date":"2018-01-27 09:40:00+00:00","close_date":"2018-01-30 04:40:00+00:00","open_rate":0.0410697,"close_rate":0.03928809,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":4020,"profit_ratio":-0.04815133,"profit_abs":-4.338015617352949e-05,"exit_reason":"force_exit","initial_stop_loss_abs":0.03696273,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.03696273,"stop_loss_ratio":0.1,"min_rate":0.03928809,"max_rate":0.0410697,"is_open":false,"open_timestamp":1517046000000.0,"close_timestamp":1517287200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.03508771929824561,"open_date":"2018-01-27 11:45:00+00:00","close_date":"2018-01-27 12:30:00+00:00","open_rate":0.0285,"close_rate":0.02864285714285714,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.025650000000000003,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025650000000000003,"stop_loss_ratio":0.1,"min_rate":0.0285,"max_rate":0.02864285714285714,"is_open":false,"open_timestamp":1517053500000.0,"close_timestamp":1517056200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"amount":0.034887307020861215,"open_date":"2018-01-27 12:35:00+00:00","close_date":"2018-01-27 15:25:00+00:00","open_rate":0.02866372,"close_rate":0.02880739779448621,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":170,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.025797348,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025797348,"stop_loss_ratio":0.1,"min_rate":0.02866372,"max_rate":0.02880739779448621,"is_open":false,"open_timestamp":1517056500000.0,"close_timestamp":1517066700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"amount":0.010484268355332824,"open_date":"2018-01-27 15:50:00+00:00","close_date":"2018-01-27 16:50:00+00:00","open_rate":0.095381,"close_rate":0.09585910025062656,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0858429,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0858429,"stop_loss_ratio":0.1,"min_rate":0.095381,"max_rate":0.09585910025062656,"is_open":false,"open_timestamp":1517068200000.0,"close_timestamp":1517071800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014794886650455415,"open_date":"2018-01-27 17:05:00+00:00","close_date":"2018-01-27 17:45:00+00:00","open_rate":0.06759092,"close_rate":0.06792972160401002,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.060831828,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.060831828,"stop_loss_ratio":0.1,"min_rate":0.06759092,"max_rate":0.06792972160401002,"is_open":false,"open_timestamp":1517072700000.0,"close_timestamp":1517075100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.38684569885609726,"open_date":"2018-01-27 23:40:00+00:00","close_date":"2018-01-28 01:05:00+00:00","open_rate":0.00258501,"close_rate":0.002597967443609022,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":85,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002326509,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002326509,"stop_loss_ratio":0.1,"min_rate":0.00258501,"max_rate":0.002597967443609022,"is_open":false,"open_timestamp":1517096400000.0,"close_timestamp":1517101500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014928710926711672,"open_date":"2018-01-28 02:25:00+00:00","close_date":"2018-01-28 08:10:00+00:00","open_rate":0.06698502,"close_rate":0.0673207845112782,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":345,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.060286518,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.060286518,"stop_loss_ratio":0.1,"min_rate":0.06698502,"max_rate":0.0673207845112782,"is_open":false,"open_timestamp":1517106300000.0,"close_timestamp":1517127000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014767187899175548,"open_date":"2018-01-28 10:25:00+00:00","close_date":"2018-01-28 16:30:00+00:00","open_rate":0.0677177,"close_rate":0.06805713709273183,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":365,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.06094593000000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.06094593000000001,"stop_loss_ratio":0.1,"min_rate":0.0677177,"max_rate":0.06805713709273183,"is_open":false,"open_timestamp":1517135100000.0,"close_timestamp":1517157000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"amount":19.175455417066157,"open_date":"2018-01-28 20:35:00+00:00","close_date":"2018-01-28 21:35:00+00:00","open_rate":5.215e-05,"close_rate":5.2411403508771925e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.6935e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6935e-05,"stop_loss_ratio":0.1,"min_rate":5.215e-05,"max_rate":5.2411403508771925e-05,"is_open":false,"open_timestamp":1517171700000.0,"close_timestamp":1517175300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.36521808998243305,"open_date":"2018-01-28 22:00:00+00:00","close_date":"2018-01-28 22:30:00+00:00","open_rate":0.00273809,"close_rate":0.002779264285714285,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002464281,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002464281,"stop_loss_ratio":0.1,"min_rate":0.00273809,"max_rate":0.002779264285714285,"is_open":false,"open_timestamp":1517176800000.0,"close_timestamp":1517178600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"amount":0.3641236272539253,"open_date":"2018-01-29 00:00:00+00:00","close_date":"2018-01-29 00:30:00+00:00","open_rate":0.00274632,"close_rate":0.002787618045112782,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002471688,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002471688,"stop_loss_ratio":0.1,"min_rate":0.00274632,"max_rate":0.002787618045112782,"is_open":false,"open_timestamp":1517184000000.0,"close_timestamp":1517185800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"amount":0.061634117689115045,"open_date":"2018-01-29 02:15:00+00:00","close_date":"2018-01-29 03:00:00+00:00","open_rate":0.01622478,"close_rate":0.016306107218045113,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.014602302,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.014602302,"stop_loss_ratio":0.1,"min_rate":0.01622478,"max_rate":0.016306107218045113,"is_open":false,"open_timestamp":1517192100000.0,"close_timestamp":1517194800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014492753623188404,"open_date":"2018-01-29 03:05:00+00:00","close_date":"2018-01-29 03:45:00+00:00","open_rate":0.069,"close_rate":0.06934586466165413,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.06210000000000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.06210000000000001,"stop_loss_ratio":0.1,"min_rate":0.069,"max_rate":0.06934586466165413,"is_open":false,"open_timestamp":1517195100000.0,"close_timestamp":1517197500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":11.42204454597373,"open_date":"2018-01-29 05:20:00+00:00","close_date":"2018-01-29 06:55:00+00:00","open_rate":8.755e-05,"close_rate":8.798884711779448e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":95,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":7.879500000000001e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":7.879500000000001e-05,"stop_loss_ratio":0.1,"min_rate":8.755e-05,"max_rate":8.798884711779448e-05,"is_open":false,"open_timestamp":1517203200000.0,"close_timestamp":1517208900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014650376815016871,"open_date":"2018-01-29 07:00:00+00:00","close_date":"2018-01-29 19:25:00+00:00","open_rate":0.06825763,"close_rate":0.06859977350877192,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":745,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.061431867,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.061431867,"stop_loss_ratio":0.1,"min_rate":0.06825763,"max_rate":0.06859977350877192,"is_open":false,"open_timestamp":1517209200000.0,"close_timestamp":1517253900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"amount":0.014894490408841846,"open_date":"2018-01-29 19:45:00+00:00","close_date":"2018-01-29 20:25:00+00:00","open_rate":0.06713892,"close_rate":0.06747545593984962,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.060425028000000006,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.060425028000000006,"stop_loss_ratio":0.1,"min_rate":0.06713892,"max_rate":0.06747545593984962,"is_open":false,"open_timestamp":1517255100000.0,"close_timestamp":1517257500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"amount":11.193194537721066,"open_date":"2018-01-29 23:30:00+00:00","close_date":"2018-01-30 04:45:00+00:00","open_rate":8.934e-05,"close_rate":8.8e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":315,"profit_ratio":-0.0199116,"profit_abs":-1.4998880680546292e-05,"exit_reason":"force_exit","initial_stop_loss_abs":8.0406e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.0406e-05,"stop_loss_ratio":0.1,"min_rate":8.8e-05,"max_rate":8.934e-05,"is_open":false,"open_timestamp":1517268600000.0,"close_timestamp":1517287500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null}],"locks":[],"best_pair":{"key":"LTC/BTC","trades":8,"profit_mean":0.00748129625,"profit_mean_pct":0.748129625,"profit_sum":0.05985037,"profit_sum_pct":5.99,"profit_total_abs":0.00010025062656641558,"profit_total":0.010025062656641558,"profit_total_pct":1.0,"duration_avg":"1:59:00","wins":8,"draws":0,"losses":0},"worst_pair":{"key":"XMR/BTC","trades":16,"profit_mean":-0.0027899012500000007,"profit_mean_pct":-0.2789901250000001,"profit_sum":-0.04463842000000001,"profit_sum_pct":-4.46,"profit_total_abs":3.533834586465928e-05,"profit_total":0.003533834586465928,"profit_total_pct":0.35,"duration_avg":"8:41:00","wins":15,"draws":0,"losses":1},"results_per_pair":[{"key":"XLM/BTC","trades":21,"profit_mean":0.0026243899999999994,"profit_mean_pct":0.2624389999999999,"profit_sum":0.05511218999999999,"profit_sum_pct":5.51,"profit_total_abs":0.00016065162907268006,"profit_total":0.016065162907268005,"profit_total_pct":1.61,"duration_avg":"3:21:00","wins":20,"draws":0,"losses":1},{"key":"ETC/BTC","trades":20,"profit_mean":0.0022568569999999997,"profit_mean_pct":0.22568569999999996,"profit_sum":0.04513713999999999,"profit_sum_pct":4.51,"profit_total_abs":0.00014561403508771753,"profit_total":0.014561403508771753,"profit_total_pct":1.46,"duration_avg":"1:45:00","wins":19,"draws":0,"losses":1},{"key":"ETH/BTC","trades":21,"profit_mean":0.0009500057142857142,"profit_mean_pct":0.09500057142857142,"profit_sum":0.01995012,"profit_sum_pct":2.0,"profit_total_abs":0.00012531328320801774,"profit_total":0.012531328320801774,"profit_total_pct":1.25,"duration_avg":"2:17:00","wins":21,"draws":0,"losses":0},{"key":"ADA/BTC","trades":29,"profit_mean":-0.0011598141379310352,"profit_mean_pct":-0.11598141379310352,"profit_sum":-0.03363461000000002,"profit_sum_pct":-3.36,"profit_total_abs":0.00011156021803969656,"profit_total":0.011156021803969657,"profit_total_pct":1.12,"duration_avg":"5:35:00","wins":27,"draws":0,"losses":2},{"key":"TRX/BTC","trades":15,"profit_mean":0.0023467073333333323,"profit_mean_pct":0.23467073333333321,"profit_sum":0.035200609999999986,"profit_sum_pct":3.52,"profit_total_abs":0.00011056502909388873,"profit_total":0.011056502909388873,"profit_total_pct":1.11,"duration_avg":"2:28:00","wins":13,"draws":0,"losses":2},{"key":"DASH/BTC","trades":16,"profit_mean":0.0018703237499999997,"profit_mean_pct":0.18703237499999997,"profit_sum":0.029925179999999996,"profit_sum_pct":2.99,"profit_total_abs":0.0001102756892230564,"profit_total":0.01102756892230564,"profit_total_pct":1.1,"duration_avg":"3:03:00","wins":16,"draws":0,"losses":0},{"key":"LTC/BTC","trades":8,"profit_mean":0.00748129625,"profit_mean_pct":0.748129625,"profit_sum":0.05985037,"profit_sum_pct":5.99,"profit_total_abs":0.00010025062656641558,"profit_total":0.010025062656641558,"profit_total_pct":1.0,"duration_avg":"1:59:00","wins":8,"draws":0,"losses":0},{"key":"ZEC/BTC","trades":21,"profit_mean":-0.00039290904761904774,"profit_mean_pct":-0.03929090476190478,"profit_sum":-0.008251090000000003,"profit_sum_pct":-0.83,"profit_total_abs":9.697072101945111e-05,"profit_total":0.009697072101945111,"profit_total_pct":0.97,"duration_avg":"4:17:00","wins":20,"draws":0,"losses":1},{"key":"NXT/BTC","trades":12,"profit_mean":-0.0012261025000000006,"profit_mean_pct":-0.12261025000000006,"profit_sum":-0.014713230000000008,"profit_sum_pct":-1.47,"profit_total_abs":4.536340852130151e-05,"profit_total":0.004536340852130151,"profit_total_pct":0.45,"duration_avg":"0:57:00","wins":11,"draws":0,"losses":1},{"key":"XMR/BTC","trades":16,"profit_mean":-0.0027899012500000007,"profit_mean_pct":-0.2789901250000001,"profit_sum":-0.04463842000000001,"profit_sum_pct":-4.46,"profit_total_abs":3.533834586465928e-05,"profit_total":0.003533834586465928,"profit_total_pct":0.35,"duration_avg":"8:41:00","wins":15,"draws":0,"losses":1},{"key":"TOTAL","trades":179,"profit_mean":0.0008041243575418989,"profit_mean_pct":0.0804124357541899,"profit_sum":0.1439382599999999,"profit_sum_pct":14.39,"profit_total_abs":0.0010419029856968845,"profit_total":0.10419029856968845,"profit_total_pct":10.42,"duration_avg":"3:40:00","wins":170,"draws":0,"losses":9}],"results_per_enter_tag":[{"key":"buy_tag","trades":1,"profit_mean":0.03990025,"profit_mean_pct":3.9900249999999997,"profit_sum":0.03990025,"profit_sum_pct":3.99,"profit_total_abs":4.5112781954887056e-05,"profit_total":0.004511278195488706,"profit_total_pct":0.45,"duration_avg":"0:15:00","wins":1,"draws":0,"losses":0},{"key":"TOTAL","trades":179,"profit_mean":0.0008041243575418989,"profit_mean_pct":0.0804124357541899,"profit_sum":0.1439382599999999,"profit_sum_pct":14.39,"profit_total_abs":0.0010419029856968845,"profit_total":0.10419029856968845,"profit_total_pct":10.42,"duration_avg":"3:40:00","wins":170,"draws":0,"losses":9}],"exit_reason_summary":[{"exit_reason":"roi","trades":170,"wins":170,"draws":0,"losses":0,"profit_mean":0.005398268352941177,"profit_mean_pct":0.54,"profit_sum":0.91770562,"profit_sum_pct":91.77,"profit_total_abs":0.0017744360902255465,"profit_total":0.30590187333333335,"profit_total_pct":30.59},{"exit_reason":"stop_loss","trades":6,"wins":0,"draws":0,"losses":6,"profit_mean":-0.10448878000000002,"profit_mean_pct":-10.45,"profit_sum":-0.6269326800000001,"profit_sum_pct":-62.69,"profit_total_abs":-0.0006000000000000003,"profit_total":-0.20897756000000003,"profit_total_pct":-20.9},{"exit_reason":"force_exit","trades":3,"wins":0,"draws":0,"losses":3,"profit_mean":-0.04894489333333333,"profit_mean_pct":-4.89,"profit_sum":-0.14683468,"profit_sum_pct":-14.68,"profit_total_abs":-0.00013253310452866177,"profit_total":-0.04894489333333333,"profit_total_pct":-4.89}],"left_open_trades":[{"key":"TRX/BTC","trades":1,"profit_mean":-0.0199116,"profit_mean_pct":-1.9911600000000003,"profit_sum":-0.0199116,"profit_sum_pct":-1.99,"profit_total_abs":-1.4998880680546292e-05,"profit_total":-0.0014998880680546292,"profit_total_pct":-0.15,"duration_avg":"5:15:00","wins":0,"draws":0,"losses":1},{"key":"ZEC/BTC","trades":1,"profit_mean":-0.04815133,"profit_mean_pct":-4.815133,"profit_sum":-0.04815133,"profit_sum_pct":-4.82,"profit_total_abs":-4.338015617352949e-05,"profit_total":-0.004338015617352949,"profit_total_pct":-0.43,"duration_avg":"2 days, 19:00:00","wins":0,"draws":0,"losses":1},{"key":"ADA/BTC","trades":1,"profit_mean":-0.07877175,"profit_mean_pct":-7.877175,"profit_sum":-0.07877175,"profit_sum_pct":-7.88,"profit_total_abs":-7.415406767458598e-05,"profit_total":-0.007415406767458598,"profit_total_pct":-0.74,"duration_avg":"3 days, 4:00:00","wins":0,"draws":0,"losses":1},{"key":"TOTAL","trades":3,"profit_mean":-0.04894489333333333,"profit_mean_pct":-4.894489333333333,"profit_sum":-0.14683468,"profit_sum_pct":-14.68,"profit_total_abs":-0.00013253310452866177,"profit_total":-0.013253310452866176,"profit_total_pct":-1.33,"duration_avg":"2 days, 1:25:00","wins":0,"draws":0,"losses":3}],"total_trades":179,"trade_count_long":179,"trade_count_short":0,"total_volume":0.17900000000000005,"avg_stake_amount":0.0010000000000000002,"profit_mean":0.0008041243575418989,"profit_median":0.0,"profit_total":0.10419029856968845,"profit_total_long":0.10419029856968845,"profit_total_short":0.0,"profit_total_abs":0.0010419029856968845,"profit_total_long_abs":0.0010419029856968845,"profit_total_short_abs":0.0,"cagr":5.712688499973264,"profit_factor":2.4223288739520954,"backtest_start":"2018-01-10 07:15:00","backtest_start_ts":1515568500000,"backtest_end":"2018-01-30 04:45:00","backtest_end_ts":1517287500000,"backtest_days":19,"backtest_run_start_ts":"2020-10-01 18:00:00+00:00","backtest_run_end_ts":"2020-10-01 18:01:00+00:00","trades_per_day":9.42,"market_change":1.22,"pairlist":["TRX/BTC","ADA/BTC","XLM/BTC","ETH/BTC","XMR/BTC","ZEC/BTC","NXT/BTC","LTC/BTC","ETC/BTC","DASH/BTC"],"stake_amount":0.001,"stake_currency":"BTC","stake_currency_decimals":8,"starting_balance":0.01,"dry_run_wallet":0.01,"final_balance":0.011041902985696884,"rejected_signals":0,"timedout_entry_orders":0,"timedout_exit_orders":0,"canceled_trade_entries":0,"canceled_entry_orders":0,"replaced_entry_orders":0,"max_open_trades":3,"max_open_trades_setting":3,"timeframe":"5m","timeframe_detail":"","timerange":"","enable_protections":false,"strategy_name":"StrategyTestV3","stoploss":0.1,"trailing_stop":false,"trailing_stop_positive":null,"trailing_stop_positive_offset":0.0,"trailing_only_offset_is_reached":false,"use_custom_stoploss":false,"minimal_roi":{},"use_exit_signal":true,"exit_profit_only":false,"exit_profit_offset":false,"ignore_roi_if_entry_signal":false,"backtest_best_day":0.17955111999999998,"backtest_worst_day":-0.14683468,"backtest_best_day_abs":0.000245614,"backtest_worst_day_abs":-0.0001325331,"winning_days":19,"draw_days":0,"losing_days":2,"daily_profit":[["2018-01-10",0.000245614],["2018-01-11",0.0001055138],["2018-01-12",4.51128e-05],["2018-01-13",3.00752e-05],["2018-01-14",3.50877e-05],["2018-01-15",6.51629e-05],["2018-01-16",5.11278e-05],["2018-01-17",7.01754e-05],["2018-01-18",8.5213e-05],["2018-01-19",3.00752e-05],["2018-01-20",2.50627e-05],["2018-01-21",4.01003e-05],["2018-01-22",7.01754e-05],["2018-01-23",8.5213e-05],["2018-01-24",8.02005e-05],["2018-01-25",-4.48622e-05],["2018-01-26",4.01003e-05],["2018-01-27",4.01003e-05],["2018-01-28",3.50877e-05],["2018-01-29",4.01003e-05],["2018-01-30",-0.0001325331]],"wins":48,"losses":9,"draws":122,"holding_avg":"3:40:00","holding_avg_s":13200.0,"winner_holding_avg":"0:24:00","winner_holding_avg_s":1440.0,"loser_holding_avg":"1 day, 5:57:00","loser_holding_avg_s":107820.0,"max_drawdown":0.21142322000000008,"max_drawdown_account":0.018740312808228732,"max_relative_drawdown":0.018740312808228732,"max_drawdown_abs":0.0002000000000000001,"drawdown_start":"2018-01-16 19:30:00","drawdown_start_ts":1516131000000.0,"drawdown_end":"2018-01-16 22:25:00","drawdown_end_ts":1516141500000.0,"max_drawdown_low":0.0004721804511278108,"max_drawdown_high":0.0006721804511278109,"csum_min":0.010045112781954888,"csum_max":0.011069172932330812}},"strategy_comparison":[{"key":"StrategyTestV3","trades":179,"profit_mean":0.0008041243575418989,"profit_mean_pct":0.0804124357541899,"profit_sum":0.1439382599999999,"profit_sum_pct":14.39,"profit_total_abs":0.0010419029856968845,"profit_total":0.10419029856968845,"profit_total_pct":10.42,"duration_avg":"3:40:00","wins":170,"draws":0,"losses":9,"max_drawdown_account":0.018740312808228732,"max_drawdown_abs":"0.0002"}]} +{"metadata":{"StrategyTestV3":{"run_id":"asdf","backtest_start_time":"2020-10-01 18:00:00+00:00"}},"strategy":{"StrategyTestV3":{"trades":[{"pair":"TRX/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":10.37344398340249,"open_date":"2018-01-10 07:15:00+00:00","close_date":"2018-01-10 07:20:00+00:00","open_rate":9.64e-05,"close_rate":0.00010074887218045112,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":8.676e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.676e-05,"stop_loss_ratio":0.1,"min_rate":9.64e-05,"max_rate":0.00010074887218045112,"is_open":false,"open_timestamp":1515568500000.0,"close_timestamp":1515568800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":21.026072329688816,"open_date":"2018-01-10 07:15:00+00:00","close_date":"2018-01-10 07:30:00+00:00","open_rate":4.756e-05,"close_rate":4.9705563909774425e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":15,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":4.2804e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.2804e-05,"stop_loss_ratio":0.1,"min_rate":4.756e-05,"max_rate":4.9705563909774425e-05,"is_open":false,"open_timestamp":1515568500000.0,"close_timestamp":1515569400000.0,"is_short":false,"leverage":1.0,"enter_tag":"buy_tag","orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":29.94908655286014,"open_date":"2018-01-10 07:25:00+00:00","close_date":"2018-01-10 07:35:00+00:00","open_rate":3.339e-05,"close_rate":3.489631578947368e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":10,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":3.0050999999999997e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.0050999999999997e-05,"stop_loss_ratio":0.1,"min_rate":3.339e-05,"max_rate":3.489631578947368e-05,"is_open":false,"open_timestamp":1515569100000.0,"close_timestamp":1515569700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":10.313531353135314,"open_date":"2018-01-10 07:25:00+00:00","close_date":"2018-01-10 07:40:00+00:00","open_rate":9.696e-05,"close_rate":0.00010133413533834584,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":15,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":8.7264e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.7264e-05,"stop_loss_ratio":0.1,"min_rate":9.696e-05,"max_rate":0.00010133413533834584,"is_open":false,"open_timestamp":1515569100000.0,"close_timestamp":1515570000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.010604453870625663,"open_date":"2018-01-10 07:35:00+00:00","close_date":"2018-01-10 08:35:00+00:00","open_rate":0.0943,"close_rate":0.09477268170426063,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.08487,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.08487,"stop_loss_ratio":0.1,"min_rate":0.0943,"max_rate":0.09477268170426063,"is_open":false,"open_timestamp":1515569700000.0,"close_timestamp":1515573300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.03677001860930642,"open_date":"2018-01-10 07:40:00+00:00","close_date":"2018-01-10 08:10:00+00:00","open_rate":0.02719607,"close_rate":0.02760503345864661,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.024476463,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.024476463,"stop_loss_ratio":0.1,"min_rate":0.02719607,"max_rate":0.02760503345864661,"is_open":false,"open_timestamp":1515570000000.0,"close_timestamp":1515571800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.021575196463739,"open_date":"2018-01-10 08:15:00+00:00","close_date":"2018-01-10 09:55:00+00:00","open_rate":0.04634952,"close_rate":0.046581848421052625,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":100,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.041714568,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.041714568,"stop_loss_ratio":0.1,"min_rate":0.04634952,"max_rate":0.046581848421052625,"is_open":false,"open_timestamp":1515572100000.0,"close_timestamp":1515578100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":32.615786040443574,"open_date":"2018-01-10 14:45:00+00:00","close_date":"2018-01-10 15:50:00+00:00","open_rate":3.066e-05,"close_rate":3.081368421052631e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":65,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":2.7594e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.7594e-05,"stop_loss_ratio":0.1,"min_rate":3.066e-05,"max_rate":3.081368421052631e-05,"is_open":false,"open_timestamp":1515595500000.0,"close_timestamp":1515599400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.05917194776300452,"open_date":"2018-01-10 16:35:00+00:00","close_date":"2018-01-10 17:15:00+00:00","open_rate":0.0168999,"close_rate":0.016984611278195488,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.01520991,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.01520991,"stop_loss_ratio":0.1,"min_rate":0.0168999,"max_rate":0.016984611278195488,"is_open":false,"open_timestamp":1515602100000.0,"close_timestamp":1515604500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.010949822656672253,"open_date":"2018-01-10 16:40:00+00:00","close_date":"2018-01-10 17:20:00+00:00","open_rate":0.09132568,"close_rate":0.0917834528320802,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.08219311200000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.08219311200000001,"stop_loss_ratio":0.1,"min_rate":0.09132568,"max_rate":0.0917834528320802,"is_open":false,"open_timestamp":1515602400000.0,"close_timestamp":1515604800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.011238476768326556,"open_date":"2018-01-10 18:50:00+00:00","close_date":"2018-01-10 19:45:00+00:00","open_rate":0.08898003,"close_rate":0.08942604518796991,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.080082027,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.080082027,"stop_loss_ratio":0.1,"min_rate":0.08898003,"max_rate":0.08942604518796991,"is_open":false,"open_timestamp":1515610200000.0,"close_timestamp":1515613500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.011682232072680309,"open_date":"2018-01-10 22:15:00+00:00","close_date":"2018-01-10 23:00:00+00:00","open_rate":0.08560008,"close_rate":0.08602915308270676,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.077040072,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.077040072,"stop_loss_ratio":0.1,"min_rate":0.08560008,"max_rate":0.08602915308270676,"is_open":false,"open_timestamp":1515622500000.0,"close_timestamp":1515625200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.4014726015023105,"open_date":"2018-01-10 22:50:00+00:00","close_date":"2018-01-10 23:20:00+00:00","open_rate":0.00249083,"close_rate":0.0025282860902255634,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002241747,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002241747,"stop_loss_ratio":0.1,"min_rate":0.00249083,"max_rate":0.0025282860902255634,"is_open":false,"open_timestamp":1515624600000.0,"close_timestamp":1515626400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":33.090668431502316,"open_date":"2018-01-10 23:15:00+00:00","close_date":"2018-01-11 00:15:00+00:00","open_rate":3.022e-05,"close_rate":3.037147869674185e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":2.7198e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.7198e-05,"stop_loss_ratio":0.1,"min_rate":3.022e-05,"max_rate":3.037147869674185e-05,"is_open":false,"open_timestamp":1515626100000.0,"close_timestamp":1515629700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.41034058268362744,"open_date":"2018-01-10 23:40:00+00:00","close_date":"2018-01-11 00:05:00+00:00","open_rate":0.002437,"close_rate":0.0024980776942355883,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":0.0021933,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0021933,"stop_loss_ratio":0.1,"min_rate":0.002437,"max_rate":0.0024980776942355883,"is_open":false,"open_timestamp":1515627600000.0,"close_timestamp":1515629100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.02095643931654345,"open_date":"2018-01-11 00:00:00+00:00","close_date":"2018-01-11 00:35:00+00:00","open_rate":0.04771803,"close_rate":0.04843559436090225,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.042946227,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.042946227,"stop_loss_ratio":0.1,"min_rate":0.04771803,"max_rate":0.04843559436090225,"is_open":false,"open_timestamp":1515628800000.0,"close_timestamp":1515630900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":27.389756231169542,"open_date":"2018-01-11 03:40:00+00:00","close_date":"2018-01-11 04:25:00+00:00","open_rate":3.651e-05,"close_rate":3.2859000000000005e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.10448878,"profit_abs":-9.999999999999994e-05,"exit_reason":"stop_loss","initial_stop_loss_abs":3.2859000000000005e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.2859000000000005e-05,"stop_loss_ratio":0.1,"min_rate":3.2859000000000005e-05,"max_rate":3.651e-05,"is_open":false,"open_timestamp":1515642000000.0,"close_timestamp":1515644700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.011332594070446804,"open_date":"2018-01-11 03:55:00+00:00","close_date":"2018-01-11 04:25:00+00:00","open_rate":0.08824105,"close_rate":0.08956798308270676,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.079416945,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.079416945,"stop_loss_ratio":0.1,"min_rate":0.08824105,"max_rate":0.08956798308270676,"is_open":false,"open_timestamp":1515642900000.0,"close_timestamp":1515644700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.411522633744856,"open_date":"2018-01-11 04:00:00+00:00","close_date":"2018-01-11 04:50:00+00:00","open_rate":0.00243,"close_rate":0.002442180451127819,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002187,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002187,"stop_loss_ratio":0.1,"min_rate":0.00243,"max_rate":0.002442180451127819,"is_open":false,"open_timestamp":1515643200000.0,"close_timestamp":1515646200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.022001890402423376,"open_date":"2018-01-11 04:30:00+00:00","close_date":"2018-01-11 04:55:00+00:00","open_rate":0.04545064,"close_rate":0.046589753784461146,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":0.040905576,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.040905576,"stop_loss_ratio":0.1,"min_rate":0.04545064,"max_rate":0.046589753784461146,"is_open":false,"open_timestamp":1515645000000.0,"close_timestamp":1515646500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":29.655990510083036,"open_date":"2018-01-11 04:30:00+00:00","close_date":"2018-01-11 04:50:00+00:00","open_rate":3.372e-05,"close_rate":3.456511278195488e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":3.0348e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.0348e-05,"stop_loss_ratio":0.1,"min_rate":3.372e-05,"max_rate":3.456511278195488e-05,"is_open":false,"open_timestamp":1515645000000.0,"close_timestamp":1515646200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.037821482602118005,"open_date":"2018-01-11 04:55:00+00:00","close_date":"2018-01-11 05:15:00+00:00","open_rate":0.02644,"close_rate":0.02710265664160401,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":0.023796,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.023796,"stop_loss_ratio":0.1,"min_rate":0.02644,"max_rate":0.02710265664160401,"is_open":false,"open_timestamp":1515646500000.0,"close_timestamp":1515647700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.011348161597821153,"open_date":"2018-01-11 11:20:00+00:00","close_date":"2018-01-11 12:00:00+00:00","open_rate":0.08812,"close_rate":0.08856170426065162,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.079308,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.079308,"stop_loss_ratio":0.1,"min_rate":0.08812,"max_rate":0.08856170426065162,"is_open":false,"open_timestamp":1515669600000.0,"close_timestamp":1515672000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.037263696923919086,"open_date":"2018-01-11 11:35:00+00:00","close_date":"2018-01-11 12:15:00+00:00","open_rate":0.02683577,"close_rate":0.026970285137844607,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.024152193,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.024152193,"stop_loss_ratio":0.1,"min_rate":0.02683577,"max_rate":0.026970285137844607,"is_open":false,"open_timestamp":1515670500000.0,"close_timestamp":1515672900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":20.329335230737954,"open_date":"2018-01-11 14:00:00+00:00","close_date":"2018-01-11 14:25:00+00:00","open_rate":4.919e-05,"close_rate":5.04228320802005e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":4.4271e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.4271e-05,"stop_loss_ratio":0.1,"min_rate":4.919e-05,"max_rate":5.04228320802005e-05,"is_open":false,"open_timestamp":1515679200000.0,"close_timestamp":1515680700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.01138317402960718,"open_date":"2018-01-11 19:25:00+00:00","close_date":"2018-01-11 20:35:00+00:00","open_rate":0.08784896,"close_rate":0.08828930566416039,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":70,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.079064064,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.079064064,"stop_loss_ratio":0.1,"min_rate":0.08784896,"max_rate":0.08828930566416039,"is_open":false,"open_timestamp":1515698700000.0,"close_timestamp":1515702900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":19.58863858961802,"open_date":"2018-01-11 22:35:00+00:00","close_date":"2018-01-11 23:30:00+00:00","open_rate":5.105e-05,"close_rate":5.130588972431077e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.5945e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.5945e-05,"stop_loss_ratio":0.1,"min_rate":5.105e-05,"max_rate":5.130588972431077e-05,"is_open":false,"open_timestamp":1515710100000.0,"close_timestamp":1515713400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":25.252525252525253,"open_date":"2018-01-11 22:55:00+00:00","close_date":"2018-01-11 23:25:00+00:00","open_rate":3.96e-05,"close_rate":4.019548872180451e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":3.5640000000000004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.5640000000000004e-05,"stop_loss_ratio":0.1,"min_rate":3.96e-05,"max_rate":4.019548872180451e-05,"is_open":false,"open_timestamp":1515711300000.0,"close_timestamp":1515713100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":34.66204506065858,"open_date":"2018-01-11 22:55:00+00:00","close_date":"2018-01-11 23:35:00+00:00","open_rate":2.885e-05,"close_rate":2.899461152882205e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":2.5965e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.5965e-05,"stop_loss_ratio":0.1,"min_rate":2.885e-05,"max_rate":2.899461152882205e-05,"is_open":false,"open_timestamp":1515711300000.0,"close_timestamp":1515713700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.03780718336483932,"open_date":"2018-01-11 23:30:00+00:00","close_date":"2018-01-12 00:05:00+00:00","open_rate":0.02645,"close_rate":0.026847744360902256,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":0.023805000000000003,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.023805000000000003,"stop_loss_ratio":0.1,"min_rate":0.02645,"max_rate":0.026847744360902256,"is_open":false,"open_timestamp":1515713400000.0,"close_timestamp":1515715500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.020833333333333332,"open_date":"2018-01-11 23:55:00+00:00","close_date":"2018-01-12 01:15:00+00:00","open_rate":0.048,"close_rate":0.04824060150375939,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0432,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0432,"stop_loss_ratio":0.1,"min_rate":0.048,"max_rate":0.04824060150375939,"is_open":false,"open_timestamp":1515714900000.0,"close_timestamp":1515719700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":21.31287297527707,"open_date":"2018-01-12 21:15:00+00:00","close_date":"2018-01-12 21:40:00+00:00","open_rate":4.692e-05,"close_rate":4.809593984962405e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":4.2228e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.2228e-05,"stop_loss_ratio":0.1,"min_rate":4.692e-05,"max_rate":4.809593984962405e-05,"is_open":false,"open_timestamp":1515791700000.0,"close_timestamp":1515793200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.38915654211062944,"open_date":"2018-01-13 00:55:00+00:00","close_date":"2018-01-13 06:20:00+00:00","open_rate":0.00256966,"close_rate":0.0025825405012531327,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":325,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002312694,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002312694,"stop_loss_ratio":0.1,"min_rate":0.00256966,"max_rate":0.0025825405012531327,"is_open":false,"open_timestamp":1515804900000.0,"close_timestamp":1515824400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":15.96933886937081,"open_date":"2018-01-13 10:55:00+00:00","close_date":"2018-01-13 11:35:00+00:00","open_rate":6.262e-05,"close_rate":6.293388471177944e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":5.6358e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.6358e-05,"stop_loss_ratio":0.1,"min_rate":6.262e-05,"max_rate":6.293388471177944e-05,"is_open":false,"open_timestamp":1515840900000.0,"close_timestamp":1515843300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":21.14164904862579,"open_date":"2018-01-13 13:05:00+00:00","close_date":"2018-01-15 14:10:00+00:00","open_rate":4.73e-05,"close_rate":4.753709273182957e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":2945,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.257e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.257e-05,"stop_loss_ratio":0.1,"min_rate":4.73e-05,"max_rate":4.753709273182957e-05,"is_open":false,"open_timestamp":1515848700000.0,"close_timestamp":1516025400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":16.49348507339601,"open_date":"2018-01-13 13:30:00+00:00","close_date":"2018-01-13 14:45:00+00:00","open_rate":6.063e-05,"close_rate":6.0933909774436085e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":75,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":5.4567e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.4567e-05,"stop_loss_ratio":0.1,"min_rate":6.063e-05,"max_rate":6.0933909774436085e-05,"is_open":false,"open_timestamp":1515850200000.0,"close_timestamp":1515854700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":9.023641941887746,"open_date":"2018-01-13 13:40:00+00:00","close_date":"2018-01-13 23:30:00+00:00","open_rate":0.00011082,"close_rate":0.00011137548872180448,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":590,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":9.9738e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":9.9738e-05,"stop_loss_ratio":0.1,"min_rate":0.00011082,"max_rate":0.00011137548872180448,"is_open":false,"open_timestamp":1515850800000.0,"close_timestamp":1515886200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":16.863406408094438,"open_date":"2018-01-13 15:15:00+00:00","close_date":"2018-01-13 15:55:00+00:00","open_rate":5.93e-05,"close_rate":5.9597243107769415e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":5.337e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.337e-05,"stop_loss_ratio":0.1,"min_rate":5.93e-05,"max_rate":5.9597243107769415e-05,"is_open":false,"open_timestamp":1515856500000.0,"close_timestamp":1515858900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.020618543947292404,"open_date":"2018-01-13 16:30:00+00:00","close_date":"2018-01-13 17:10:00+00:00","open_rate":0.04850003,"close_rate":0.04874313791979949,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.043650027,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.043650027,"stop_loss_ratio":0.1,"min_rate":0.04850003,"max_rate":0.04874313791979949,"is_open":false,"open_timestamp":1515861000000.0,"close_timestamp":1515863400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.010178097365511457,"open_date":"2018-01-13 22:05:00+00:00","close_date":"2018-01-14 06:25:00+00:00","open_rate":0.09825019,"close_rate":0.09874267215538848,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":500,"profit_ratio":-0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.088425171,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.088425171,"stop_loss_ratio":0.1,"min_rate":0.09825019,"max_rate":0.09874267215538848,"is_open":false,"open_timestamp":1515881100000.0,"close_timestamp":1515911100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":16.616816218012627,"open_date":"2018-01-14 00:20:00+00:00","close_date":"2018-01-14 22:55:00+00:00","open_rate":6.018e-05,"close_rate":6.048165413533834e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":1355,"profit_ratio":0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":5.4162e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.4162e-05,"stop_loss_ratio":0.1,"min_rate":6.018e-05,"max_rate":6.048165413533834e-05,"is_open":false,"open_timestamp":1515889200000.0,"close_timestamp":1515970500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.010246952581919518,"open_date":"2018-01-14 12:45:00+00:00","close_date":"2018-01-14 13:25:00+00:00","open_rate":0.09758999,"close_rate":0.0980791628822055,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.087830991,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.087830991,"stop_loss_ratio":0.1,"min_rate":0.09758999,"max_rate":0.0980791628822055,"is_open":false,"open_timestamp":1515933900000.0,"close_timestamp":1515936300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.3215434083601286,"open_date":"2018-01-14 15:30:00+00:00","close_date":"2018-01-14 16:00:00+00:00","open_rate":0.00311,"close_rate":0.0031567669172932328,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002799,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002799,"stop_loss_ratio":0.1,"min_rate":0.00311,"max_rate":0.0031567669172932328,"is_open":false,"open_timestamp":1515943800000.0,"close_timestamp":1515945600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.32010140812609433,"open_date":"2018-01-14 20:45:00+00:00","close_date":"2018-01-14 22:15:00+00:00","open_rate":0.00312401,"close_rate":0.003139669197994987,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":90,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002811609,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002811609,"stop_loss_ratio":0.1,"min_rate":0.00312401,"max_rate":0.003139669197994987,"is_open":false,"open_timestamp":1515962700000.0,"close_timestamp":1515968100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.057247866085791646,"open_date":"2018-01-14 23:35:00+00:00","close_date":"2018-01-15 00:30:00+00:00","open_rate":0.0174679,"close_rate":0.017555458395989976,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.015721110000000003,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.015721110000000003,"stop_loss_ratio":0.1,"min_rate":0.0174679,"max_rate":0.017555458395989976,"is_open":false,"open_timestamp":1515972900000.0,"close_timestamp":1515976200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.013611282991367995,"open_date":"2018-01-14 23:45:00+00:00","close_date":"2018-01-15 00:25:00+00:00","open_rate":0.07346846,"close_rate":0.07383672295739348,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.066121614,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.066121614,"stop_loss_ratio":0.1,"min_rate":0.07346846,"max_rate":0.07383672295739348,"is_open":false,"open_timestamp":1515973500000.0,"close_timestamp":1515975900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.010204706410596568,"open_date":"2018-01-15 02:25:00+00:00","close_date":"2018-01-15 03:05:00+00:00","open_rate":0.097994,"close_rate":0.09848519799498744,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0881946,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0881946,"stop_loss_ratio":0.1,"min_rate":0.097994,"max_rate":0.09848519799498744,"is_open":false,"open_timestamp":1515983100000.0,"close_timestamp":1515985500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.010353038616834042,"open_date":"2018-01-15 07:20:00+00:00","close_date":"2018-01-15 08:00:00+00:00","open_rate":0.09659,"close_rate":0.09707416040100247,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.086931,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.086931,"stop_loss_ratio":0.1,"min_rate":0.09659,"max_rate":0.09707416040100247,"is_open":false,"open_timestamp":1516000800000.0,"close_timestamp":1516003200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":10.0130169219986,"open_date":"2018-01-15 08:20:00+00:00","close_date":"2018-01-15 08:55:00+00:00","open_rate":9.987e-05,"close_rate":0.00010137180451127818,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":8.9883e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.9883e-05,"stop_loss_ratio":0.1,"min_rate":9.987e-05,"max_rate":0.00010137180451127818,"is_open":false,"open_timestamp":1516004400000.0,"close_timestamp":1516006500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.010537752023511832,"open_date":"2018-01-15 12:10:00+00:00","close_date":"2018-01-16 02:50:00+00:00","open_rate":0.0948969,"close_rate":0.09537257368421052,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":880,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.08540721000000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.08540721000000001,"stop_loss_ratio":0.1,"min_rate":0.0948969,"max_rate":0.09537257368421052,"is_open":false,"open_timestamp":1516018200000.0,"close_timestamp":1516071000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.014084507042253523,"open_date":"2018-01-15 14:10:00+00:00","close_date":"2018-01-15 17:40:00+00:00","open_rate":0.071,"close_rate":0.07135588972431077,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":210,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0639,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0639,"stop_loss_ratio":0.1,"min_rate":0.071,"max_rate":0.07135588972431077,"is_open":false,"open_timestamp":1516025400000.0,"close_timestamp":1516038000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.021736763017766975,"open_date":"2018-01-15 14:30:00+00:00","close_date":"2018-01-15 15:10:00+00:00","open_rate":0.04600501,"close_rate":0.046235611553884705,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.041404509,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.041404509,"stop_loss_ratio":0.1,"min_rate":0.04600501,"max_rate":0.046235611553884705,"is_open":false,"open_timestamp":1516026600000.0,"close_timestamp":1516029000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":10.595465140919686,"open_date":"2018-01-15 18:10:00+00:00","close_date":"2018-01-15 19:25:00+00:00","open_rate":9.438e-05,"close_rate":9.485308270676693e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":75,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":8.4942e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.4942e-05,"stop_loss_ratio":0.1,"min_rate":9.438e-05,"max_rate":9.485308270676693e-05,"is_open":false,"open_timestamp":1516039800000.0,"close_timestamp":1516044300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.032894726021471705,"open_date":"2018-01-15 18:35:00+00:00","close_date":"2018-01-15 19:15:00+00:00","open_rate":0.03040001,"close_rate":0.030552391002506264,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.027360009,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.027360009,"stop_loss_ratio":0.1,"min_rate":0.03040001,"max_rate":0.030552391002506264,"is_open":false,"open_timestamp":1516041300000.0,"close_timestamp":1516043700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":17.13208840157615,"open_date":"2018-01-15 20:25:00+00:00","close_date":"2018-01-16 08:25:00+00:00","open_rate":5.837e-05,"close_rate":5.2533e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":720,"profit_ratio":-0.10448878,"profit_abs":-0.00010000000000000005,"exit_reason":"stop_loss","initial_stop_loss_abs":5.2533e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.2533e-05,"stop_loss_ratio":0.1,"min_rate":5.2533e-05,"max_rate":5.837e-05,"is_open":false,"open_timestamp":1516047900000.0,"close_timestamp":1516091100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.021722130506560085,"open_date":"2018-01-15 20:40:00+00:00","close_date":"2018-01-15 22:00:00+00:00","open_rate":0.046036,"close_rate":0.04626675689223057,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0414324,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0414324,"stop_loss_ratio":0.1,"min_rate":0.046036,"max_rate":0.04626675689223057,"is_open":false,"open_timestamp":1516048800000.0,"close_timestamp":1516053600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.34861425832316545,"open_date":"2018-01-16 00:30:00+00:00","close_date":"2018-01-16 01:10:00+00:00","open_rate":0.0028685,"close_rate":0.0028828784461152877,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.00258165,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.00258165,"stop_loss_ratio":0.1,"min_rate":0.0028685,"max_rate":0.0028828784461152877,"is_open":false,"open_timestamp":1516062600000.0,"close_timestamp":1516065000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.014854967241083492,"open_date":"2018-01-16 01:15:00+00:00","close_date":"2018-01-16 02:35:00+00:00","open_rate":0.06731755,"close_rate":0.0676549813283208,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.060585795000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.060585795000000005,"stop_loss_ratio":0.1,"min_rate":0.06731755,"max_rate":0.0676549813283208,"is_open":false,"open_timestamp":1516065300000.0,"close_timestamp":1516070100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.010848794492804754,"open_date":"2018-01-16 07:45:00+00:00","close_date":"2018-01-16 08:40:00+00:00","open_rate":0.09217614,"close_rate":0.09263817578947368,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.082958526,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.082958526,"stop_loss_ratio":0.1,"min_rate":0.09217614,"max_rate":0.09263817578947368,"is_open":false,"open_timestamp":1516088700000.0,"close_timestamp":1516092000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.06060606060606061,"open_date":"2018-01-16 08:35:00+00:00","close_date":"2018-01-16 08:55:00+00:00","open_rate":0.0165,"close_rate":0.016913533834586467,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641604113e-05,"exit_reason":"roi","initial_stop_loss_abs":0.01485,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.01485,"stop_loss_ratio":0.1,"min_rate":0.0165,"max_rate":0.016913533834586467,"is_open":false,"open_timestamp":1516091700000.0,"close_timestamp":1516092900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":12.57387149503332,"open_date":"2018-01-16 08:35:00+00:00","close_date":"2018-01-16 08:40:00+00:00","open_rate":7.953e-05,"close_rate":8.311781954887218e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":7.157700000000001e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":7.157700000000001e-05,"stop_loss_ratio":0.1,"min_rate":7.953e-05,"max_rate":8.311781954887218e-05,"is_open":false,"open_timestamp":1516091700000.0,"close_timestamp":1516092000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.022122914915269236,"open_date":"2018-01-16 08:45:00+00:00","close_date":"2018-01-16 09:50:00+00:00","open_rate":0.045202,"close_rate":0.04542857644110275,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":65,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0406818,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0406818,"stop_loss_ratio":0.1,"min_rate":0.045202,"max_rate":0.04542857644110275,"is_open":false,"open_timestamp":1516092300000.0,"close_timestamp":1516096200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":19.054878048780488,"open_date":"2018-01-16 09:15:00+00:00","close_date":"2018-01-16 09:45:00+00:00","open_rate":5.248e-05,"close_rate":5.326917293233082e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":4.7232e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7232e-05,"stop_loss_ratio":0.1,"min_rate":5.248e-05,"max_rate":5.326917293233082e-05,"is_open":false,"open_timestamp":1516094100000.0,"close_timestamp":1516095900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.03457434486802627,"open_date":"2018-01-16 09:15:00+00:00","close_date":"2018-01-16 09:55:00+00:00","open_rate":0.02892318,"close_rate":0.02906815834586466,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.026030862,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.026030862,"stop_loss_ratio":0.1,"min_rate":0.02892318,"max_rate":0.02906815834586466,"is_open":false,"open_timestamp":1516094100000.0,"close_timestamp":1516096500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":19.38735944164405,"open_date":"2018-01-16 09:50:00+00:00","close_date":"2018-01-16 10:10:00+00:00","open_rate":5.158e-05,"close_rate":5.287273182957392e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":4.6422e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6422e-05,"stop_loss_ratio":0.1,"min_rate":5.158e-05,"max_rate":5.287273182957392e-05,"is_open":false,"open_timestamp":1516096200000.0,"close_timestamp":1516097400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.035357778286929785,"open_date":"2018-01-16 10:05:00+00:00","close_date":"2018-01-16 10:35:00+00:00","open_rate":0.02828232,"close_rate":0.02870761804511278,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.025454088,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025454088,"stop_loss_ratio":0.1,"min_rate":0.02828232,"max_rate":0.02870761804511278,"is_open":false,"open_timestamp":1516097100000.0,"close_timestamp":1516098900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.022948496230938985,"open_date":"2018-01-16 10:05:00+00:00","close_date":"2018-01-16 10:40:00+00:00","open_rate":0.04357584,"close_rate":0.044231115789473675,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.039218256,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.039218256,"stop_loss_ratio":0.1,"min_rate":0.04357584,"max_rate":0.044231115789473675,"is_open":false,"open_timestamp":1516097100000.0,"close_timestamp":1516099200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":18.64975755315181,"open_date":"2018-01-16 13:45:00+00:00","close_date":"2018-01-16 14:20:00+00:00","open_rate":5.362e-05,"close_rate":5.442631578947368e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":4.8258e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.8258e-05,"stop_loss_ratio":0.1,"min_rate":5.362e-05,"max_rate":5.442631578947368e-05,"is_open":false,"open_timestamp":1516110300000.0,"close_timestamp":1516112400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":18.86080724254998,"open_date":"2018-01-16 17:30:00+00:00","close_date":"2018-01-16 18:25:00+00:00","open_rate":5.302e-05,"close_rate":5.328576441102756e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.7718e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7718e-05,"stop_loss_ratio":0.1,"min_rate":5.302e-05,"max_rate":5.328576441102756e-05,"is_open":false,"open_timestamp":1516123800000.0,"close_timestamp":1516127100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.010952903718828448,"open_date":"2018-01-16 18:15:00+00:00","close_date":"2018-01-16 18:45:00+00:00","open_rate":0.09129999,"close_rate":0.09267292218045112,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":0.082169991,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.082169991,"stop_loss_ratio":0.1,"min_rate":0.09129999,"max_rate":0.09267292218045112,"is_open":false,"open_timestamp":1516126500000.0,"close_timestamp":1516128300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":26.26050420168067,"open_date":"2018-01-16 18:15:00+00:00","close_date":"2018-01-16 18:35:00+00:00","open_rate":3.808e-05,"close_rate":3.903438596491228e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":3.4272e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.4272e-05,"stop_loss_ratio":0.1,"min_rate":3.808e-05,"max_rate":3.903438596491228e-05,"is_open":false,"open_timestamp":1516126500000.0,"close_timestamp":1516127700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.035574376772493324,"open_date":"2018-01-16 19:00:00+00:00","close_date":"2018-01-16 19:30:00+00:00","open_rate":0.02811012,"close_rate":0.028532828571428567,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.025299108,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025299108,"stop_loss_ratio":0.1,"min_rate":0.02811012,"max_rate":0.028532828571428567,"is_open":false,"open_timestamp":1516129200000.0,"close_timestamp":1516131000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.387028357567759,"open_date":"2018-01-16 21:25:00+00:00","close_date":"2018-01-16 22:25:00+00:00","open_rate":0.00258379,"close_rate":0.002325411,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":-0.10448878,"profit_abs":-0.00010000000000000005,"exit_reason":"stop_loss","initial_stop_loss_abs":0.002325411,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002325411,"stop_loss_ratio":0.1,"min_rate":0.002325411,"max_rate":0.00258379,"is_open":false,"open_timestamp":1516137900000.0,"close_timestamp":1516141500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":39.07776475185619,"open_date":"2018-01-16 21:25:00+00:00","close_date":"2018-01-16 22:45:00+00:00","open_rate":2.559e-05,"close_rate":2.3031e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":-0.10448878,"profit_abs":-0.00010000000000000005,"exit_reason":"stop_loss","initial_stop_loss_abs":2.3031e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.3031e-05,"stop_loss_ratio":0.1,"min_rate":2.3031e-05,"max_rate":2.559e-05,"is_open":false,"open_timestamp":1516137900000.0,"close_timestamp":1516142700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":13.123359580052494,"open_date":"2018-01-16 21:35:00+00:00","close_date":"2018-01-16 22:25:00+00:00","open_rate":7.62e-05,"close_rate":6.858e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":-0.10448878,"profit_abs":-0.00010000000000000005,"exit_reason":"stop_loss","initial_stop_loss_abs":6.858e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":6.858e-05,"stop_loss_ratio":0.1,"min_rate":6.858e-05,"max_rate":7.62e-05,"is_open":false,"open_timestamp":1516138500000.0,"close_timestamp":1516141500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.4350777048780912,"open_date":"2018-01-16 22:30:00+00:00","close_date":"2018-01-16 22:35:00+00:00","open_rate":0.00229844,"close_rate":0.002402129022556391,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":4.511278195488727e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002068596,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002068596,"stop_loss_ratio":0.1,"min_rate":0.00229844,"max_rate":0.002402129022556391,"is_open":false,"open_timestamp":1516141800000.0,"close_timestamp":1516142100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.06622516556291391,"open_date":"2018-01-16 22:30:00+00:00","close_date":"2018-01-16 22:40:00+00:00","open_rate":0.0151,"close_rate":0.015781203007518795,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":10,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":0.01359,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.01359,"stop_loss_ratio":0.1,"min_rate":0.0151,"max_rate":0.015781203007518795,"is_open":false,"open_timestamp":1516141800000.0,"close_timestamp":1516142400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.4243113426908128,"open_date":"2018-01-16 22:40:00+00:00","close_date":"2018-01-16 22:45:00+00:00","open_rate":0.00235676,"close_rate":0.00246308,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":4.511278195488727e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002121084,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002121084,"stop_loss_ratio":0.1,"min_rate":0.00235676,"max_rate":0.00246308,"is_open":false,"open_timestamp":1516142400000.0,"close_timestamp":1516142700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.01585559988076589,"open_date":"2018-01-16 22:45:00+00:00","close_date":"2018-01-16 23:05:00+00:00","open_rate":0.0630692,"close_rate":0.06464988170426066,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641604113e-05,"exit_reason":"roi","initial_stop_loss_abs":0.056762280000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.056762280000000005,"stop_loss_ratio":0.1,"min_rate":0.0630692,"max_rate":0.06464988170426066,"is_open":false,"open_timestamp":1516142700000.0,"close_timestamp":1516143900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":45.45454545454545,"open_date":"2018-01-16 22:50:00+00:00","close_date":"2018-01-16 22:55:00+00:00","open_rate":2.2e-05,"close_rate":2.299248120300751e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":5,"profit_ratio":0.03990025,"profit_abs":4.511278195488684e-05,"exit_reason":"roi","initial_stop_loss_abs":1.98e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":1.98e-05,"stop_loss_ratio":0.1,"min_rate":2.2e-05,"max_rate":2.299248120300751e-05,"is_open":false,"open_timestamp":1516143000000.0,"close_timestamp":1516143300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":20.10454362685967,"open_date":"2018-01-17 03:30:00+00:00","close_date":"2018-01-17 04:00:00+00:00","open_rate":4.974e-05,"close_rate":5.048796992481203e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":4.4766000000000005e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.4766000000000005e-05,"stop_loss_ratio":0.1,"min_rate":4.974e-05,"max_rate":5.048796992481203e-05,"is_open":false,"open_timestamp":1516159800000.0,"close_timestamp":1516161600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":14.068655036578503,"open_date":"2018-01-17 03:55:00+00:00","close_date":"2018-01-17 04:15:00+00:00","open_rate":7.108e-05,"close_rate":7.28614536340852e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641603896e-05,"exit_reason":"roi","initial_stop_loss_abs":6.3972e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":6.3972e-05,"stop_loss_ratio":0.1,"min_rate":7.108e-05,"max_rate":7.28614536340852e-05,"is_open":false,"open_timestamp":1516161300000.0,"close_timestamp":1516162500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.0231107002542177,"open_date":"2018-01-17 09:35:00+00:00","close_date":"2018-01-17 10:15:00+00:00","open_rate":0.04327,"close_rate":0.04348689223057644,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.038943000000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.038943000000000005,"stop_loss_ratio":0.1,"min_rate":0.04327,"max_rate":0.04348689223057644,"is_open":false,"open_timestamp":1516181700000.0,"close_timestamp":1516184100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":20.012007204322593,"open_date":"2018-01-17 10:20:00+00:00","close_date":"2018-01-17 17:00:00+00:00","open_rate":4.997e-05,"close_rate":5.022047619047618e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":400,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":4.4973e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.4973e-05,"stop_loss_ratio":0.1,"min_rate":4.997e-05,"max_rate":5.022047619047618e-05,"is_open":false,"open_timestamp":1516184400000.0,"close_timestamp":1516208400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.014626687444363738,"open_date":"2018-01-17 10:30:00+00:00","close_date":"2018-01-17 11:25:00+00:00","open_rate":0.06836818,"close_rate":0.06871087764411027,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.061531362,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.061531362,"stop_loss_ratio":0.1,"min_rate":0.06836818,"max_rate":0.06871087764411027,"is_open":false,"open_timestamp":1516185000000.0,"close_timestamp":1516188300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":27.548209366391184,"open_date":"2018-01-17 10:30:00+00:00","close_date":"2018-01-17 11:10:00+00:00","open_rate":3.63e-05,"close_rate":3.648195488721804e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":3.2670000000000004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.2670000000000004e-05,"stop_loss_ratio":0.1,"min_rate":3.63e-05,"max_rate":3.648195488721804e-05,"is_open":false,"open_timestamp":1516185000000.0,"close_timestamp":1516187400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.03558718861209965,"open_date":"2018-01-17 12:30:00+00:00","close_date":"2018-01-17 22:05:00+00:00","open_rate":0.0281,"close_rate":0.02824085213032581,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":575,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.02529,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.02529,"stop_loss_ratio":0.1,"min_rate":0.0281,"max_rate":0.02824085213032581,"is_open":false,"open_timestamp":1516192200000.0,"close_timestamp":1516226700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.011559355963546878,"open_date":"2018-01-17 12:35:00+00:00","close_date":"2018-01-17 16:55:00+00:00","open_rate":0.08651001,"close_rate":0.08694364413533832,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":260,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.077859009,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.077859009,"stop_loss_ratio":0.1,"min_rate":0.08651001,"max_rate":0.08694364413533832,"is_open":false,"open_timestamp":1516192500000.0,"close_timestamp":1516208100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":17.752529735487308,"open_date":"2018-01-18 05:00:00+00:00","close_date":"2018-01-18 05:55:00+00:00","open_rate":5.633e-05,"close_rate":5.6612355889724306e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":5.0697e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.0697e-05,"stop_loss_ratio":0.1,"min_rate":5.633e-05,"max_rate":5.6612355889724306e-05,"is_open":false,"open_timestamp":1516251600000.0,"close_timestamp":1516254900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.01430923457900944,"open_date":"2018-01-18 05:20:00+00:00","close_date":"2018-01-18 05:55:00+00:00","open_rate":0.06988494,"close_rate":0.07093584135338346,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":35,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":0.06289644600000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.06289644600000001,"stop_loss_ratio":0.1,"min_rate":0.06988494,"max_rate":0.07093584135338346,"is_open":false,"open_timestamp":1516252800000.0,"close_timestamp":1516254900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":18.034265103697024,"open_date":"2018-01-18 07:35:00+00:00","close_date":"2018-01-18 08:15:00+00:00","open_rate":5.545e-05,"close_rate":5.572794486215538e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":4.9905e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.9905e-05,"stop_loss_ratio":0.1,"min_rate":5.545e-05,"max_rate":5.572794486215538e-05,"is_open":false,"open_timestamp":1516260900000.0,"close_timestamp":1516263300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.06121723118136401,"open_date":"2018-01-18 09:00:00+00:00","close_date":"2018-01-18 09:40:00+00:00","open_rate":0.01633527,"close_rate":0.016417151052631574,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.014701743,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.014701743,"stop_loss_ratio":0.1,"min_rate":0.01633527,"max_rate":0.016417151052631574,"is_open":false,"open_timestamp":1516266000000.0,"close_timestamp":1516268400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.3707356136045141,"open_date":"2018-01-18 16:40:00+00:00","close_date":"2018-01-18 17:20:00+00:00","open_rate":0.00269734,"close_rate":0.002710860501253133,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002427606,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002427606,"stop_loss_ratio":0.1,"min_rate":0.00269734,"max_rate":0.002710860501253133,"is_open":false,"open_timestamp":1516293600000.0,"close_timestamp":1516296000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":22.3463687150838,"open_date":"2018-01-18 18:05:00+00:00","close_date":"2018-01-18 18:30:00+00:00","open_rate":4.475e-05,"close_rate":4.587155388471177e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":2.5062656641604113e-05,"exit_reason":"roi","initial_stop_loss_abs":4.0275e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.0275e-05,"stop_loss_ratio":0.1,"min_rate":4.475e-05,"max_rate":4.587155388471177e-05,"is_open":false,"open_timestamp":1516298700000.0,"close_timestamp":1516300200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":35.842293906810035,"open_date":"2018-01-18 18:25:00+00:00","close_date":"2018-01-18 18:55:00+00:00","open_rate":2.79e-05,"close_rate":2.8319548872180444e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":2.511e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.511e-05,"stop_loss_ratio":0.1,"min_rate":2.79e-05,"max_rate":2.8319548872180444e-05,"is_open":false,"open_timestamp":1516299900000.0,"close_timestamp":1516301700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.022525942001105578,"open_date":"2018-01-18 20:10:00+00:00","close_date":"2018-01-18 20:50:00+00:00","open_rate":0.04439326,"close_rate":0.04461578260651629,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.039953934,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.039953934,"stop_loss_ratio":0.1,"min_rate":0.04439326,"max_rate":0.04461578260651629,"is_open":false,"open_timestamp":1516306200000.0,"close_timestamp":1516308600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":22.271714922048996,"open_date":"2018-01-18 21:30:00+00:00","close_date":"2018-01-19 00:35:00+00:00","open_rate":4.49e-05,"close_rate":4.51250626566416e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":185,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.041e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.041e-05,"stop_loss_ratio":0.1,"min_rate":4.49e-05,"max_rate":4.51250626566416e-05,"is_open":false,"open_timestamp":1516311000000.0,"close_timestamp":1516322100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.03502626970227671,"open_date":"2018-01-18 21:55:00+00:00","close_date":"2018-01-19 05:05:00+00:00","open_rate":0.02855,"close_rate":0.028693107769423555,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":430,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.025695,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025695,"stop_loss_ratio":0.1,"min_rate":0.02855,"max_rate":0.028693107769423555,"is_open":false,"open_timestamp":1516312500000.0,"close_timestamp":1516338300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":17.25327812284334,"open_date":"2018-01-18 22:10:00+00:00","close_date":"2018-01-18 22:50:00+00:00","open_rate":5.796e-05,"close_rate":5.8250526315789473e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":5.2164e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.2164e-05,"stop_loss_ratio":0.1,"min_rate":5.796e-05,"max_rate":5.8250526315789473e-05,"is_open":false,"open_timestamp":1516313400000.0,"close_timestamp":1516315800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.02303975994413319,"open_date":"2018-01-18 23:50:00+00:00","close_date":"2018-01-19 00:30:00+00:00","open_rate":0.04340323,"close_rate":0.04362079005012531,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.039062907,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.039062907,"stop_loss_ratio":0.1,"min_rate":0.04340323,"max_rate":0.04362079005012531,"is_open":false,"open_timestamp":1516319400000.0,"close_timestamp":1516321800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.02244943545282195,"open_date":"2018-01-19 16:45:00+00:00","close_date":"2018-01-19 17:35:00+00:00","open_rate":0.04454455,"close_rate":0.04476783095238095,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.040090095000000006,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.040090095000000006,"stop_loss_ratio":0.1,"min_rate":0.04454455,"max_rate":0.04476783095238095,"is_open":false,"open_timestamp":1516380300000.0,"close_timestamp":1516383300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":17.793594306049823,"open_date":"2018-01-19 17:15:00+00:00","close_date":"2018-01-19 19:55:00+00:00","open_rate":5.62e-05,"close_rate":5.648170426065162e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":160,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":5.058e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.058e-05,"stop_loss_ratio":0.1,"min_rate":5.62e-05,"max_rate":5.648170426065162e-05,"is_open":false,"open_timestamp":1516382100000.0,"close_timestamp":1516391700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":23.04678497349619,"open_date":"2018-01-19 17:20:00+00:00","close_date":"2018-01-19 20:15:00+00:00","open_rate":4.339e-05,"close_rate":4.360749373433584e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":175,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":3.9051e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.9051e-05,"stop_loss_ratio":0.1,"min_rate":4.339e-05,"max_rate":4.360749373433584e-05,"is_open":false,"open_timestamp":1516382400000.0,"close_timestamp":1516392900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":9.910802775024775,"open_date":"2018-01-20 04:45:00+00:00","close_date":"2018-01-20 17:35:00+00:00","open_rate":0.0001009,"close_rate":0.00010140576441102755,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":770,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":9.081e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":9.081e-05,"stop_loss_ratio":0.1,"min_rate":0.0001009,"max_rate":0.00010140576441102755,"is_open":false,"open_timestamp":1516423500000.0,"close_timestamp":1516469700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.3696789338459548,"open_date":"2018-01-20 04:50:00+00:00","close_date":"2018-01-20 15:15:00+00:00","open_rate":0.00270505,"close_rate":0.002718609147869674,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":625,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002434545,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002434545,"stop_loss_ratio":0.1,"min_rate":0.00270505,"max_rate":0.002718609147869674,"is_open":false,"open_timestamp":1516423800000.0,"close_timestamp":1516461300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.033333311111125925,"open_date":"2018-01-20 04:50:00+00:00","close_date":"2018-01-20 07:00:00+00:00","open_rate":0.03000002,"close_rate":0.030150396040100245,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":130,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.027000018,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.027000018,"stop_loss_ratio":0.1,"min_rate":0.03000002,"max_rate":0.030150396040100245,"is_open":false,"open_timestamp":1516423800000.0,"close_timestamp":1516431600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":18.315018315018317,"open_date":"2018-01-20 09:00:00+00:00","close_date":"2018-01-20 09:40:00+00:00","open_rate":5.46e-05,"close_rate":5.4873684210526304e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.914e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.914e-05,"stop_loss_ratio":0.1,"min_rate":5.46e-05,"max_rate":5.4873684210526304e-05,"is_open":false,"open_timestamp":1516438800000.0,"close_timestamp":1516441200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.03244412634781012,"open_date":"2018-01-20 18:25:00+00:00","close_date":"2018-01-25 03:50:00+00:00","open_rate":0.03082222,"close_rate":0.027739998,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":6325,"profit_ratio":-0.10448878,"profit_abs":-0.00010000000000000015,"exit_reason":"stop_loss","initial_stop_loss_abs":0.027739998,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.027739998,"stop_loss_ratio":0.1,"min_rate":0.027739998,"max_rate":0.03082222,"is_open":false,"open_timestamp":1516472700000.0,"close_timestamp":1516852200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.011148273260677063,"open_date":"2018-01-20 22:25:00+00:00","close_date":"2018-01-20 23:15:00+00:00","open_rate":0.08969999,"close_rate":0.09014961401002504,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.080729991,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.080729991,"stop_loss_ratio":0.1,"min_rate":0.08969999,"max_rate":0.09014961401002504,"is_open":false,"open_timestamp":1516487100000.0,"close_timestamp":1516490100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.06125570520324337,"open_date":"2018-01-21 02:50:00+00:00","close_date":"2018-01-21 14:30:00+00:00","open_rate":0.01632501,"close_rate":0.01640683962406015,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":700,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.014692509,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.014692509,"stop_loss_ratio":0.1,"min_rate":0.01632501,"max_rate":0.01640683962406015,"is_open":false,"open_timestamp":1516503000000.0,"close_timestamp":1516545000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.01417675579120474,"open_date":"2018-01-21 10:20:00+00:00","close_date":"2018-01-21 11:00:00+00:00","open_rate":0.070538,"close_rate":0.07089157393483708,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0634842,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0634842,"stop_loss_ratio":0.1,"min_rate":0.070538,"max_rate":0.07089157393483708,"is_open":false,"open_timestamp":1516530000000.0,"close_timestamp":1516532400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":18.864365214110546,"open_date":"2018-01-21 15:50:00+00:00","close_date":"2018-01-21 18:45:00+00:00","open_rate":5.301e-05,"close_rate":5.327571428571427e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":175,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":4.7709e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7709e-05,"stop_loss_ratio":0.1,"min_rate":5.301e-05,"max_rate":5.327571428571427e-05,"is_open":false,"open_timestamp":1516549800000.0,"close_timestamp":1516560300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":25.284450063211125,"open_date":"2018-01-21 16:20:00+00:00","close_date":"2018-01-21 17:00:00+00:00","open_rate":3.955e-05,"close_rate":3.9748245614035085e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":3.5595e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.5595e-05,"stop_loss_ratio":0.1,"min_rate":3.955e-05,"max_rate":3.9748245614035085e-05,"is_open":false,"open_timestamp":1516551600000.0,"close_timestamp":1516554000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.38683971296493297,"open_date":"2018-01-21 21:15:00+00:00","close_date":"2018-01-21 21:45:00+00:00","open_rate":0.00258505,"close_rate":0.002623922932330827,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002326545,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002326545,"stop_loss_ratio":0.1,"min_rate":0.00258505,"max_rate":0.002623922932330827,"is_open":false,"open_timestamp":1516569300000.0,"close_timestamp":1516571100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":25.621316935690498,"open_date":"2018-01-21 21:15:00+00:00","close_date":"2018-01-21 21:55:00+00:00","open_rate":3.903e-05,"close_rate":3.922563909774435e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":3.5127e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.5127e-05,"stop_loss_ratio":0.1,"min_rate":3.903e-05,"max_rate":3.922563909774435e-05,"is_open":false,"open_timestamp":1516569300000.0,"close_timestamp":1516571700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":19.098548510313215,"open_date":"2018-01-22 00:35:00+00:00","close_date":"2018-01-22 10:35:00+00:00","open_rate":5.236e-05,"close_rate":5.262245614035087e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":600,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":4.7124e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7124e-05,"stop_loss_ratio":0.1,"min_rate":5.236e-05,"max_rate":5.262245614035087e-05,"is_open":false,"open_timestamp":1516581300000.0,"close_timestamp":1516617300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":11.076650420912715,"open_date":"2018-01-22 01:30:00+00:00","close_date":"2018-01-22 02:10:00+00:00","open_rate":9.028e-05,"close_rate":9.07325313283208e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":8.1252e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.1252e-05,"stop_loss_ratio":0.1,"min_rate":9.028e-05,"max_rate":9.07325313283208e-05,"is_open":false,"open_timestamp":1516584600000.0,"close_timestamp":1516587000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.3721622627465575,"open_date":"2018-01-22 12:25:00+00:00","close_date":"2018-01-22 14:35:00+00:00","open_rate":0.002687,"close_rate":0.002700468671679198,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":130,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0024183000000000004,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0024183000000000004,"stop_loss_ratio":0.1,"min_rate":0.002687,"max_rate":0.002700468671679198,"is_open":false,"open_timestamp":1516623900000.0,"close_timestamp":1516631700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":23.99232245681382,"open_date":"2018-01-22 13:15:00+00:00","close_date":"2018-01-22 13:55:00+00:00","open_rate":4.168e-05,"close_rate":4.188892230576441e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":3.7512e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.7512e-05,"stop_loss_ratio":0.1,"min_rate":4.168e-05,"max_rate":4.188892230576441e-05,"is_open":false,"open_timestamp":1516626900000.0,"close_timestamp":1516629300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":11.336583153837434,"open_date":"2018-01-22 14:00:00+00:00","close_date":"2018-01-22 14:30:00+00:00","open_rate":8.821e-05,"close_rate":8.953646616541353e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":7.9389e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":7.9389e-05,"stop_loss_ratio":0.1,"min_rate":8.821e-05,"max_rate":8.953646616541353e-05,"is_open":false,"open_timestamp":1516629600000.0,"close_timestamp":1516631400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":19.334880123743233,"open_date":"2018-01-22 15:55:00+00:00","close_date":"2018-01-22 16:40:00+00:00","open_rate":5.172e-05,"close_rate":5.1979248120300745e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.6548e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6548e-05,"stop_loss_ratio":0.1,"min_rate":5.172e-05,"max_rate":5.1979248120300745e-05,"is_open":false,"open_timestamp":1516636500000.0,"close_timestamp":1516639200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":33.04692663582287,"open_date":"2018-01-22 16:05:00+00:00","close_date":"2018-01-22 16:25:00+00:00","open_rate":3.026e-05,"close_rate":3.101839598997494e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":20,"profit_ratio":0.01995012,"profit_abs":2.5062656641604113e-05,"exit_reason":"roi","initial_stop_loss_abs":2.7234e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.7234e-05,"stop_loss_ratio":0.1,"min_rate":3.026e-05,"max_rate":3.101839598997494e-05,"is_open":false,"open_timestamp":1516637100000.0,"close_timestamp":1516638300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.014156285390713478,"open_date":"2018-01-22 19:50:00+00:00","close_date":"2018-01-23 00:10:00+00:00","open_rate":0.07064,"close_rate":0.07099408521303258,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":260,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.063576,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.063576,"stop_loss_ratio":0.1,"min_rate":0.07064,"max_rate":0.07099408521303258,"is_open":false,"open_timestamp":1516650600000.0,"close_timestamp":1516666200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.06080938507725528,"open_date":"2018-01-22 21:25:00+00:00","close_date":"2018-01-22 22:05:00+00:00","open_rate":0.01644483,"close_rate":0.01652726022556391,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.014800347,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.014800347,"stop_loss_ratio":0.1,"min_rate":0.01644483,"max_rate":0.01652726022556391,"is_open":false,"open_timestamp":1516656300000.0,"close_timestamp":1516658700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":23.08935580697299,"open_date":"2018-01-23 00:05:00+00:00","close_date":"2018-01-23 00:35:00+00:00","open_rate":4.331e-05,"close_rate":4.3961278195488714e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":3.8979e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":3.8979e-05,"stop_loss_ratio":0.1,"min_rate":4.331e-05,"max_rate":4.3961278195488714e-05,"is_open":false,"open_timestamp":1516665900000.0,"close_timestamp":1516667700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":31.250000000000004,"open_date":"2018-01-23 01:50:00+00:00","close_date":"2018-01-23 02:15:00+00:00","open_rate":3.2e-05,"close_rate":3.2802005012531326e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":25,"profit_ratio":0.01995012,"profit_abs":2.5062656641604113e-05,"exit_reason":"roi","initial_stop_loss_abs":2.88e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.88e-05,"stop_loss_ratio":0.1,"min_rate":3.2e-05,"max_rate":3.2802005012531326e-05,"is_open":false,"open_timestamp":1516672200000.0,"close_timestamp":1516673700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.010907854156754156,"open_date":"2018-01-23 04:25:00+00:00","close_date":"2018-01-23 05:15:00+00:00","open_rate":0.09167706,"close_rate":0.09213659413533835,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":50,"profit_ratio":0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.08250935400000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.08250935400000001,"stop_loss_ratio":0.1,"min_rate":0.09167706,"max_rate":0.09213659413533835,"is_open":false,"open_timestamp":1516681500000.0,"close_timestamp":1516684500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.014440474918339117,"open_date":"2018-01-23 07:35:00+00:00","close_date":"2018-01-23 09:00:00+00:00","open_rate":0.0692498,"close_rate":0.06959691679197995,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":85,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.06232482,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.06232482,"stop_loss_ratio":0.1,"min_rate":0.0692498,"max_rate":0.06959691679197995,"is_open":false,"open_timestamp":1516692900000.0,"close_timestamp":1516698000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":31.426775612822127,"open_date":"2018-01-23 10:50:00+00:00","close_date":"2018-01-23 13:05:00+00:00","open_rate":3.182e-05,"close_rate":3.197949874686716e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":135,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":2.8638e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.8638e-05,"stop_loss_ratio":0.1,"min_rate":3.182e-05,"max_rate":3.197949874686716e-05,"is_open":false,"open_timestamp":1516704600000.0,"close_timestamp":1516712700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.024461839530332683,"open_date":"2018-01-23 11:05:00+00:00","close_date":"2018-01-23 16:05:00+00:00","open_rate":0.04088,"close_rate":0.04108491228070175,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":300,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.036792,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.036792,"stop_loss_ratio":0.1,"min_rate":0.04088,"max_rate":0.04108491228070175,"is_open":false,"open_timestamp":1516705500000.0,"close_timestamp":1516723500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":19.417475728155345,"open_date":"2018-01-23 14:55:00+00:00","close_date":"2018-01-23 15:35:00+00:00","open_rate":5.15e-05,"close_rate":5.175814536340851e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.635e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.635e-05,"stop_loss_ratio":0.1,"min_rate":5.15e-05,"max_rate":5.175814536340851e-05,"is_open":false,"open_timestamp":1516719300000.0,"close_timestamp":1516721700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.011023294646713328,"open_date":"2018-01-23 16:35:00+00:00","close_date":"2018-01-24 00:05:00+00:00","open_rate":0.09071698,"close_rate":0.09117170170426064,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":450,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.081645282,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.081645282,"stop_loss_ratio":0.1,"min_rate":0.09071698,"max_rate":0.09117170170426064,"is_open":false,"open_timestamp":1516725300000.0,"close_timestamp":1516752300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":31.969309462915604,"open_date":"2018-01-23 17:25:00+00:00","close_date":"2018-01-23 18:45:00+00:00","open_rate":3.128e-05,"close_rate":3.1436791979949865e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":80,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":2.8152e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.8152e-05,"stop_loss_ratio":0.1,"min_rate":3.128e-05,"max_rate":3.1436791979949865e-05,"is_open":false,"open_timestamp":1516728300000.0,"close_timestamp":1516733100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":10.465724751439035,"open_date":"2018-01-23 20:15:00+00:00","close_date":"2018-01-23 22:00:00+00:00","open_rate":9.555e-05,"close_rate":9.602894736842104e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":105,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":8.5995e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.5995e-05,"stop_loss_ratio":0.1,"min_rate":9.555e-05,"max_rate":9.602894736842104e-05,"is_open":false,"open_timestamp":1516738500000.0,"close_timestamp":1516744800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.02450979791426522,"open_date":"2018-01-23 22:30:00+00:00","close_date":"2018-01-23 23:10:00+00:00","open_rate":0.04080001,"close_rate":0.0410045213283208,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":0.036720009,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.036720009,"stop_loss_ratio":0.1,"min_rate":0.04080001,"max_rate":0.0410045213283208,"is_open":false,"open_timestamp":1516746600000.0,"close_timestamp":1516749000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":19.36858415649816,"open_date":"2018-01-23 23:50:00+00:00","close_date":"2018-01-24 03:35:00+00:00","open_rate":5.163e-05,"close_rate":5.18887969924812e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":225,"profit_ratio":-0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":4.6467e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6467e-05,"stop_loss_ratio":0.1,"min_rate":5.163e-05,"max_rate":5.18887969924812e-05,"is_open":false,"open_timestamp":1516751400000.0,"close_timestamp":1516764900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.024747691102289384,"open_date":"2018-01-24 00:20:00+00:00","close_date":"2018-01-24 01:50:00+00:00","open_rate":0.04040781,"close_rate":0.04061035541353383,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":90,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.036367029,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.036367029,"stop_loss_ratio":0.1,"min_rate":0.04040781,"max_rate":0.04061035541353383,"is_open":false,"open_timestamp":1516753200000.0,"close_timestamp":1516758600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":19.485580670303975,"open_date":"2018-01-24 06:45:00+00:00","close_date":"2018-01-24 07:25:00+00:00","open_rate":5.132e-05,"close_rate":5.157724310776942e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.6188000000000006e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6188000000000006e-05,"stop_loss_ratio":0.1,"min_rate":5.132e-05,"max_rate":5.157724310776942e-05,"is_open":false,"open_timestamp":1516776300000.0,"close_timestamp":1516778700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":19.23816852635629,"open_date":"2018-01-24 14:15:00+00:00","close_date":"2018-01-24 14:25:00+00:00","open_rate":5.198e-05,"close_rate":5.432496240601503e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":10,"profit_ratio":0.03990025,"profit_abs":4.5112781954887056e-05,"exit_reason":"roi","initial_stop_loss_abs":4.6782e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6782e-05,"stop_loss_ratio":0.1,"min_rate":5.198e-05,"max_rate":5.432496240601503e-05,"is_open":false,"open_timestamp":1516803300000.0,"close_timestamp":1516803900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":32.74394237066143,"open_date":"2018-01-24 14:50:00+00:00","close_date":"2018-01-24 16:35:00+00:00","open_rate":3.054e-05,"close_rate":3.069308270676692e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":105,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":2.7486000000000004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.7486000000000004e-05,"stop_loss_ratio":0.1,"min_rate":3.054e-05,"max_rate":3.069308270676692e-05,"is_open":false,"open_timestamp":1516805400000.0,"close_timestamp":1516811700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":10.795638562020944,"open_date":"2018-01-24 15:10:00+00:00","close_date":"2018-01-24 16:15:00+00:00","open_rate":9.263e-05,"close_rate":9.309431077694236e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":65,"profit_ratio":0.0,"profit_abs":5.012531328320953e-06,"exit_reason":"roi","initial_stop_loss_abs":8.3367e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.3367e-05,"stop_loss_ratio":0.1,"min_rate":9.263e-05,"max_rate":9.309431077694236e-05,"is_open":false,"open_timestamp":1516806600000.0,"close_timestamp":1516810500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":18.13565469713457,"open_date":"2018-01-24 22:40:00+00:00","close_date":"2018-01-24 23:25:00+00:00","open_rate":5.514e-05,"close_rate":5.54163909774436e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.962599999999999e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.962599999999999e-05,"stop_loss_ratio":0.1,"min_rate":5.514e-05,"max_rate":5.54163909774436e-05,"is_open":false,"open_timestamp":1516833600000.0,"close_timestamp":1516836300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":20.3210729526519,"open_date":"2018-01-25 00:50:00+00:00","close_date":"2018-01-25 01:30:00+00:00","open_rate":4.921e-05,"close_rate":4.9456666666666664e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.4289e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.4289e-05,"stop_loss_ratio":0.1,"min_rate":4.921e-05,"max_rate":4.9456666666666664e-05,"is_open":false,"open_timestamp":1516841400000.0,"close_timestamp":1516843800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.38461538461538464,"open_date":"2018-01-25 08:15:00+00:00","close_date":"2018-01-25 12:15:00+00:00","open_rate":0.0026,"close_rate":0.002613032581453634,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":240,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.00234,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.00234,"stop_loss_ratio":0.1,"min_rate":0.0026,"max_rate":0.002613032581453634,"is_open":false,"open_timestamp":1516868100000.0,"close_timestamp":1516882500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.03571593119825878,"open_date":"2018-01-25 10:25:00+00:00","close_date":"2018-01-25 16:15:00+00:00","open_rate":0.02799871,"close_rate":0.028139054411027563,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":350,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.025198839,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025198839,"stop_loss_ratio":0.1,"min_rate":0.02799871,"max_rate":0.028139054411027563,"is_open":false,"open_timestamp":1516875900000.0,"close_timestamp":1516896900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.024516401717913305,"open_date":"2018-01-25 11:00:00+00:00","close_date":"2018-01-25 11:45:00+00:00","open_rate":0.04078902,"close_rate":0.0409934762406015,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.036710118,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.036710118,"stop_loss_ratio":0.1,"min_rate":0.04078902,"max_rate":0.0409934762406015,"is_open":false,"open_timestamp":1516878000000.0,"close_timestamp":1516880700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"NXT/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":34.602076124567475,"open_date":"2018-01-25 13:05:00+00:00","close_date":"2018-01-25 13:45:00+00:00","open_rate":2.89e-05,"close_rate":2.904486215538847e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":2.601e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":2.601e-05,"stop_loss_ratio":0.1,"min_rate":2.89e-05,"max_rate":2.904486215538847e-05,"is_open":false,"open_timestamp":1516885500000.0,"close_timestamp":1516887900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.02432912439481303,"open_date":"2018-01-25 13:20:00+00:00","close_date":"2018-01-25 14:05:00+00:00","open_rate":0.041103,"close_rate":0.04130903007518797,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0369927,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0369927,"stop_loss_ratio":0.1,"min_rate":0.041103,"max_rate":0.04130903007518797,"is_open":false,"open_timestamp":1516886400000.0,"close_timestamp":1516889100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":18.42299189388357,"open_date":"2018-01-25 15:45:00+00:00","close_date":"2018-01-25 16:15:00+00:00","open_rate":5.428e-05,"close_rate":5.509624060150376e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":4.8852000000000006e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.8852000000000006e-05,"stop_loss_ratio":0.1,"min_rate":5.428e-05,"max_rate":5.509624060150376e-05,"is_open":false,"open_timestamp":1516895100000.0,"close_timestamp":1516896900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":18.47063169560399,"open_date":"2018-01-25 17:45:00+00:00","close_date":"2018-01-25 23:15:00+00:00","open_rate":5.414e-05,"close_rate":5.441137844611528e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":330,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.8726e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.8726e-05,"stop_loss_ratio":0.1,"min_rate":5.414e-05,"max_rate":5.441137844611528e-05,"is_open":false,"open_timestamp":1516902300000.0,"close_timestamp":1516922100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.02415005686130888,"open_date":"2018-01-25 21:15:00+00:00","close_date":"2018-01-25 21:55:00+00:00","open_rate":0.04140777,"close_rate":0.0416153277443609,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.037266993000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.037266993000000005,"stop_loss_ratio":0.1,"min_rate":0.04140777,"max_rate":0.0416153277443609,"is_open":false,"open_timestamp":1516914900000.0,"close_timestamp":1516917300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.3932224183965176,"open_date":"2018-01-26 02:05:00+00:00","close_date":"2018-01-26 02:45:00+00:00","open_rate":0.00254309,"close_rate":0.002555837318295739,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002288781,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002288781,"stop_loss_ratio":0.1,"min_rate":0.00254309,"max_rate":0.002555837318295739,"is_open":false,"open_timestamp":1516932300000.0,"close_timestamp":1516934700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":17.834849295523455,"open_date":"2018-01-26 02:55:00+00:00","close_date":"2018-01-26 15:10:00+00:00","open_rate":5.607e-05,"close_rate":5.6351052631578935e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":735,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":5.0463e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.0463e-05,"stop_loss_ratio":0.1,"min_rate":5.607e-05,"max_rate":5.6351052631578935e-05,"is_open":false,"open_timestamp":1516935300000.0,"close_timestamp":1516979400000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.39400171784748983,"open_date":"2018-01-26 06:10:00+00:00","close_date":"2018-01-26 09:25:00+00:00","open_rate":0.00253806,"close_rate":0.0025507821052631577,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":195,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002284254,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002284254,"stop_loss_ratio":0.1,"min_rate":0.00253806,"max_rate":0.0025507821052631577,"is_open":false,"open_timestamp":1516947000000.0,"close_timestamp":1516958700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.024096385542168672,"open_date":"2018-01-26 07:25:00+00:00","close_date":"2018-01-26 09:55:00+00:00","open_rate":0.0415,"close_rate":0.04170802005012531,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":150,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.03735,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.03735,"stop_loss_ratio":0.1,"min_rate":0.0415,"max_rate":0.04170802005012531,"is_open":false,"open_timestamp":1516951500000.0,"close_timestamp":1516960500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":18.793459875963165,"open_date":"2018-01-26 09:55:00+00:00","close_date":"2018-01-26 10:25:00+00:00","open_rate":5.321e-05,"close_rate":5.401015037593984e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":4.7889e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.7889e-05,"stop_loss_ratio":0.1,"min_rate":5.321e-05,"max_rate":5.401015037593984e-05,"is_open":false,"open_timestamp":1516960500000.0,"close_timestamp":1516962300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.036074437437185386,"open_date":"2018-01-26 16:05:00+00:00","close_date":"2018-01-26 16:45:00+00:00","open_rate":0.02772046,"close_rate":0.02785940967418546,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.024948414,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.024948414,"stop_loss_ratio":0.1,"min_rate":0.02772046,"max_rate":0.02785940967418546,"is_open":false,"open_timestamp":1516982700000.0,"close_timestamp":1516985100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.010569326272036914,"open_date":"2018-01-26 23:35:00+00:00","close_date":"2018-01-27 00:15:00+00:00","open_rate":0.09461341,"close_rate":0.09508766268170424,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.085152069,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.085152069,"stop_loss_ratio":0.1,"min_rate":0.09461341,"max_rate":0.09508766268170424,"is_open":false,"open_timestamp":1517009700000.0,"close_timestamp":1517012100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":17.809439002671414,"open_date":"2018-01-27 00:35:00+00:00","close_date":"2018-01-27 01:30:00+00:00","open_rate":5.615e-05,"close_rate":5.643145363408521e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":55,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":5.0535e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.0535e-05,"stop_loss_ratio":0.1,"min_rate":5.615e-05,"max_rate":5.643145363408521e-05,"is_open":false,"open_timestamp":1517013300000.0,"close_timestamp":1517016600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ADA/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":17.998560115190784,"open_date":"2018-01-27 00:45:00+00:00","close_date":"2018-01-30 04:45:00+00:00","open_rate":5.556e-05,"close_rate":5.144e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":4560,"profit_ratio":-0.07877175,"profit_abs":-7.415406767458598e-05,"exit_reason":"force_exit","initial_stop_loss_abs":5.0004e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":5.0004e-05,"stop_loss_ratio":0.1,"min_rate":5.144e-05,"max_rate":5.556e-05,"is_open":false,"open_timestamp":1517013900000.0,"close_timestamp":1517287500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.014492751522789634,"open_date":"2018-01-27 02:30:00+00:00","close_date":"2018-01-27 11:25:00+00:00","open_rate":0.06900001,"close_rate":0.06934587471177944,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":535,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.062100009000000005,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.062100009000000005,"stop_loss_ratio":0.1,"min_rate":0.06900001,"max_rate":0.06934587471177944,"is_open":false,"open_timestamp":1517020200000.0,"close_timestamp":1517052300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.010582027378879436,"open_date":"2018-01-27 06:25:00+00:00","close_date":"2018-01-27 07:05:00+00:00","open_rate":0.09449985,"close_rate":0.0949735334586466,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.085049865,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.085049865,"stop_loss_ratio":0.1,"min_rate":0.09449985,"max_rate":0.0949735334586466,"is_open":false,"open_timestamp":1517034300000.0,"close_timestamp":1517036700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ZEC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.02434885085598385,"open_date":"2018-01-27 09:40:00+00:00","close_date":"2018-01-30 04:40:00+00:00","open_rate":0.0410697,"close_rate":0.03928809,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":4020,"profit_ratio":-0.04815133,"profit_abs":-4.338015617352949e-05,"exit_reason":"force_exit","initial_stop_loss_abs":0.03696273,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.03696273,"stop_loss_ratio":0.1,"min_rate":0.03928809,"max_rate":0.0410697,"is_open":false,"open_timestamp":1517046000000.0,"close_timestamp":1517287200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.03508771929824561,"open_date":"2018-01-27 11:45:00+00:00","close_date":"2018-01-27 12:30:00+00:00","open_rate":0.0285,"close_rate":0.02864285714285714,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.025650000000000003,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025650000000000003,"stop_loss_ratio":0.1,"min_rate":0.0285,"max_rate":0.02864285714285714,"is_open":false,"open_timestamp":1517053500000.0,"close_timestamp":1517056200000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XMR/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.034887307020861215,"open_date":"2018-01-27 12:35:00+00:00","close_date":"2018-01-27 15:25:00+00:00","open_rate":0.02866372,"close_rate":0.02880739779448621,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":170,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.025797348,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.025797348,"stop_loss_ratio":0.1,"min_rate":0.02866372,"max_rate":0.02880739779448621,"is_open":false,"open_timestamp":1517056500000.0,"close_timestamp":1517066700000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.010484268355332824,"open_date":"2018-01-27 15:50:00+00:00","close_date":"2018-01-27 16:50:00+00:00","open_rate":0.095381,"close_rate":0.09585910025062656,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.0858429,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.0858429,"stop_loss_ratio":0.1,"min_rate":0.095381,"max_rate":0.09585910025062656,"is_open":false,"open_timestamp":1517068200000.0,"close_timestamp":1517071800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.014794886650455415,"open_date":"2018-01-27 17:05:00+00:00","close_date":"2018-01-27 17:45:00+00:00","open_rate":0.06759092,"close_rate":0.06792972160401002,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.060831828,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.060831828,"stop_loss_ratio":0.1,"min_rate":0.06759092,"max_rate":0.06792972160401002,"is_open":false,"open_timestamp":1517072700000.0,"close_timestamp":1517075100000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.38684569885609726,"open_date":"2018-01-27 23:40:00+00:00","close_date":"2018-01-28 01:05:00+00:00","open_rate":0.00258501,"close_rate":0.002597967443609022,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":85,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.002326509,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002326509,"stop_loss_ratio":0.1,"min_rate":0.00258501,"max_rate":0.002597967443609022,"is_open":false,"open_timestamp":1517096400000.0,"close_timestamp":1517101500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.014928710926711672,"open_date":"2018-01-28 02:25:00+00:00","close_date":"2018-01-28 08:10:00+00:00","open_rate":0.06698502,"close_rate":0.0673207845112782,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":345,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.060286518,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.060286518,"stop_loss_ratio":0.1,"min_rate":0.06698502,"max_rate":0.0673207845112782,"is_open":false,"open_timestamp":1517106300000.0,"close_timestamp":1517127000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.014767187899175548,"open_date":"2018-01-28 10:25:00+00:00","close_date":"2018-01-28 16:30:00+00:00","open_rate":0.0677177,"close_rate":0.06805713709273183,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":365,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.06094593000000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.06094593000000001,"stop_loss_ratio":0.1,"min_rate":0.0677177,"max_rate":0.06805713709273183,"is_open":false,"open_timestamp":1517135100000.0,"close_timestamp":1517157000000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"XLM/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":19.175455417066157,"open_date":"2018-01-28 20:35:00+00:00","close_date":"2018-01-28 21:35:00+00:00","open_rate":5.215e-05,"close_rate":5.2411403508771925e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":60,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":4.6935e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":4.6935e-05,"stop_loss_ratio":0.1,"min_rate":5.215e-05,"max_rate":5.2411403508771925e-05,"is_open":false,"open_timestamp":1517171700000.0,"close_timestamp":1517175300000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.36521808998243305,"open_date":"2018-01-28 22:00:00+00:00","close_date":"2018-01-28 22:30:00+00:00","open_rate":0.00273809,"close_rate":0.002779264285714285,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962207e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002464281,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002464281,"stop_loss_ratio":0.1,"min_rate":0.00273809,"max_rate":0.002779264285714285,"is_open":false,"open_timestamp":1517176800000.0,"close_timestamp":1517178600000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"ETC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.3641236272539253,"open_date":"2018-01-29 00:00:00+00:00","close_date":"2018-01-29 00:30:00+00:00","open_rate":0.00274632,"close_rate":0.002787618045112782,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":30,"profit_ratio":0.00997506,"profit_abs":1.5037593984962424e-05,"exit_reason":"roi","initial_stop_loss_abs":0.002471688,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.002471688,"stop_loss_ratio":0.1,"min_rate":0.00274632,"max_rate":0.002787618045112782,"is_open":false,"open_timestamp":1517184000000.0,"close_timestamp":1517185800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"LTC/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.061634117689115045,"open_date":"2018-01-29 02:15:00+00:00","close_date":"2018-01-29 03:00:00+00:00","open_rate":0.01622478,"close_rate":0.016306107218045113,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":45,"profit_ratio":0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.014602302,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.014602302,"stop_loss_ratio":0.1,"min_rate":0.01622478,"max_rate":0.016306107218045113,"is_open":false,"open_timestamp":1517192100000.0,"close_timestamp":1517194800000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.014492753623188404,"open_date":"2018-01-29 03:05:00+00:00","close_date":"2018-01-29 03:45:00+00:00","open_rate":0.069,"close_rate":0.06934586466165413,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320519e-06,"exit_reason":"roi","initial_stop_loss_abs":0.06210000000000001,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.06210000000000001,"stop_loss_ratio":0.1,"min_rate":0.069,"max_rate":0.06934586466165413,"is_open":false,"open_timestamp":1517195100000.0,"close_timestamp":1517197500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":11.42204454597373,"open_date":"2018-01-29 05:20:00+00:00","close_date":"2018-01-29 06:55:00+00:00","open_rate":8.755e-05,"close_rate":8.798884711779448e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":95,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":7.879500000000001e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":7.879500000000001e-05,"stop_loss_ratio":0.1,"min_rate":8.755e-05,"max_rate":8.798884711779448e-05,"is_open":false,"open_timestamp":1517203200000.0,"close_timestamp":1517208900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.014650376815016871,"open_date":"2018-01-29 07:00:00+00:00","close_date":"2018-01-29 19:25:00+00:00","open_rate":0.06825763,"close_rate":0.06859977350877192,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":745,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.061431867,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.061431867,"stop_loss_ratio":0.1,"min_rate":0.06825763,"max_rate":0.06859977350877192,"is_open":false,"open_timestamp":1517209200000.0,"close_timestamp":1517253900000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"DASH/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":0.014894490408841846,"open_date":"2018-01-29 19:45:00+00:00","close_date":"2018-01-29 20:25:00+00:00","open_rate":0.06713892,"close_rate":0.06747545593984962,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":40,"profit_ratio":-0.0,"profit_abs":5.012531328320736e-06,"exit_reason":"roi","initial_stop_loss_abs":0.060425028000000006,"initial_stop_loss_ratio":0.1,"stop_loss_abs":0.060425028000000006,"stop_loss_ratio":0.1,"min_rate":0.06713892,"max_rate":0.06747545593984962,"is_open":false,"open_timestamp":1517255100000.0,"close_timestamp":1517257500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null},{"pair":"TRX/BTC","stake_amount":0.001,"max_stake_amount":0.001,"amount":11.193194537721066,"open_date":"2018-01-29 23:30:00+00:00","close_date":"2018-01-30 04:45:00+00:00","open_rate":8.934e-05,"close_rate":8.8e-05,"fee_open":0.0025,"fee_close":0.0025,"trade_duration":315,"profit_ratio":-0.0199116,"profit_abs":-1.4998880680546292e-05,"exit_reason":"force_exit","initial_stop_loss_abs":8.0406e-05,"initial_stop_loss_ratio":0.1,"stop_loss_abs":8.0406e-05,"stop_loss_ratio":0.1,"min_rate":8.8e-05,"max_rate":8.934e-05,"is_open":false,"open_timestamp":1517268600000.0,"close_timestamp":1517287500000.0,"is_short":false,"leverage":1.0,"enter_tag":null,"orders":null}],"locks":[],"best_pair":{"key":"LTC/BTC","trades":8,"profit_mean":0.00748129625,"profit_mean_pct":0.748129625,"profit_sum":0.05985037,"profit_sum_pct":5.99,"profit_total_abs":0.00010025062656641558,"profit_total":0.010025062656641558,"profit_total_pct":1.0,"duration_avg":"1:59:00","wins":8,"draws":0,"losses":0},"worst_pair":{"key":"XMR/BTC","trades":16,"profit_mean":-0.0027899012500000007,"profit_mean_pct":-0.2789901250000001,"profit_sum":-0.04463842000000001,"profit_sum_pct":-4.46,"profit_total_abs":3.533834586465928e-05,"profit_total":0.003533834586465928,"profit_total_pct":0.35,"duration_avg":"8:41:00","wins":15,"draws":0,"losses":1},"results_per_pair":[{"key":"XLM/BTC","trades":21,"profit_mean":0.0026243899999999994,"profit_mean_pct":0.2624389999999999,"profit_sum":0.05511218999999999,"profit_sum_pct":5.51,"profit_total_abs":0.00016065162907268006,"profit_total":0.016065162907268005,"profit_total_pct":1.61,"duration_avg":"3:21:00","wins":20,"draws":0,"losses":1},{"key":"ETC/BTC","trades":20,"profit_mean":0.0022568569999999997,"profit_mean_pct":0.22568569999999996,"profit_sum":0.04513713999999999,"profit_sum_pct":4.51,"profit_total_abs":0.00014561403508771753,"profit_total":0.014561403508771753,"profit_total_pct":1.46,"duration_avg":"1:45:00","wins":19,"draws":0,"losses":1},{"key":"ETH/BTC","trades":21,"profit_mean":0.0009500057142857142,"profit_mean_pct":0.09500057142857142,"profit_sum":0.01995012,"profit_sum_pct":2.0,"profit_total_abs":0.00012531328320801774,"profit_total":0.012531328320801774,"profit_total_pct":1.25,"duration_avg":"2:17:00","wins":21,"draws":0,"losses":0},{"key":"ADA/BTC","trades":29,"profit_mean":-0.0011598141379310352,"profit_mean_pct":-0.11598141379310352,"profit_sum":-0.03363461000000002,"profit_sum_pct":-3.36,"profit_total_abs":0.00011156021803969656,"profit_total":0.011156021803969657,"profit_total_pct":1.12,"duration_avg":"5:35:00","wins":27,"draws":0,"losses":2},{"key":"TRX/BTC","trades":15,"profit_mean":0.0023467073333333323,"profit_mean_pct":0.23467073333333321,"profit_sum":0.035200609999999986,"profit_sum_pct":3.52,"profit_total_abs":0.00011056502909388873,"profit_total":0.011056502909388873,"profit_total_pct":1.11,"duration_avg":"2:28:00","wins":13,"draws":0,"losses":2},{"key":"DASH/BTC","trades":16,"profit_mean":0.0018703237499999997,"profit_mean_pct":0.18703237499999997,"profit_sum":0.029925179999999996,"profit_sum_pct":2.99,"profit_total_abs":0.0001102756892230564,"profit_total":0.01102756892230564,"profit_total_pct":1.1,"duration_avg":"3:03:00","wins":16,"draws":0,"losses":0},{"key":"LTC/BTC","trades":8,"profit_mean":0.00748129625,"profit_mean_pct":0.748129625,"profit_sum":0.05985037,"profit_sum_pct":5.99,"profit_total_abs":0.00010025062656641558,"profit_total":0.010025062656641558,"profit_total_pct":1.0,"duration_avg":"1:59:00","wins":8,"draws":0,"losses":0},{"key":"ZEC/BTC","trades":21,"profit_mean":-0.00039290904761904774,"profit_mean_pct":-0.03929090476190478,"profit_sum":-0.008251090000000003,"profit_sum_pct":-0.83,"profit_total_abs":9.697072101945111e-05,"profit_total":0.009697072101945111,"profit_total_pct":0.97,"duration_avg":"4:17:00","wins":20,"draws":0,"losses":1},{"key":"NXT/BTC","trades":12,"profit_mean":-0.0012261025000000006,"profit_mean_pct":-0.12261025000000006,"profit_sum":-0.014713230000000008,"profit_sum_pct":-1.47,"profit_total_abs":4.536340852130151e-05,"profit_total":0.004536340852130151,"profit_total_pct":0.45,"duration_avg":"0:57:00","wins":11,"draws":0,"losses":1},{"key":"XMR/BTC","trades":16,"profit_mean":-0.0027899012500000007,"profit_mean_pct":-0.2789901250000001,"profit_sum":-0.04463842000000001,"profit_sum_pct":-4.46,"profit_total_abs":3.533834586465928e-05,"profit_total":0.003533834586465928,"profit_total_pct":0.35,"duration_avg":"8:41:00","wins":15,"draws":0,"losses":1},{"key":"TOTAL","trades":179,"profit_mean":0.0008041243575418989,"profit_mean_pct":0.0804124357541899,"profit_sum":0.1439382599999999,"profit_sum_pct":14.39,"profit_total_abs":0.0010419029856968845,"profit_total":0.10419029856968845,"profit_total_pct":10.42,"duration_avg":"3:40:00","wins":170,"draws":0,"losses":9}],"results_per_enter_tag":[{"key":"buy_tag","trades":1,"profit_mean":0.03990025,"profit_mean_pct":3.9900249999999997,"profit_sum":0.03990025,"profit_sum_pct":3.99,"profit_total_abs":4.5112781954887056e-05,"profit_total":0.004511278195488706,"profit_total_pct":0.45,"duration_avg":"0:15:00","wins":1,"draws":0,"losses":0},{"key":"TOTAL","trades":179,"profit_mean":0.0008041243575418989,"profit_mean_pct":0.0804124357541899,"profit_sum":0.1439382599999999,"profit_sum_pct":14.39,"profit_total_abs":0.0010419029856968845,"profit_total":0.10419029856968845,"profit_total_pct":10.42,"duration_avg":"3:40:00","wins":170,"draws":0,"losses":9}],"exit_reason_summary":[{"exit_reason":"roi","trades":170,"wins":170,"draws":0,"losses":0,"profit_mean":0.005398268352941177,"profit_mean_pct":0.54,"profit_sum":0.91770562,"profit_sum_pct":91.77,"profit_total_abs":0.0017744360902255465,"profit_total":0.30590187333333335,"profit_total_pct":30.59},{"exit_reason":"stop_loss","trades":6,"wins":0,"draws":0,"losses":6,"profit_mean":-0.10448878000000002,"profit_mean_pct":-10.45,"profit_sum":-0.6269326800000001,"profit_sum_pct":-62.69,"profit_total_abs":-0.0006000000000000003,"profit_total":-0.20897756000000003,"profit_total_pct":-20.9},{"exit_reason":"force_exit","trades":3,"wins":0,"draws":0,"losses":3,"profit_mean":-0.04894489333333333,"profit_mean_pct":-4.89,"profit_sum":-0.14683468,"profit_sum_pct":-14.68,"profit_total_abs":-0.00013253310452866177,"profit_total":-0.04894489333333333,"profit_total_pct":-4.89}],"left_open_trades":[{"key":"TRX/BTC","trades":1,"profit_mean":-0.0199116,"profit_mean_pct":-1.9911600000000003,"profit_sum":-0.0199116,"profit_sum_pct":-1.99,"profit_total_abs":-1.4998880680546292e-05,"profit_total":-0.0014998880680546292,"profit_total_pct":-0.15,"duration_avg":"5:15:00","wins":0,"draws":0,"losses":1},{"key":"ZEC/BTC","trades":1,"profit_mean":-0.04815133,"profit_mean_pct":-4.815133,"profit_sum":-0.04815133,"profit_sum_pct":-4.82,"profit_total_abs":-4.338015617352949e-05,"profit_total":-0.004338015617352949,"profit_total_pct":-0.43,"duration_avg":"2 days, 19:00:00","wins":0,"draws":0,"losses":1},{"key":"ADA/BTC","trades":1,"profit_mean":-0.07877175,"profit_mean_pct":-7.877175,"profit_sum":-0.07877175,"profit_sum_pct":-7.88,"profit_total_abs":-7.415406767458598e-05,"profit_total":-0.007415406767458598,"profit_total_pct":-0.74,"duration_avg":"3 days, 4:00:00","wins":0,"draws":0,"losses":1},{"key":"TOTAL","trades":3,"profit_mean":-0.04894489333333333,"profit_mean_pct":-4.894489333333333,"profit_sum":-0.14683468,"profit_sum_pct":-14.68,"profit_total_abs":-0.00013253310452866177,"profit_total":-0.013253310452866176,"profit_total_pct":-1.33,"duration_avg":"2 days, 1:25:00","wins":0,"draws":0,"losses":3}],"total_trades":179,"trade_count_long":179,"trade_count_short":0,"total_volume":0.17900000000000005,"avg_stake_amount":0.0010000000000000002,"profit_mean":0.0008041243575418989,"profit_median":0.0,"profit_total":0.10419029856968845,"profit_total_long":0.10419029856968845,"profit_total_short":0.0,"profit_total_abs":0.0010419029856968845,"profit_total_long_abs":0.0010419029856968845,"profit_total_short_abs":0.0,"cagr":5.712688499973264,"profit_factor":2.4223288739520954,"backtest_start":"2018-01-10 07:15:00","backtest_start_ts":1515568500000,"backtest_end":"2018-01-30 04:45:00","backtest_end_ts":1517287500000,"backtest_days":19,"backtest_run_start_ts":"2020-10-01 18:00:00+00:00","backtest_run_end_ts":"2020-10-01 18:01:00+00:00","trades_per_day":9.42,"market_change":1.22,"pairlist":["TRX/BTC","ADA/BTC","XLM/BTC","ETH/BTC","XMR/BTC","ZEC/BTC","NXT/BTC","LTC/BTC","ETC/BTC","DASH/BTC"],"stake_amount":0.001,"stake_currency":"BTC","stake_currency_decimals":8,"starting_balance":0.01,"dry_run_wallet":0.01,"final_balance":0.011041902985696884,"rejected_signals":0,"timedout_entry_orders":0,"timedout_exit_orders":0,"canceled_trade_entries":0,"canceled_entry_orders":0,"replaced_entry_orders":0,"max_open_trades":3,"max_open_trades_setting":3,"timeframe":"5m","timeframe_detail":"","timerange":"","enable_protections":false,"strategy_name":"StrategyTestV3","stoploss":0.1,"trailing_stop":false,"trailing_stop_positive":null,"trailing_stop_positive_offset":0.0,"trailing_only_offset_is_reached":false,"use_custom_stoploss":false,"minimal_roi":{},"use_exit_signal":true,"exit_profit_only":false,"exit_profit_offset":false,"ignore_roi_if_entry_signal":false,"backtest_best_day":0.17955111999999998,"backtest_worst_day":-0.14683468,"backtest_best_day_abs":0.000245614,"backtest_worst_day_abs":-0.0001325331,"winning_days":19,"draw_days":0,"losing_days":2,"daily_profit":[["2018-01-10",0.000245614],["2018-01-11",0.0001055138],["2018-01-12",4.51128e-05],["2018-01-13",3.00752e-05],["2018-01-14",3.50877e-05],["2018-01-15",6.51629e-05],["2018-01-16",5.11278e-05],["2018-01-17",7.01754e-05],["2018-01-18",8.5213e-05],["2018-01-19",3.00752e-05],["2018-01-20",2.50627e-05],["2018-01-21",4.01003e-05],["2018-01-22",7.01754e-05],["2018-01-23",8.5213e-05],["2018-01-24",8.02005e-05],["2018-01-25",-4.48622e-05],["2018-01-26",4.01003e-05],["2018-01-27",4.01003e-05],["2018-01-28",3.50877e-05],["2018-01-29",4.01003e-05],["2018-01-30",-0.0001325331]],"wins":48,"losses":9,"draws":122,"holding_avg":"3:40:00","holding_avg_s":13200.0,"winner_holding_avg":"0:24:00","winner_holding_avg_s":1440.0,"loser_holding_avg":"1 day, 5:57:00","loser_holding_avg_s":107820.0,"max_drawdown":0.21142322000000008,"max_drawdown_account":0.018740312808228732,"max_relative_drawdown":0.018740312808228732,"max_drawdown_abs":0.0002000000000000001,"drawdown_start":"2018-01-16 19:30:00","drawdown_start_ts":1516131000000.0,"drawdown_end":"2018-01-16 22:25:00","drawdown_end_ts":1516141500000.0,"max_drawdown_low":0.0004721804511278108,"max_drawdown_high":0.0006721804511278109,"csum_min":0.010045112781954888,"csum_max":0.011069172932330812}},"strategy_comparison":[{"key":"StrategyTestV3","trades":179,"profit_mean":0.0008041243575418989,"profit_mean_pct":0.0804124357541899,"profit_sum":0.1439382599999999,"profit_sum_pct":14.39,"profit_total_abs":0.0010419029856968845,"profit_total":0.10419029856968845,"profit_total_pct":10.42,"duration_avg":"3:40:00","wins":170,"draws":0,"losses":9,"max_drawdown_account":0.018740312808228732,"max_drawdown_abs":"0.0002"}]} From 70e9fa6136a0d7705dfe6a72e0315467d6c0a838 Mon Sep 17 00:00:00 2001 From: hippocritical Date: Tue, 27 Dec 2022 20:14:39 +0100 Subject: [PATCH 155/795] implementing the strategy_updater in a first version --- freqtrade/commands/__init__.py | 1 + freqtrade/commands/arguments.py | 14 +- freqtrade/commands/strategy_utils_commands.py | 37 +++ freqtrade/strategy/strategy_updater.py | 213 ++++++++++++++++++ 4 files changed, 263 insertions(+), 2 deletions(-) create mode 100644 freqtrade/commands/strategy_utils_commands.py create mode 100644 freqtrade/strategy/strategy_updater.py diff --git a/freqtrade/commands/__init__.py b/freqtrade/commands/__init__.py index 788657cc8..66a9c995b 100644 --- a/freqtrade/commands/__init__.py +++ b/freqtrade/commands/__init__.py @@ -22,5 +22,6 @@ from freqtrade.commands.optimize_commands import (start_backtesting, start_backt start_edge, start_hyperopt) from freqtrade.commands.pairlist_commands import start_test_pairlist from freqtrade.commands.plot_commands import start_plot_dataframe, start_plot_profit +from freqtrade.commands.strategy_utils_commands import start_strategy_update from freqtrade.commands.trade_commands import start_trading from freqtrade.commands.webserver_commands import start_webserver diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index b53a1022d..9990ad230 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -115,6 +115,8 @@ NO_CONF_REQURIED = ["convert-data", "convert-trade-data", "download-data", "list NO_CONF_ALLOWED = ["create-userdir", "list-exchanges", "new-strategy"] +ARGS_STRATEGY_UTILS = ARGS_COMMON_OPTIMIZE + ["strategy_list"] + class Arguments: """ @@ -198,8 +200,8 @@ class Arguments: start_list_freqAI_models, start_list_markets, start_list_strategies, start_list_timeframes, start_new_config, start_new_strategy, start_plot_dataframe, - start_plot_profit, start_show_trades, start_test_pairlist, - start_trading, start_webserver) + start_plot_profit, start_show_trades, start_strategy_update, + start_test_pairlist, start_trading, start_webserver) subparsers = self.parser.add_subparsers(dest='command', # Use custom message when no subhandler is added @@ -440,3 +442,11 @@ class Arguments: parents=[_common_parser]) webserver_cmd.set_defaults(func=start_webserver) self._build_args(optionlist=ARGS_WEBSERVER, parser=webserver_cmd) + + # Add strategy_updater subcommand + strategy_updater_cmd = subparsers.add_parser('strategy-updater', + help='updates outdated strategy' + 'files to the current version', + parents=[_common_parser]) + strategy_updater_cmd.set_defaults(func=start_strategy_update) + self._build_args(optionlist=ARGS_STRATEGY_UTILS, parser=strategy_updater_cmd) diff --git a/freqtrade/commands/strategy_utils_commands.py b/freqtrade/commands/strategy_utils_commands.py new file mode 100644 index 000000000..b9431fecf --- /dev/null +++ b/freqtrade/commands/strategy_utils_commands.py @@ -0,0 +1,37 @@ +import logging +from typing import Any, Dict + +from freqtrade.configuration import setup_utils_configuration +from freqtrade.enums import RunMode +from freqtrade.resolvers import StrategyResolver + + +logger = logging.getLogger(__name__) + + +def start_strategy_update(args: Dict[str, Any]) -> None: + """ + Start the strategy updating script + :param args: Cli args from Arguments() + :return: None + """ + + # Import here to avoid loading backtesting module when it's not used + from freqtrade.strategy.strategy_updater import strategy_updater + + config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE) + + strategy_objs = StrategyResolver.search_all_objects( + config, enum_failed=True, recursive=config.get('recursive_strategy_search', False)) + + filtered_strategy_objs = [] + for args_strategy in args['strategy_list']: + for strategy_obj in strategy_objs: + if strategy_obj['name'] == args_strategy and strategy_obj not in filtered_strategy_objs: + filtered_strategy_objs.append(strategy_obj) + break + + for filtered_strategy_obj in filtered_strategy_objs: + # Initialize backtesting object + instance_strategy_updater = strategy_updater() + strategy_updater.start(instance_strategy_updater, filtered_strategy_obj) diff --git a/freqtrade/strategy/strategy_updater.py b/freqtrade/strategy/strategy_updater.py new file mode 100644 index 000000000..fea8edf31 --- /dev/null +++ b/freqtrade/strategy/strategy_updater.py @@ -0,0 +1,213 @@ +import ast +import os +import shutil + + +class strategy_updater: + name_mapping = { + 'ticker_interval': 'timeframe', + 'buy': 'enter_long', + 'sell': 'exit_long', + 'buy_tag': 'enter_tag', + 'sell_reason': 'exit_reason', + + 'sell_signal': 'exit_signal', + 'custom_sell': 'custom_exit', + 'force_sell': 'force_exit', + 'emergency_sell': 'emergency_exit', + + # Strategy/config settings: + 'use_sell_signal': 'use_exit_signal', + 'sell_profit_only': 'exit_profit_only', + 'sell_profit_offset': 'exit_profit_offset', + 'ignore_roi_if_buy_signal': 'ignore_roi_if_entry_signal', + 'forcebuy_enable': 'force_entry_enable', + } + + function_mapping = { + 'populate_buy_trend': 'populate_entry_trend', + 'populate_sell_trend': 'populate_exit_trend', + 'custom_sell': 'custom_exit', + 'check_buy_timeout': 'check_entry_timeout', + 'check_sell_timeout': 'check_exit_timeout', + # '': '', + } + # order_time_in_force, order_types, unfilledtimeout + otif_ot_unfilledtimeout = { + 'buy': 'entry', + 'sell': 'exit', + } + + # create a dictionary that maps the old column names to the new ones + rename_dict = {'buy': 'enter_long', 'sell': 'exit_long', 'buy_tag': 'enter_tag'} + + def start(self, strategy_obj: dict) -> None: + """ + Run strategy updater + It updates a strategy to v3 with the help of the ast-module + :return: None + """ + + self.cwd = os.getcwd() + self.strategies_backup_folder = f'{os.getcwd()}user_data/strategies_orig_updater' + source_file = strategy_obj['location'] + + # read the file + with open(source_file, 'r') as f: + old_code = f.read() + if not os.path.exists(self.strategies_backup_folder): + os.makedirs(self.strategies_backup_folder) + + # backup original + # => currently no date after the filename, + # could get overridden pretty fast if this is fired twice! + shutil.copy(source_file, f"{self.strategies_backup_folder}/{strategy_obj['location_rel']}") + + # update the code + new_code = strategy_updater.update_code(self, old_code, + strategy_updater.name_mapping, + strategy_updater.function_mapping, + strategy_updater.rename_dict) + + # write the modified code to the destination folder + with open(source_file, 'w') as f: + f.write(new_code) + print(f"conversion of file {source_file} successful.") + + # define the function to update the code + def update_code(self, code, _name_mapping, _function_mapping, _rename_dict): + # parse the code into an AST + tree = ast.parse(code) + + # use the AST to update the code + updated_code = strategy_updater.modify_ast( + tree, + _name_mapping, + _function_mapping, + _rename_dict) + + # return the modified code without executing it + return updated_code + + # function that uses the ast module to update the code + def modify_ast(node, _name_mapping, _function_mapping, _rename_dict): # noqa + # create a visitor that will update the names and functions + class NameUpdater(ast.NodeTransformer): + def generic_visit(self, node): + # traverse the AST recursively by calling the visitor method for each child node + if hasattr(node, "_fields"): + for field_name, field_value in ast.iter_fields(node): + self.check_fields(field_value) + self.check_strategy_and_config_settings(field_value) + # add this check to handle the case where field_value is a slice + if isinstance(field_value, ast.Slice): + self.visit(field_value) + # add this check to handle the case where field_value is a target + if isinstance(field_value, ast.expr_context): + self.visit(field_value) + + def check_fields(self, field_value): + if isinstance(field_value, list): + for item in field_value: + if isinstance(item, ast.AST): + self.visit(item) + + def check_strategy_and_config_settings(self, field_value): + if (isinstance(field_value, ast.AST) and + hasattr(node, "targets") and + isinstance(node.targets, list)): + for target in node.targets: + if (hasattr(target, "id") and + (target.id == "order_time_in_force" or + target.id == "order_types" or + target.id == "unfilledtimeout") and + hasattr(field_value, "keys") and + isinstance(field_value.keys, list)): + for key in field_value.keys: + self.visit(key) + + def visit_Name(self, node): + # if the name is in the mapping, update it + if node.id in _name_mapping: + node.id = _name_mapping[node.id] + return node + + def visit_Import(self, node): + # do not update the names in import statements + return node + + def visit_ImportFrom(self, node): + # do not update the names in import statements + if hasattr(node, "module"): + if node.module == "freqtrade.strategy.hyper": + node.module = "freqtrade.strategy" + return node + + def visit_FunctionDef(self, node): + # if the function name is in the mapping, update it + if node.name in _function_mapping: + node.name = _function_mapping[node.name] + return self.generic_visit(node) + + def visit_Attribute(self, node): + # if the attribute name is 'nr_of_successful_buys', + # update it to 'nr_of_successful_entries' + if isinstance(node.value, ast.Name) and \ + node.value.id == 'trades' and \ + node.attr == 'nr_of_successful_buys': + node.attr = 'nr_of_successful_entries' + return self.generic_visit(node) + + def visit_ClassDef(self, node): + # check if the class is derived from IStrategy + if any(isinstance(base, ast.Name) and + base.id == 'IStrategy' for base in node.bases): + # check if the INTERFACE_VERSION variable exists + has_interface_version = any( + isinstance(child, ast.Assign) and + isinstance(child.targets[0], ast.Name) and + child.targets[0].id == 'INTERFACE_VERSION' + for child in node.body + ) + + # if the INTERFACE_VERSION variable does not exist, add it as the first child + if not has_interface_version: + node.body.insert(0, ast.parse('INTERFACE_VERSION = 3').body[0]) + # otherwise, update its value to 3 + else: + for child in node.body: + if isinstance(child, ast.Assign) and \ + isinstance(child.targets[0], ast.Name) and \ + child.targets[0].id == 'INTERFACE_VERSION': + child.value = ast.parse('3').body[0].value + return self.generic_visit(node) + + def visit_Subscript(self, node): + if isinstance(node.slice, ast.Constant): + if node.slice.value in strategy_updater.rename_dict: + # Replace the slice attributes with the values from rename_dict + node.slice.value = strategy_updater.rename_dict[node.slice.value] + if hasattr(node.slice, "elts"): + for elt in node.slice.elts: + if isinstance(elt, ast.Constant) and \ + elt.value in strategy_updater.rename_dict: + elt.value = strategy_updater.rename_dict[elt.value] + return node + + def visit_Constant(self, node): + # do not update the names in import statements + if node.value in \ + strategy_updater.otif_ot_unfilledtimeout: + node.value = \ + strategy_updater.otif_ot_unfilledtimeout[node.value] + return node + + # use the visitor to update the names and functions in the AST + NameUpdater().visit(node) + + # first fix the comments so it understands "\n" properly inside multi line comments. + ast.fix_missing_locations(node) + ast.increment_lineno(node, n=1) + + # generate the new code from the updated AST + return ast.unparse(node) From 4d112def172cb9a34520f2876b825c486d7ccbef Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 28 Dec 2022 07:10:11 +0100 Subject: [PATCH 156/795] Remove binance AD from docs page fixes #7921 --- docs/overrides/main.html | 24 ------------------------ docs/stylesheets/ft.extra.css | 15 --------------- 2 files changed, 39 deletions(-) diff --git a/docs/overrides/main.html b/docs/overrides/main.html index dfc5264be..cba627ead 100644 --- a/docs/overrides/main.html +++ b/docs/overrides/main.html @@ -11,9 +11,6 @@ {% endif %}