diff --git a/docs/backtesting.md b/docs/backtesting.md index 13d19f0ca..3da76c0ce 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -79,18 +79,18 @@ freqtrade backtesting --datadir user_data/data/bittrex-20180101 #### With a (custom) strategy file ```bash -freqtrade -s TestStrategy backtesting +freqtrade -s SampleStrategy backtesting ``` -Where `-s TestStrategy` refers to the class name within the strategy file `test_strategy.py` found in the `freqtrade/user_data/strategies` directory. +Where `-s SampleStrategy` refers to the class name within the strategy file `sample_strategy.py` found in the `freqtrade/user_data/strategies` directory. #### Comparing multiple Strategies ```bash -freqtrade backtesting --strategy-list TestStrategy1 AwesomeStrategy --ticker-interval 5m +freqtrade backtesting --strategy-list SampleStrategy1 AwesomeStrategy --ticker-interval 5m ``` -Where `TestStrategy1` and `AwesomeStrategy` refer to class names of strategies. +Where `SampleStrategy1` and `AwesomeStrategy` refer to class names of strategies. #### Exporting trades to file @@ -103,7 +103,7 @@ The exported trades can be used for [further analysis](#further-backtest-result- #### Exporting trades to file specifying a custom filename ```bash -freqtrade backtesting --export trades --export-filename=backtest_teststrategy.json +freqtrade backtesting --export trades --export-filename=backtest_samplestrategy.json ``` #### Running backtest with smaller testset diff --git a/docs/configuration.md b/docs/configuration.md index 2d21de942..fcd6c2bf6 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -61,8 +61,9 @@ Mandatory parameters are marked as **Required**. | `order_time_in_force` | None | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force). [Strategy Override](#parameters-in-the-strategy). | `exchange.name` | | **Required.** Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename). | `exchange.sandbox` | false | Use the 'sandbox' version of the exchange, where the exchange provides a sandbox for risk-free integration. See [here](sandbox-testing.md) in more details. -| `exchange.key` | '' | API key to use for the exchange. Only required when you are in production mode. -| `exchange.secret` | '' | API secret to use for the exchange. Only required when you are in production mode. +| `exchange.key` | '' | API key to use for the exchange. Only required when you are in production mode. ***Keep it in secrete, do not disclose publicly.*** +| `exchange.secret` | '' | API secret to use for the exchange. Only required when you are in production mode. ***Keep it in secrete, do not disclose publicly.*** +| `exchange.password` | '' | API password to use for the exchange. Only required when you are in production mode and for exchanges that use password for API requests. ***Keep it in secrete, do not disclose publicly.*** | `exchange.pair_whitelist` | [] | List of pairs to use by the bot for trading and to check for potential trades during backtesting. Can be overriden by dynamic pairlists (see [below](#dynamic-pairlists)). | `exchange.pair_blacklist` | [] | List of pairs the bot must absolutely avoid for trading and backtesting. Can be overriden by dynamic pairlists (see [below](#dynamic-pairlists)). | `exchange.ccxt_config` | None | Additional CCXT parameters passed to the regular ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) @@ -76,8 +77,8 @@ Mandatory parameters are marked as **Required**. | `pairlist.method` | StaticPairList | Use static or dynamic volume-based pairlist. [More information below](#dynamic-pairlists). | `pairlist.config` | None | Additional configuration for dynamic pairlists. [More information below](#dynamic-pairlists). | `telegram.enabled` | true | **Required.** Enable or not the usage of Telegram. -| `telegram.token` | token | Your Telegram bot token. Only required if `telegram.enabled` is `true`. -| `telegram.chat_id` | chat_id | Your personal Telegram account id. Only required if `telegram.enabled` is `true`. +| `telegram.token` | token | Your Telegram bot token. Only required if `telegram.enabled` is `true`. ***Keep it in secrete, do not disclose publicly.*** +| `telegram.chat_id` | chat_id | Your personal Telegram account id. Only required if `telegram.enabled` is `true`. ***Keep it in secrete, do not disclose publicly.*** | `webhook.enabled` | false | Enable usage of Webhook notifications | `webhook.url` | false | URL for the webhook. Only required if `webhook.enabled` is `true`. See the [webhook documentation](webhook-config.md) for more details. | `webhook.webhookbuy` | false | Payload to send on buy. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. diff --git a/docs/data-analysis.md b/docs/data-analysis.md index 2f077edb7..f6277cac2 100644 --- a/docs/data-analysis.md +++ b/docs/data-analysis.md @@ -139,7 +139,7 @@ You can override strategy settings as demonstrated below. # Define some constants ticker_interval = "5m" # Name of the strategy class -strategy_name = 'TestStrategy' +strategy_name = 'SampleStrategy' # Path to user data user_data_dir = 'user_data' # Location of the strategy diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index ce76d52e5..10ea068ad 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1 +1 @@ -mkdocs-material==4.4.0 \ No newline at end of file +mkdocs-material==4.4.1 \ No newline at end of file diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 5cf3784ef..c7da19659 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -24,7 +24,7 @@ strategy file will be updated on Github. Put your custom strategy file into the directory `user_data/strategies`. Best copy the test-strategy and modify this copy to avoid having bot-updates override your changes. -`cp user_data/strategies/test_strategy.py user_data/strategies/awesome-strategy.py` +`cp user_data/strategies/sample_strategy.py user_data/strategies/awesome-strategy.py` ### Anatomy of a strategy @@ -36,8 +36,8 @@ A strategy file contains all the information needed to build a good strategy: - Minimal ROI recommended - Stoploss strongly recommended -The bot includes a sample strategy called `TestStrategy` you can update: `user_data/strategies/test_strategy.py`. -You can test it with the parameter: `--strategy TestStrategy` +The bot also include a sample strategy called `SampleStrategy` you can update: `user_data/strategies/sample_strategy.py`. +You can test it with the parameter: `--strategy SampleStrategy` Additionally, there is an attribute called `INTERFACE_VERSION`, which defines the version of the strategy interface the bot should use. The current version is 2 - which is also the default when it's not set explicitly in the strategy. @@ -48,7 +48,7 @@ Future versions will require this to be set. freqtrade --strategy AwesomeStrategy ``` -**For the following section we will use the [user_data/strategies/test_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/test_strategy.py) +**For the following section we will use the [user_data/strategies/sample_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/sample_strategy.py) file as reference.** !!! Note Strategies and Backtesting @@ -114,9 +114,8 @@ def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame return dataframe ``` - !!! Note "Want more indicator examples?" - Look into the [user_data/strategies/test_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/test_strategy.py).
+ Look into the [user_data/strategies/sample_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/sample_strategy.py). Then uncomment indicators you need. ### Buy signal rules @@ -127,7 +126,7 @@ It's important to always return the dataframe without removing/modifying the col This will method will also define a new column, `"buy"`, which needs to contain 1 for buys, and 0 for "no action". -Sample from `user_data/strategies/test_strategy.py`: +Sample from `user_data/strategies/sample_strategy.py`: ```python def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: @@ -157,7 +156,7 @@ It's important to always return the dataframe without removing/modifying the col This will method will also define a new column, `"sell"`, which needs to contain 1 for sells, and 0 for "no action". -Sample from `user_data/strategies/test_strategy.py`: +Sample from `user_data/strategies/sample_strategy.py`: ```python def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 6c93f9128..e20546856 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -14,6 +14,7 @@ from typing import Any, Dict, List, Optional, Tuple import arrow import ccxt import ccxt.async_support as ccxt_async +from ccxt.base.decimal_to_precision import ROUND_UP, ROUND_DOWN from pandas import DataFrame from freqtrade import (DependencyException, InvalidOrderException, @@ -808,11 +809,9 @@ def timeframe_to_prev_date(timeframe: str, date: datetime = None) -> datetime: """ if not date: date = datetime.now(timezone.utc) - timeframe_secs = timeframe_to_seconds(timeframe) - # Get offset based on timerame_secs - offset = date.timestamp() % timeframe_secs - # Subtract seconds passed since last offset - new_timestamp = date.timestamp() - offset + + new_timestamp = ccxt.Exchange.round_timeframe(timeframe, date.timestamp() * 1000, + ROUND_DOWN) // 1000 return datetime.fromtimestamp(new_timestamp, tz=timezone.utc) @@ -823,9 +822,8 @@ def timeframe_to_next_date(timeframe: str, date: datetime = None) -> datetime: :param date: date to use. Defaults to utcnow() :returns: date of next candle (with utc timezone) """ - prevdate = timeframe_to_prev_date(timeframe, date) - timeframe_secs = timeframe_to_seconds(timeframe) - - # Add one interval to previous candle - new_timestamp = prevdate.timestamp() + timeframe_secs + if not date: + date = datetime.now(timezone.utc) + new_timestamp = ccxt.Exchange.round_timeframe(timeframe, date.timestamp() * 1000, + ROUND_UP) // 1000 return datetime.fromtimestamp(new_timestamp, tz=timezone.utc) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 568615b53..4fba47243 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -81,6 +81,12 @@ class Backtesting(object): # No strategy list specified, only one strategy self.strategylist.append(StrategyResolver(self.config).strategy) + if "ticker_interval" not in self.config: + raise OperationalException("Ticker-interval needs to be set in either configuration " + "or as cli argument `--ticker-interval 5m`") + self.ticker_interval = str(self.config.get('ticker_interval')) + self.ticker_interval_mins = timeframe_to_minutes(self.ticker_interval) + # Load one (first) strategy self._set_strategy(self.strategylist[0]) @@ -89,12 +95,6 @@ class Backtesting(object): Load strategy into backtesting """ self.strategy = strategy - if "ticker_interval" not in self.config: - raise OperationalException("Ticker-interval needs to be set in either configuration " - "or as cli argument `--ticker-interval 5m`") - - self.ticker_interval = self.config.get('ticker_interval') - self.ticker_interval_mins = timeframe_to_minutes(self.ticker_interval) self.advise_buy = strategy.advise_buy self.advise_sell = strategy.advise_sell # Set stoploss_on_exchange to false for backtesting, diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index d9f350c7b..1fd045f54 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -1531,7 +1531,7 @@ def test_timeframe_to_prev_date(): assert timeframe_to_prev_date(interval, date) == result date = datetime.now(tz=timezone.utc) - assert timeframe_to_prev_date("5m", date) < date + assert timeframe_to_prev_date("5m") < date def test_timeframe_to_next_date(): @@ -1556,4 +1556,4 @@ def test_timeframe_to_next_date(): assert timeframe_to_next_date(interval, date) == result date = datetime.now(tz=timezone.utc) - assert timeframe_to_next_date("5m", date) > date + assert timeframe_to_next_date("5m") > date diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index 5c942ab72..52eae9df0 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -330,7 +330,7 @@ def test_backtesting_init_no_ticker_interval(mocker, default_conf, caplog) -> No patch_exchange(mocker) del default_conf['ticker_interval'] default_conf['strategy_list'] = ['DefaultStrategy', - 'TestStrategy'] + 'SampleStrategy'] mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.5)) with pytest.raises(OperationalException): @@ -877,7 +877,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog): '--disable-max-market-positions', '--strategy-list', 'DefaultStrategy', - 'TestStrategy', + 'SampleStrategy', ] args = get_args(args) start_backtesting(args) @@ -898,7 +898,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog): 'up to 2017-11-14T22:58:00+00:00 (0 days)..', 'Parameter --enable-position-stacking detected ...', 'Running backtesting for Strategy DefaultStrategy', - 'Running backtesting for Strategy TestStrategy', + 'Running backtesting for Strategy SampleStrategy', ] for line in exists: diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py index f9ebf552d..9583de510 100644 --- a/freqtrade/tests/optimize/test_hyperopt.py +++ b/freqtrade/tests/optimize/test_hyperopt.py @@ -254,7 +254,7 @@ def test_start_failure(mocker, default_conf, caplog) -> None: args = [ '--config', 'config.json', - '--strategy', 'TestStrategy', + '--strategy', 'SampleStrategy', 'hyperopt', '--epochs', '5' ] diff --git a/freqtrade/tests/strategy/legacy_strategy.py b/freqtrade/tests/strategy/legacy_strategy.py index 2cd13b791..af1b617a6 100644 --- a/freqtrade/tests/strategy/legacy_strategy.py +++ b/freqtrade/tests/strategy/legacy_strategy.py @@ -15,7 +15,7 @@ class TestStrategyLegacy(IStrategy): """ This is a test strategy using the legacy function headers, which will be removed in a future update. - Please do not use this as a template, but refer to user_data/strategy/TestStrategy.py + Please do not use this as a template, but refer to user_data/strategy/sample_strategy.py for a uptodate version of this template. """ diff --git a/freqtrade/tests/strategy/test_strategy.py b/freqtrade/tests/strategy/test_strategy.py index b1a6a6548..cd1102ead 100644 --- a/freqtrade/tests/strategy/test_strategy.py +++ b/freqtrade/tests/strategy/test_strategy.py @@ -61,27 +61,27 @@ def test_search_strategy(): def test_load_strategy(default_conf, result): - default_conf.update({'strategy': 'TestStrategy'}) + default_conf.update({'strategy': 'SampleStrategy'}) resolver = StrategyResolver(default_conf) assert 'adx' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'}) def test_load_strategy_base64(result, caplog, default_conf): - with open("user_data/strategies/test_strategy.py", "rb") as file: + with open("user_data/strategies/sample_strategy.py", "rb") as file: encoded_string = urlsafe_b64encode(file.read()).decode("utf-8") - default_conf.update({'strategy': 'TestStrategy:{}'.format(encoded_string)}) + default_conf.update({'strategy': 'SampleStrategy:{}'.format(encoded_string)}) resolver = StrategyResolver(default_conf) assert 'adx' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'}) # Make sure strategy was loaded from base64 (using temp directory)!! - assert log_has_re(r"Using resolved strategy TestStrategy from '" - + tempfile.gettempdir() + r"/.*/TestStrategy\.py'\.\.\.", caplog) + assert log_has_re(r"Using resolved strategy SampleStrategy from '" + + tempfile.gettempdir() + r"/.*/SampleStrategy\.py'\.\.\.", caplog) def test_load_strategy_invalid_directory(result, caplog, default_conf): resolver = StrategyResolver(default_conf) extra_dir = Path.cwd() / 'some/path' - resolver._load_strategy('TestStrategy', config=default_conf, extra_dir=extra_dir) + resolver._load_strategy('SampleStrategy', config=default_conf, extra_dir=extra_dir) assert log_has_re(r'Path .*' + r'some.*path.*' + r'.* does not exist', caplog) diff --git a/freqtrade/tests/test_arguments.py b/freqtrade/tests/test_arguments.py index 24f11e32e..381457bbd 100644 --- a/freqtrade/tests/test_arguments.py +++ b/freqtrade/tests/test_arguments.py @@ -102,7 +102,7 @@ def test_parse_args_backtesting_custom() -> None: '--refresh-pairs-cached', '--strategy-list', 'DefaultStrategy', - 'TestStrategy' + 'SampleStrategy' ] call_args = Arguments(args, '').get_parsed_arg() assert call_args.config == ['test_conf.json'] diff --git a/requirements-common.txt b/requirements-common.txt index 3d80c3ef5..c8a9c2f74 100644 --- a/requirements-common.txt +++ b/requirements-common.txt @@ -1,6 +1,6 @@ # requirements without requirements installable via conda # mainly used for Raspberry pi installs -ccxt==1.18.1068 +ccxt==1.18.1085 SQLAlchemy==1.3.7 python-telegram-bot==11.1.0 arrow==0.14.5 diff --git a/requirements-dev.txt b/requirements-dev.txt index 6436c60e4..a7b0d358d 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,7 +7,7 @@ flake8==3.7.8 flake8-type-annotations==0.1.0 flake8-tidy-imports==2.0.0 mypy==0.720 -pytest==5.1.0 +pytest==5.1.1 pytest-asyncio==0.10.0 pytest-cov==2.7.1 pytest-mock==1.10.4 diff --git a/requirements.txt b/requirements.txt index 9d558b5b8..e5015d620 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,5 +2,5 @@ -r requirements-common.txt numpy==1.17.0 -pandas==0.25.0 +pandas==0.25.1 scipy==1.3.1 diff --git a/setup.py b/setup.py index 631c8b654..b48bddd56 100644 --- a/setup.py +++ b/setup.py @@ -45,7 +45,7 @@ setup(name='freqtrade', tests_require=['pytest', 'pytest-mock', 'pytest-cov'], install_requires=[ # from requirements-common.txt - 'ccxt>=1.18', + 'ccxt>=1.18.1080', 'SQLAlchemy', 'python-telegram-bot', 'arrow', @@ -76,7 +76,7 @@ setup(name='freqtrade', 'plot': plot, 'all': all_extra, 'jupyter': jupyter, - + }, include_package_data=True, zip_safe=False, diff --git a/user_data/notebooks/strategy_analysis_example.ipynb b/user_data/notebooks/strategy_analysis_example.ipynb index 014f4ca90..89d71fe9d 100644 --- a/user_data/notebooks/strategy_analysis_example.ipynb +++ b/user_data/notebooks/strategy_analysis_example.ipynb @@ -52,7 +52,7 @@ "# Define some constants\n", "ticker_interval = \"5m\"\n", "# Name of the strategy class\n", - "strategy_name = 'TestStrategy'\n", + "strategy_name = 'SampleStrategy'\n", "# Path to user data\n", "user_data_dir = 'user_data'\n", "# Location of the strategy\n", diff --git a/user_data/strategies/test_strategy.py b/user_data/strategies/sample_strategy.py similarity index 98% rename from user_data/strategies/test_strategy.py rename to user_data/strategies/sample_strategy.py index 8e2bf8973..0649c6f94 100644 --- a/user_data/strategies/test_strategy.py +++ b/user_data/strategies/sample_strategy.py @@ -11,10 +11,9 @@ import numpy # noqa # This class is a sample. Feel free to customize it. -class TestStrategy(IStrategy): - __test__ = False # pytest expects to find tests here because of the name +class SampleStrategy(IStrategy): """ - This is a test strategy to inspire you. + This is a sample strategy to inspire you. More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md You can: