Merge branch 'feat/short' into pr/samgermain/5378

This commit is contained in:
Matthias 2021-09-04 19:54:34 +02:00
commit ca44d2e092
59 changed files with 566 additions and 276 deletions

View File

@ -1,4 +1,4 @@
FROM python:3.9.6-slim-buster as base FROM python:3.9.7-slim-buster as base
# Setup env # Setup env
ENV LANG C.UTF-8 ENV LANG C.UTF-8

View File

@ -42,7 +42,7 @@ docker build --cache-from freqtrade:${TAG_ARM} --build-arg sourceimage=${CACHE_I
docker tag freqtrade:$TAG_PLOT_ARM ${CACHE_IMAGE}:$TAG_PLOT_ARM docker tag freqtrade:$TAG_PLOT_ARM ${CACHE_IMAGE}:$TAG_PLOT_ARM
# Run backtest # Run backtest
docker run --rm -v $(pwd)/config_examples/config_bittrex.example.json:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG_ARM} backtesting --datadir /tests/testdata --strategy-path /tests/strategy/strats/ --strategy DefaultStrategy docker run --rm -v $(pwd)/config_examples/config_bittrex.example.json:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG_ARM} backtesting --datadir /tests/testdata --strategy-path /tests/strategy/strats/ --strategy StrategyTestV2
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "failed running backtest" echo "failed running backtest"

View File

@ -53,7 +53,7 @@ docker build --cache-from freqtrade:${TAG} --build-arg sourceimage=${CACHE_IMAGE
docker tag freqtrade:$TAG_PLOT ${CACHE_IMAGE}:$TAG_PLOT docker tag freqtrade:$TAG_PLOT ${CACHE_IMAGE}:$TAG_PLOT
# Run backtest # Run backtest
docker run --rm -v $(pwd)/config_examples/config_bittrex.example.json:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG} backtesting --datadir /tests/testdata --strategy-path /tests/strategy/strats/ --strategy DefaultStrategy docker run --rm -v $(pwd)/config_examples/config_bittrex.example.json:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG} backtesting --datadir /tests/testdata --strategy-path /tests/strategy/strats/ --strategy StrategyTestV2
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "failed running backtest" echo "failed running backtest"

View File

@ -174,7 +174,7 @@
"heartbeat_interval": 60 "heartbeat_interval": 60
}, },
"disable_dataframe_checks": false, "disable_dataframe_checks": false,
"strategy": "DefaultStrategy", "strategy": "SampleStrategy",
"strategy_path": "user_data/strategies/", "strategy_path": "user_data/strategies/",
"dataformat_ohlcv": "json", "dataformat_ohlcv": "json",
"dataformat_trades": "jsongz" "dataformat_trades": "jsongz"

View File

@ -335,7 +335,7 @@ Once the optimized parameters and conditions have been implemented into your str
To achieve same results (number of trades, their durations, profit, etc.) than during Hyperopt, please use same configuration and parameters (timerange, timeframe, ...) used for hyperopt `--dmmp`/`--disable-max-market-positions` and `--eps`/`--enable-position-stacking` for Backtesting. To achieve same results (number of trades, their durations, profit, etc.) than during Hyperopt, please use same configuration and parameters (timerange, timeframe, ...) used for hyperopt `--dmmp`/`--disable-max-market-positions` and `--eps`/`--enable-position-stacking` for Backtesting.
Should results don't match, please double-check to make sure you transferred all conditions correctly. Should results not match, please double-check to make sure you transferred all conditions correctly.
Pay special care to the stoploss (and trailing stoploss) parameters, as these are often set in configuration files, which override changes to the strategy. Pay special care to the stoploss (and trailing stoploss) parameters, as these are often set in configuration files, which override changes to the strategy.
You should also carefully review the log of your backtest to ensure that there were no parameters inadvertently set by the configuration (like `stoploss` or `trailing_stop`). You should also carefully review the log of your backtest to ensure that there were no parameters inadvertently set by the configuration (like `stoploss` or `trailing_stop`).

View File

@ -18,6 +18,7 @@ usage: freqtrade backtesting [-h] [-v] [--logfile FILE] [-V] [-c PATH]
[-p PAIRS [PAIRS ...]] [--eps] [--dmmp] [-p PAIRS [PAIRS ...]] [--eps] [--dmmp]
[--enable-protections] [--enable-protections]
[--dry-run-wallet DRY_RUN_WALLET] [--dry-run-wallet DRY_RUN_WALLET]
[--timeframe-detail TIMEFRAME_DETAIL]
[--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]] [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]]
[--export {none,trades}] [--export-filename PATH] [--export {none,trades}] [--export-filename PATH]
@ -55,6 +56,9 @@ optional arguments:
--dry-run-wallet DRY_RUN_WALLET, --starting-balance DRY_RUN_WALLET --dry-run-wallet DRY_RUN_WALLET, --starting-balance DRY_RUN_WALLET
Starting balance, used for backtesting / hyperopt and Starting balance, used for backtesting / hyperopt and
dry-runs. dry-runs.
--timeframe-detail TIMEFRAME_DETAIL
Specify detail timeframe for backtesting (`1m`, `5m`,
`30m`, `1h`, `1d`).
--strategy-list STRATEGY_LIST [STRATEGY_LIST ...] --strategy-list STRATEGY_LIST [STRATEGY_LIST ...]
Provide a space-separated list of strategies to Provide a space-separated list of strategies to
backtest. Please note that ticker-interval needs to be backtest. Please note that ticker-interval needs to be
@ -62,7 +66,7 @@ optional arguments:
this together with `--export trades`, the strategy- this together with `--export trades`, the strategy-
name is injected into the filename (so `backtest- name is injected into the filename (so `backtest-
data.json` becomes `backtest-data- data.json` becomes `backtest-data-
DefaultStrategy.json` SampleStrategy.json`
--export {none,trades} --export {none,trades}
Export backtest results (default: trades). Export backtest results (default: trades).
--export-filename PATH --export-filename PATH
@ -425,7 +429,12 @@ It contains some useful key metrics about performance of your strategy on backte
- `Drawdown Start` / `Drawdown End`: Start and end datetime for this largest drawdown (can also be visualized via the `plot-dataframe` sub-command). - `Drawdown Start` / `Drawdown End`: Start and end datetime for this largest drawdown (can also be visualized via the `plot-dataframe` sub-command).
- `Market change`: Change of the market during the backtest period. Calculated as average of all pairs changes from the first to the last candle using the "close" column. - `Market change`: Change of the market during the backtest period. Calculated as average of all pairs changes from the first to the last candle using the "close" column.
### Assumptions made by backtesting ### Further backtest-result analysis
To further analyze your backtest results, you can [export the trades](#exporting-trades-to-file).
You can then load the trades to perform further analysis as shown in our [data analysis](data-analysis.md#backtesting) backtesting section.
## Assumptions made by backtesting
Since backtesting lacks some detailed information about what happens within a candle, it needs to take a few assumptions: Since backtesting lacks some detailed information about what happens within a candle, it needs to take a few assumptions:
@ -456,10 +465,30 @@ Also, keep in mind that past results don't guarantee future success.
In addition to the above assumptions, strategy authors should carefully read the [Common Mistakes](strategy-customization.md#common-mistakes-when-developing-strategies) section, to avoid using data in backtesting which is not available in real market conditions. In addition to the above assumptions, strategy authors should carefully read the [Common Mistakes](strategy-customization.md#common-mistakes-when-developing-strategies) section, to avoid using data in backtesting which is not available in real market conditions.
### Further backtest-result analysis ### Improved backtest accuracy
To further analyze your backtest results, you can [export the trades](#exporting-trades-to-file). One big limitation of backtesting is it's inability to know how prices moved intra-candle (was high before close, or viceversa?).
You can then load the trades to perform further analysis as shown in our [data analysis](data-analysis.md#backtesting) backtesting section. So assuming you run backtesting with a 1h timeframe, there will be 4 prices for that candle (Open, High, Low, Close).
While backtesting does take some assumptions (read above) about this - this can never be perfect, and will always be biased in one way or the other.
To mitigate this, freqtrade can use a lower (faster) timeframe to simulate intra-candle movements.
To utilize this, you can append `--timeframe-detail 5m` to your regular backtesting command.
``` bash
freqtrade backtesting --strategy AwesomeStrategy --timeframe 1h --timeframe-detail 5m
```
This will load 1h data as well as 5m data for the timeframe. The strategy will be analyzed with the 1h timeframe - and for every "open trade candle" (candles where a trade is open) the 5m data will be used to simulate intra-candle movements.
All callback functions (`custom_sell()`, `custom_stoploss()`, ... ) will be running for each 5m candle once the trade is opened (so 12 times in the above example of 1h timeframe, and 5m detailed timeframe).
`--timeframe-detail` must be smaller than the original timeframe, otherwise backtesting will fail to start.
Obviously this will require more memory (5m data is bigger than 1h data), and will also impact runtime (depending on the amount of trades and trade durations).
Also, data must be available / downloaded already.
!!! Tip
You can use this function as the last part of strategy development, to ensure your strategy is not exploiting one of the [backtesting assumptions](#assumptions-made-by-backtesting). Strategies that perform similarly well with this mode have a good chance to perform well in dry/live modes too (although only forward-testing (dry-mode) can really confirm a strategy).
## Backtesting multiple strategies ## Backtesting multiple strategies

View File

@ -7,7 +7,7 @@ This page provides you some basic concepts on how Freqtrade works and operates.
* **Strategy**: Your trading strategy, telling the bot what to do. * **Strategy**: Your trading strategy, telling the bot what to do.
* **Trade**: Open position. * **Trade**: Open position.
* **Open Order**: Order which is currently placed on the exchange, and is not yet complete. * **Open Order**: Order which is currently placed on the exchange, and is not yet complete.
* **Pair**: Tradable pair, usually in the format of Quote/Base (e.g. XRP/USDT). * **Pair**: Tradable pair, usually in the format of Base/Quote (e.g. XRP/USDT).
* **Timeframe**: Candle length to use (e.g. `"5m"`, `"1h"`, ...). * **Timeframe**: Candle length to use (e.g. `"5m"`, `"1h"`, ...).
* **Indicators**: Technical indicators (SMA, EMA, RSI, ...). * **Indicators**: Technical indicators (SMA, EMA, RSI, ...).
* **Limit order**: Limit orders which execute at the defined limit price or better. * **Limit order**: Limit orders which execute at the defined limit price or better.

View File

@ -456,7 +456,7 @@ class MyAwesomeStrategy(IStrategy):
"only_per_pair": False "only_per_pair": False
}) })
return protection return prot
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# ... # ...
@ -827,8 +827,8 @@ After you run Hyperopt for the desired amount of epochs, you can later list all
Once the optimized strategy has been implemented into your strategy, you should backtest this strategy to make sure everything is working as expected. Once the optimized strategy has been implemented into your strategy, you should backtest this strategy to make sure everything is working as expected.
To achieve same results (number of trades, their durations, profit, etc.) than during Hyperopt, please use same configuration and parameters (timerange, timeframe, ...) used for hyperopt `--dmmp`/`--disable-max-market-positions` and `--eps`/`--enable-position-stacking` for Backtesting. To achieve same the results (number of trades, their durations, profit, etc.) as during Hyperopt, please use the same configuration and parameters (timerange, timeframe, ...) used for hyperopt `--dmmp`/`--disable-max-market-positions` and `--eps`/`--enable-position-stacking` for Backtesting.
Should results don't match, please double-check to make sure you transferred all conditions correctly. Should results not match, please double-check to make sure you transferred all conditions correctly.
Pay special care to the stoploss (and trailing stoploss) parameters, as these are often set in configuration files, which override changes to the strategy. Pay special care to the stoploss (and trailing stoploss) parameters, as these are often set in configuration files, which override changes to the strategy.
You should also carefully review the log of your backtest to ensure that there were no parameters inadvertently set by the configuration (like `stoploss` or `trailing_stop`). You should also carefully review the log of your backtest to ensure that there were no parameters inadvertently set by the configuration (like `stoploss` or `trailing_stop`).

View File

@ -1,4 +1,4 @@
mkdocs==1.2.2 mkdocs==1.2.2
mkdocs-material==7.2.4 mkdocs-material==7.2.5
mdx_truly_sane_lists==1.2 mdx_truly_sane_lists==1.2
pymdown-extensions==8.2 pymdown-extensions==8.2

View File

@ -22,7 +22,7 @@ ARGS_COMMON_OPTIMIZE = ["timeframe", "timerange", "dataformat_ohlcv",
"max_open_trades", "stake_amount", "fee", "pairs"] "max_open_trades", "stake_amount", "fee", "pairs"]
ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + ["position_stacking", "use_max_market_positions", ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + ["position_stacking", "use_max_market_positions",
"enable_protections", "dry_run_wallet", "enable_protections", "dry_run_wallet", "timeframe_detail",
"strategy_list", "export", "exportfilename"] "strategy_list", "export", "exportfilename"]
ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path", ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path",

View File

@ -66,16 +66,22 @@ def ask_user_config() -> Dict[str, Any]:
{ {
"type": "text", "type": "text",
"name": "stake_amount", "name": "stake_amount",
"message": "Please insert your stake amount:", "message": f"Please insert your stake amount (Number or '{UNLIMITED_STAKE_AMOUNT}'):",
"default": "0.01", "default": "0.01",
"validate": lambda val: val == UNLIMITED_STAKE_AMOUNT or validate_is_float(val), "validate": lambda val: val == UNLIMITED_STAKE_AMOUNT or validate_is_float(val),
"filter": lambda val: '"' + UNLIMITED_STAKE_AMOUNT + '"'
if val == UNLIMITED_STAKE_AMOUNT
else val
}, },
{ {
"type": "text", "type": "text",
"name": "max_open_trades", "name": "max_open_trades",
"message": f"Please insert max_open_trades (Integer or '{UNLIMITED_STAKE_AMOUNT}'):", "message": f"Please insert max_open_trades (Integer or '{UNLIMITED_STAKE_AMOUNT}'):",
"default": "3", "default": "3",
"validate": lambda val: val == UNLIMITED_STAKE_AMOUNT or validate_is_int(val) "validate": lambda val: val == UNLIMITED_STAKE_AMOUNT or validate_is_int(val),
"filter": lambda val: '"' + UNLIMITED_STAKE_AMOUNT + '"'
if val == UNLIMITED_STAKE_AMOUNT
else val
}, },
{ {
"type": "text", "type": "text",

View File

@ -135,6 +135,10 @@ AVAILABLE_CLI_OPTIONS = {
help='Override the value of the `stake_amount` configuration setting.', help='Override the value of the `stake_amount` configuration setting.',
), ),
# Backtesting # Backtesting
"timeframe_detail": Arg(
'--timeframe-detail',
help='Specify detail timeframe for backtesting (`1m`, `5m`, `30m`, `1h`, `1d`).',
),
"position_stacking": Arg( "position_stacking": Arg(
'--eps', '--enable-position-stacking', '--eps', '--enable-position-stacking',
help='Allow buying the same pair multiple times (position stacking).', help='Allow buying the same pair multiple times (position stacking).',
@ -162,7 +166,7 @@ AVAILABLE_CLI_OPTIONS = {
'Please note that ticker-interval needs to be set either in config ' 'Please note that ticker-interval needs to be set either in config '
'or via command line. When using this together with `--export trades`, ' 'or via command line. When using this together with `--export trades`, '
'the strategy-name is injected into the filename ' 'the strategy-name is injected into the filename '
'(so `backtest-data.json` becomes `backtest-data-DefaultStrategy.json`', '(so `backtest-data.json` becomes `backtest-data-SampleStrategy.json`',
nargs='+', nargs='+',
), ),
"export": Arg( "export": Arg(

View File

@ -74,8 +74,6 @@ def start_new_strategy(args: Dict[str, Any]) -> None:
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE) config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
if "strategy" in args and args["strategy"]: if "strategy" in args and args["strategy"]:
if args["strategy"] == "DefaultStrategy":
raise OperationalException("DefaultStrategy is not allowed as name.")
new_path = config['user_data_dir'] / USERPATH_STRATEGIES / (args['strategy'] + '.py') new_path = config['user_data_dir'] / USERPATH_STRATEGIES / (args['strategy'] + '.py')
@ -128,8 +126,6 @@ def start_new_hyperopt(args: Dict[str, Any]) -> None:
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE) config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
if 'hyperopt' in args and args['hyperopt']: if 'hyperopt' in args and args['hyperopt']:
if args['hyperopt'] == 'DefaultHyperopt':
raise OperationalException("DefaultHyperopt is not allowed as name.")
new_path = config['user_data_dir'] / USERPATH_HYPEROPTS / (args['hyperopt'] + '.py') new_path = config['user_data_dir'] / USERPATH_HYPEROPTS / (args['hyperopt'] + '.py')

View File

@ -242,6 +242,9 @@ class Configuration:
except ValueError: except ValueError:
pass pass
self._args_to_config(config, argname='timeframe_detail',
logstring='Parameter --timeframe-detail detected, '
'using {} for intra-candle backtesting ...')
self._args_to_config(config, argname='stake_amount', self._args_to_config(config, argname='stake_amount',
logstring='Parameter --stake-amount detected, ' logstring='Parameter --stake-amount detected, '
'overriding stake_amount to: {} ...') 'overriding stake_amount to: {} ...')

View File

@ -49,6 +49,8 @@ USERPATH_NOTEBOOKS = 'notebooks'
TELEGRAM_SETTING_OPTIONS = ['on', 'off', 'silent'] TELEGRAM_SETTING_OPTIONS = ['on', 'off', 'silent']
ENV_VAR_PREFIX = 'FREQTRADE__' ENV_VAR_PREFIX = 'FREQTRADE__'
NON_OPEN_EXCHANGE_STATES = ('cancelled', 'canceled', 'closed', 'expired')
# Define decimals per coin for outputs # Define decimals per coin for outputs
# Only used for outputs. # Only used for outputs.

View File

@ -19,7 +19,8 @@ from ccxt.base.decimal_to_precision import (ROUND_DOWN, ROUND_UP, TICK_SIZE, TRU
decimal_to_precision) decimal_to_precision)
from pandas import DataFrame from pandas import DataFrame
from freqtrade.constants import DEFAULT_AMOUNT_RESERVE_PERCENT, ListPairsWithTimeframes from freqtrade.constants import (DEFAULT_AMOUNT_RESERVE_PERCENT, NON_OPEN_EXCHANGE_STATES,
ListPairsWithTimeframes)
from freqtrade.data.converter import ohlcv_to_dataframe, trades_dict_to_list from freqtrade.data.converter import ohlcv_to_dataframe, trades_dict_to_list
from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFundsError, from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFundsError,
InvalidOrderException, OperationalException, PricingError, InvalidOrderException, OperationalException, PricingError,
@ -351,9 +352,16 @@ class Exchange:
def validate_stakecurrency(self, stake_currency: str) -> None: def validate_stakecurrency(self, stake_currency: str) -> None:
""" """
Checks stake-currency against available currencies on the exchange. Checks stake-currency against available currencies on the exchange.
Only runs on startup. If markets have not been loaded, there's been a problem with
the connection to the exchange.
:param stake_currency: Stake-currency to validate :param stake_currency: Stake-currency to validate
:raise: OperationalException if stake-currency is not available. :raise: OperationalException if stake-currency is not available.
""" """
if not self._markets:
raise OperationalException(
'Could not load markets, therefore cannot start. '
'Please investigate the above error for more details.'
)
quote_currencies = self.get_quote_currencies() quote_currencies = self.get_quote_currencies()
if stake_currency not in quote_currencies: if stake_currency not in quote_currencies:
raise OperationalException( raise OperationalException(
@ -804,7 +812,7 @@ class Exchange:
:param order: Order dict as returned from fetch_order() :param order: Order dict as returned from fetch_order()
:return: True if order has been cancelled without being filled, False otherwise. :return: True if order has been cancelled without being filled, False otherwise.
""" """
return (order.get('status') in ('closed', 'canceled', 'cancelled') return (order.get('status') in NON_OPEN_EXCHANGE_STATES
and order.get('filled') == 0.0) and order.get('filled') == 0.0)
@retrier @retrier

View File

@ -433,11 +433,12 @@ class FreqtradeBot(LoggingMixin):
# TODO-lev: Does the below need to be adjusted for shorts? # TODO-lev: Does the below need to be adjusted for shorts?
if self._check_depth_of_market_buy(pair, bid_check_dom): if self._check_depth_of_market_buy(pair, bid_check_dom):
# TODO-lev: pass in "enter" as side. # TODO-lev: pass in "enter" as side.
return self.execute_buy(pair, stake_amount, enter_tag=enter_tag)
return self.execute_entry(pair, stake_amount, buy_tag=enter_tag)
else: else:
return False return False
return self.execute_buy(pair, stake_amount, enter_tag=enter_tag) return self.execute_entry(pair, stake_amount, buy_tag=enter_tag)
else: else:
return False return False
@ -465,8 +466,8 @@ class FreqtradeBot(LoggingMixin):
logger.info(f"Bids to asks delta for {pair} does not satisfy condition.") logger.info(f"Bids to asks delta for {pair} does not satisfy condition.")
return False return False
def execute_buy(self, pair: str, stake_amount: float, price: Optional[float] = None, def execute_entry(self, pair: str, stake_amount: float, price: Optional[float] = None,
forcebuy: bool = False, enter_tag: Optional[str] = None) -> bool: forcebuy: bool = False, enter_tag: Optional[str] = None) -> bool:
""" """
Executes a limit buy for the given pair Executes a limit buy for the given pair
:param pair: pair for which we want to create a LIMIT_BUY :param pair: pair for which we want to create a LIMIT_BUY
@ -746,7 +747,7 @@ class FreqtradeBot(LoggingMixin):
trade.stoploss_order_id = None trade.stoploss_order_id = None
logger.error(f'Unable to place a stoploss order on exchange. {e}') logger.error(f'Unable to place a stoploss order on exchange. {e}')
logger.warning('Selling the trade forcefully') logger.warning('Selling the trade forcefully')
self.execute_sell(trade, trade.stop_loss, sell_reason=SellCheckTuple( self.execute_trade_exit(trade, trade.stop_loss, sell_reason=SellCheckTuple(
sell_type=SellType.EMERGENCY_SELL)) sell_type=SellType.EMERGENCY_SELL))
except ExchangeError: except ExchangeError:
@ -864,7 +865,7 @@ class FreqtradeBot(LoggingMixin):
if should_exit.sell_flag: if should_exit.sell_flag:
logger.info(f'Exit for {trade.pair} detected. Reason: {should_exit.sell_type}') logger.info(f'Exit for {trade.pair} detected. Reason: {should_exit.sell_type}')
self.execute_sell(trade, sell_rate, should_exit) self.execute_trade_exit(trade, sell_rate, should_exit)
return True return True
return False return False
@ -946,7 +947,7 @@ class FreqtradeBot(LoggingMixin):
was_trade_fully_canceled = False was_trade_fully_canceled = False
# Cancelled orders may have the status of 'canceled' or 'closed' # Cancelled orders may have the status of 'canceled' or 'closed'
if order['status'] not in ('cancelled', 'canceled', 'closed'): if order['status'] not in constants.NON_OPEN_EXCHANGE_STATES:
filled_val = order.get('filled', 0.0) or 0.0 filled_val = order.get('filled', 0.0) or 0.0
filled_stake = filled_val * trade.open_rate filled_stake = filled_val * trade.open_rate
minstake = self.exchange.get_min_pair_stake_amount( minstake = self.exchange.get_min_pair_stake_amount(
@ -962,7 +963,7 @@ class FreqtradeBot(LoggingMixin):
# Avoid race condition where the order could not be cancelled coz its already filled. # Avoid race condition where the order could not be cancelled coz its already filled.
# Simply bailing here is the only safe way - as this order will then be # Simply bailing here is the only safe way - as this order will then be
# handled in the next iteration. # handled in the next iteration.
if corder.get('status') not in ('cancelled', 'canceled', 'closed'): if corder.get('status') not in constants.NON_OPEN_EXCHANGE_STATES:
logger.warning(f"Order {trade.open_order_id} for {trade.pair} not cancelled.") logger.warning(f"Order {trade.open_order_id} for {trade.pair} not cancelled.")
return False return False
else: else:
@ -1065,9 +1066,9 @@ class FreqtradeBot(LoggingMixin):
raise DependencyException( raise DependencyException(
f"Not enough amount to sell. Trade-amount: {amount}, Wallet: {wallet_amount}") f"Not enough amount to sell. Trade-amount: {amount}, Wallet: {wallet_amount}")
def execute_sell(self, trade: Trade, limit: float, sell_reason: SellCheckTuple) -> bool: def execute_trade_exit(self, trade: Trade, limit: float, sell_reason: SellCheckTuple) -> bool:
""" """
Executes a limit sell for the given trade and limit Executes a trade exit for the given trade and limit
:param trade: Trade instance :param trade: Trade instance
:param limit: limit rate for the sell order :param limit: limit rate for the sell order
:param sell_reason: Reason the sell was triggered :param sell_reason: Reason the sell was triggered
@ -1143,7 +1144,7 @@ class FreqtradeBot(LoggingMixin):
trade.close_rate_requested = limit trade.close_rate_requested = limit
trade.sell_reason = sell_reason.sell_reason trade.sell_reason = sell_reason.sell_reason
# In case of market sell orders the order can be closed immediately # In case of market sell orders the order can be closed immediately
if order.get('status', 'unknown') == 'closed': if order.get('status', 'unknown') in ('closed', 'expired'):
self.update_trade_state(trade, trade.open_order_id, order) self.update_trade_state(trade, trade.open_order_id, order)
Trade.commit() Trade.commit()

View File

@ -89,6 +89,17 @@ class Backtesting:
"configuration or as cli argument `--timeframe 5m`") "configuration or as cli argument `--timeframe 5m`")
self.timeframe = str(self.config.get('timeframe')) self.timeframe = str(self.config.get('timeframe'))
self.timeframe_min = timeframe_to_minutes(self.timeframe) self.timeframe_min = timeframe_to_minutes(self.timeframe)
# Load detail timeframe if specified
self.timeframe_detail = str(self.config.get('timeframe_detail', ''))
if self.timeframe_detail:
self.timeframe_detail_min = timeframe_to_minutes(self.timeframe_detail)
if self.timeframe_min <= self.timeframe_detail_min:
raise OperationalException(
"Detail timeframe must be smaller than strategy timeframe.")
else:
self.timeframe_detail_min = 0
self.detail_data: Dict[str, DataFrame] = {}
self.pairlists = PairListManager(self.exchange, self.config) self.pairlists = PairListManager(self.exchange, self.config)
if 'VolumePairList' in self.pairlists.name_list: if 'VolumePairList' in self.pairlists.name_list:
@ -191,6 +202,23 @@ class Backtesting:
self.progress.set_new_value(1) self.progress.set_new_value(1)
return data, self.timerange return data, self.timerange
def load_bt_data_detail(self) -> None:
"""
Loads backtest detail data (smaller timeframe) if necessary.
"""
if self.timeframe_detail:
self.detail_data = history.load_data(
datadir=self.config['datadir'],
pairs=self.pairlists.whitelist,
timeframe=self.timeframe_detail,
timerange=self.timerange,
startup_candles=0,
fail_without_data=True,
data_format=self.config.get('dataformat_ohlcv', 'json'),
)
else:
self.detail_data = {}
def prepare_backtest(self, enable_protections): def prepare_backtest(self, enable_protections):
""" """
Backtesting setup method - called once for every call to "backtest()". Backtesting setup method - called once for every call to "backtest()".
@ -334,7 +362,8 @@ class Backtesting:
else: else:
return sell_row[OPEN_IDX] return sell_row[OPEN_IDX]
def _get_sell_trade_entry(self, trade: LocalTrade, sell_row: Tuple) -> Optional[LocalTrade]: def _get_sell_trade_entry_for_candle(self, trade: LocalTrade,
sell_row: Tuple) -> Optional[LocalTrade]:
sell_candle_time = sell_row[DATE_IDX].to_pydatetime() sell_candle_time = sell_row[DATE_IDX].to_pydatetime()
enter = sell_row[LONG_IDX] if trade.is_short else sell_row[SHORT_IDX] enter = sell_row[LONG_IDX] if trade.is_short else sell_row[SHORT_IDX]
exit_ = sell_row[ELONG_IDX] if trade.is_short else sell_row[ESHORT_IDX] exit_ = sell_row[ELONG_IDX] if trade.is_short else sell_row[ESHORT_IDX]
@ -365,6 +394,35 @@ class Backtesting:
return None return None
def _get_sell_trade_entry(self, trade: LocalTrade, sell_row: Tuple) -> Optional[LocalTrade]:
if self.timeframe_detail and trade.pair in self.detail_data:
sell_candle_time = sell_row[DATE_IDX].to_pydatetime()
sell_candle_end = sell_candle_time + timedelta(minutes=self.timeframe_min)
detail_data = self.detail_data[trade.pair]
detail_data = detail_data.loc[
(detail_data['date'] >= sell_candle_time) &
(detail_data['date'] < sell_candle_end)
]
if len(detail_data) == 0:
# Fall back to "regular" data if no detail data was found for this candle
return self._get_sell_trade_entry_for_candle(trade, sell_row)
detail_data['enter_long'] = sell_row[LONG_IDX]
detail_data['exit_long'] = sell_row[ELONG_IDX]
detail_data['enter_short'] = sell_row[SHORT_IDX]
detail_data['exit_short'] = sell_row[ESHORT_IDX]
headers = ['date', 'open', 'high', 'low', 'close', 'enter_long', 'exit_long',
'enter_short', 'exit_short']
for det_row in detail_data[headers].values.tolist():
res = self._get_sell_trade_entry_for_candle(trade, det_row)
if res:
return res
return None
else:
return self._get_sell_trade_entry_for_candle(trade, sell_row)
def _enter_trade(self, pair: str, row: List, direction: str) -> Optional[LocalTrade]: def _enter_trade(self, pair: str, row: List, direction: str) -> Optional[LocalTrade]:
try: try:
stake_amount = self.wallets.get_trade_stake_amount(pair, None) stake_amount = self.wallets.get_trade_stake_amount(pair, None)
@ -626,6 +684,7 @@ class Backtesting:
data: Dict[str, Any] = {} data: Dict[str, Any] = {}
data, timerange = self.load_bt_data() data, timerange = self.load_bt_data()
self.load_bt_data_detail()
logger.info("Dataload complete. Calculating indicators") logger.info("Dataload complete. Calculating indicators")
for strat in self.strategylist: for strat in self.strategylist:

View File

@ -368,6 +368,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
'max_open_trades_setting': (config['max_open_trades'] 'max_open_trades_setting': (config['max_open_trades']
if config['max_open_trades'] != float('inf') else -1), if config['max_open_trades'] != float('inf') else -1),
'timeframe': config['timeframe'], 'timeframe': config['timeframe'],
'timeframe_detail': config.get('timeframe_detail', ''),
'timerange': config.get('timerange', ''), 'timerange': config.get('timerange', ''),
'enable_protections': config.get('enable_protections', False), 'enable_protections': config.get('enable_protections', False),
'strategy_name': strategy, 'strategy_name': strategy,

View File

@ -13,7 +13,7 @@ from sqlalchemy.orm import Query, declarative_base, relationship, scoped_session
from sqlalchemy.pool import StaticPool from sqlalchemy.pool import StaticPool
from sqlalchemy.sql.schema import UniqueConstraint from sqlalchemy.sql.schema import UniqueConstraint
from freqtrade.constants import DATETIME_PRINT_FORMAT from freqtrade.constants import DATETIME_PRINT_FORMAT, NON_OPEN_EXCHANGE_STATES
from freqtrade.enums import SellType from freqtrade.enums import SellType
from freqtrade.exceptions import DependencyException, OperationalException from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.leverage import interest from freqtrade.leverage import interest
@ -164,7 +164,7 @@ class Order(_DECL_BASE):
self.order_date = datetime.fromtimestamp(order['timestamp'] / 1000, tz=timezone.utc) self.order_date = datetime.fromtimestamp(order['timestamp'] / 1000, tz=timezone.utc)
self.ft_is_open = True self.ft_is_open = True
if self.status in ('closed', 'canceled', 'cancelled'): if self.status in NON_OPEN_EXCHANGE_STATES:
self.ft_is_open = False self.ft_is_open = False
if (order.get('filled', 0.0) or 0.0) > 0: if (order.get('filled', 0.0) or 0.0) > 0:
self.order_filled_date = datetime.now(timezone.utc) self.order_filled_date = datetime.now(timezone.utc)

View File

@ -46,11 +46,14 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac
if ( if (
not ApiServer._bt not ApiServer._bt
or lastconfig.get('timeframe') != strat.timeframe or lastconfig.get('timeframe') != strat.timeframe
or lastconfig.get('timeframe_detail') != btconfig.get('timeframe_detail')
or lastconfig.get('dry_run_wallet') != btconfig.get('dry_run_wallet', 0) or lastconfig.get('dry_run_wallet') != btconfig.get('dry_run_wallet', 0)
or lastconfig.get('timerange') != btconfig['timerange'] or lastconfig.get('timerange') != btconfig['timerange']
): ):
from freqtrade.optimize.backtesting import Backtesting from freqtrade.optimize.backtesting import Backtesting
ApiServer._bt = Backtesting(btconfig) ApiServer._bt = Backtesting(btconfig)
if ApiServer._bt.timeframe_detail:
ApiServer._bt.load_bt_data_detail()
# Only reload data if timeframe changed. # Only reload data if timeframe changed.
if ( if (

View File

@ -324,6 +324,7 @@ class PairHistory(BaseModel):
class BacktestRequest(BaseModel): class BacktestRequest(BaseModel):
strategy: str strategy: str
timeframe: Optional[str] timeframe: Optional[str]
timeframe_detail: Optional[str]
timerange: Optional[str] timerange: Optional[str]
max_open_trades: Optional[int] max_open_trades: Optional[int]
stake_amount: Optional[Union[float, str]] stake_amount: Optional[Union[float, str]]

View File

@ -557,7 +557,7 @@ class RPC:
current_rate = self._freqtrade.exchange.get_rate( current_rate = self._freqtrade.exchange.get_rate(
trade.pair, refresh=False, side="sell") trade.pair, refresh=False, side="sell")
sell_reason = SellCheckTuple(sell_type=SellType.FORCE_SELL) sell_reason = SellCheckTuple(sell_type=SellType.FORCE_SELL)
self._freqtrade.execute_sell(trade, current_rate, sell_reason) self._freqtrade.execute_trade_exit(trade, current_rate, sell_reason)
# ---- EOF def _exec_forcesell ---- # ---- EOF def _exec_forcesell ----
if self._freqtrade.state != State.RUNNING: if self._freqtrade.state != State.RUNNING:
@ -613,7 +613,7 @@ class RPC:
stakeamount = self._freqtrade.wallets.get_trade_stake_amount(pair) stakeamount = self._freqtrade.wallets.get_trade_stake_amount(pair)
# execute buy # execute buy
if self._freqtrade.execute_buy(pair, stakeamount, price, forcebuy=True): if self._freqtrade.execute_entry(pair, stakeamount, price, forcebuy=True):
Trade.commit() Trade.commit()
trade = Trade.get_trades([Trade.is_open.is_(True), Trade.pair == pair]).first() trade = Trade.get_trades([Trade.is_open.is_(True), Trade.pair == pair]).first()
return trade return trade

View File

@ -120,6 +120,8 @@ class IStrategy(ABC, HyperStrategyMixin):
# and wallets - access to the current balance. # and wallets - access to the current balance.
dp: Optional[DataProvider] = None dp: Optional[DataProvider] = None
wallets: Optional[Wallets] = None wallets: Optional[Wallets] = None
# Filled from configuration
stake_currency: str
# container variable for strategy source code # container variable for strategy source code
__source__: str = '' __source__: str = ''

View File

@ -36,6 +36,6 @@
"BNB/TUSD", "BNB/TUSD",
"BNB/USDC", "BNB/USDC",
"BNB/USDS", "BNB/USDS",
"BNB/USDT", "BNB/USDT"
] ]
} }

View File

@ -1,5 +1,5 @@
# Include all requirements to run the bot. # Include all requirements to run the bot.
-r requirements.txt -r requirements.txt
plotly==5.2.1 plotly==5.3.0

View File

@ -1,9 +1,9 @@
numpy==1.21.2 numpy==1.21.2
pandas==1.3.2 pandas==1.3.2
ccxt==1.55.28 ccxt==1.55.56
# Pin cryptography for now due to rust build errors with piwheels # Pin cryptography for now due to rust build errors with piwheels
cryptography==3.4.7 cryptography==3.4.8
aiohttp==3.7.4.post0 aiohttp==3.7.4.post0
SQLAlchemy==1.4.23 SQLAlchemy==1.4.23
python-telegram-bot==13.7 python-telegram-bot==13.7
@ -31,7 +31,7 @@ python-rapidjson==1.4
sdnotify==0.3.2 sdnotify==0.3.2
# API Server # API Server
fastapi==0.68.0 fastapi==0.68.1
uvicorn==0.15.0 uvicorn==0.15.0
pyjwt==2.1.0 pyjwt==2.1.0
aiofiles==0.7.0 aiofiles==0.7.0

View File

@ -119,6 +119,7 @@ function install_mac_newer_python_dependencies() {
echo "-------------------------" echo "-------------------------"
brew install hdf5 brew install hdf5
fi fi
export HDF5_DIR=$(brew --prefix)
if [ ! $(brew --prefix --installed c-blosc 2>/dev/null) ] if [ ! $(brew --prefix --installed c-blosc 2>/dev/null) ]
then then
@ -127,6 +128,7 @@ function install_mac_newer_python_dependencies() {
echo "-------------------------" echo "-------------------------"
brew install c-blosc brew install c-blosc
fi fi
export CBLOSC_DIR=$(brew --prefix)
} }
# Install bot MacOS # Install bot MacOS

View File

@ -510,17 +510,6 @@ def test_start_new_strategy(mocker, caplog):
start_new_strategy(get_args(args)) start_new_strategy(get_args(args))
def test_start_new_strategy_DefaultStrat(mocker, caplog):
args = [
"new-strategy",
"--strategy",
"DefaultStrategy"
]
with pytest.raises(OperationalException,
match=r"DefaultStrategy is not allowed as name\."):
start_new_strategy(get_args(args))
def test_start_new_strategy_no_arg(mocker, caplog): def test_start_new_strategy_no_arg(mocker, caplog):
args = [ args = [
"new-strategy", "new-strategy",
@ -552,17 +541,6 @@ def test_start_new_hyperopt(mocker, caplog):
start_new_hyperopt(get_args(args)) start_new_hyperopt(get_args(args))
def test_start_new_hyperopt_DefaultHyperopt(mocker, caplog):
args = [
"new-hyperopt",
"--hyperopt",
"DefaultHyperopt"
]
with pytest.raises(OperationalException,
match=r"DefaultHyperopt is not allowed as name\."):
start_new_hyperopt(get_args(args))
def test_start_new_hyperopt_no_arg(mocker): def test_start_new_hyperopt_no_arg(mocker):
args = [ args = [
"new-hyperopt", "new-hyperopt",
@ -827,9 +805,9 @@ def test_start_list_strategies(mocker, caplog, capsys):
# pargs['config'] = None # pargs['config'] = None
start_list_strategies(pargs) start_list_strategies(pargs)
captured = capsys.readouterr() captured = capsys.readouterr()
assert "TestStrategyLegacy" in captured.out assert "TestStrategyLegacyV1" in captured.out
assert "legacy_strategy.py" not in captured.out assert "legacy_strategy_v1.py" not in captured.out
assert "DefaultStrategy" in captured.out assert "StrategyTestV2" in captured.out
# Test regular output # Test regular output
args = [ args = [
@ -842,9 +820,9 @@ def test_start_list_strategies(mocker, caplog, capsys):
# pargs['config'] = None # pargs['config'] = None
start_list_strategies(pargs) start_list_strategies(pargs)
captured = capsys.readouterr() captured = capsys.readouterr()
assert "TestStrategyLegacy" in captured.out assert "TestStrategyLegacyV1" in captured.out
assert "legacy_strategy.py" in captured.out assert "legacy_strategy_v1.py" in captured.out
assert "DefaultStrategy" in captured.out assert "StrategyTestV2" in captured.out
def test_start_list_hyperopts(mocker, caplog, capsys): def test_start_list_hyperopts(mocker, caplog, capsys):
@ -861,7 +839,7 @@ def test_start_list_hyperopts(mocker, caplog, capsys):
captured = capsys.readouterr() captured = capsys.readouterr()
assert "TestHyperoptLegacy" not in captured.out assert "TestHyperoptLegacy" not in captured.out
assert "legacy_hyperopt.py" not in captured.out assert "legacy_hyperopt.py" not in captured.out
assert "DefaultHyperOpt" in captured.out assert "HyperoptTestSepFile" in captured.out
assert "test_hyperopt.py" not in captured.out assert "test_hyperopt.py" not in captured.out
# Test regular output # Test regular output
@ -876,7 +854,7 @@ def test_start_list_hyperopts(mocker, caplog, capsys):
captured = capsys.readouterr() captured = capsys.readouterr()
assert "TestHyperoptLegacy" not in captured.out assert "TestHyperoptLegacy" not in captured.out
assert "legacy_hyperopt.py" not in captured.out assert "legacy_hyperopt.py" not in captured.out
assert "DefaultHyperOpt" in captured.out assert "HyperoptTestSepFile" in captured.out
def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys): def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys):

View File

@ -384,7 +384,7 @@ def get_default_conf(testdatadir):
"user_data_dir": Path("user_data"), "user_data_dir": Path("user_data"),
"verbosity": 3, "verbosity": 3,
"strategy_path": str(Path(__file__).parent / "strategy" / "strats"), "strategy_path": str(Path(__file__).parent / "strategy" / "strats"),
"strategy": "DefaultStrategy", "strategy": "StrategyTestV2",
"disableparamexport": True, "disableparamexport": True,
"internals": {}, "internals": {},
"export": "none", "export": "none",

View File

@ -33,7 +33,7 @@ def mock_trade_1(fee):
open_rate=0.123, open_rate=0.123,
exchange='binance', exchange='binance',
open_order_id='dry_run_buy_12345', open_order_id='dry_run_buy_12345',
strategy='DefaultStrategy', strategy='StrategyTestV2',
timeframe=5, timeframe=5,
) )
o = Order.parse_from_ccxt_object(mock_order_1(), 'ETH/BTC', 'buy') o = Order.parse_from_ccxt_object(mock_order_1(), 'ETH/BTC', 'buy')
@ -87,7 +87,7 @@ def mock_trade_2(fee):
exchange='binance', exchange='binance',
is_open=False, is_open=False,
open_order_id='dry_run_sell_12345', open_order_id='dry_run_sell_12345',
strategy='DefaultStrategy', strategy='StrategyTestV2',
timeframe=5, timeframe=5,
sell_reason='sell_signal', sell_reason='sell_signal',
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20),
@ -146,7 +146,7 @@ def mock_trade_3(fee):
close_profit_abs=0.000155, close_profit_abs=0.000155,
exchange='binance', exchange='binance',
is_open=False, is_open=False,
strategy='DefaultStrategy', strategy='StrategyTestV2',
timeframe=5, timeframe=5,
sell_reason='roi', sell_reason='roi',
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20),
@ -189,7 +189,7 @@ def mock_trade_4(fee):
open_rate=0.123, open_rate=0.123,
exchange='binance', exchange='binance',
open_order_id='prod_buy_12345', open_order_id='prod_buy_12345',
strategy='DefaultStrategy', strategy='StrategyTestV2',
timeframe=5, timeframe=5,
) )
o = Order.parse_from_ccxt_object(mock_order_4(), 'ETC/BTC', 'buy') o = Order.parse_from_ccxt_object(mock_order_4(), 'ETC/BTC', 'buy')

View File

@ -93,7 +93,7 @@ def test_load_backtest_data_new_format(testdatadir):
def test_load_backtest_data_multi(testdatadir): def test_load_backtest_data_multi(testdatadir):
filename = testdatadir / "backtest-result_multistrat.json" filename = testdatadir / "backtest-result_multistrat.json"
for strategy in ('DefaultStrategy', 'TestStrategy'): for strategy in ('StrategyTestV2', 'TestStrategy'):
bt_data = load_backtest_data(filename, strategy=strategy) bt_data = load_backtest_data(filename, strategy=strategy)
assert isinstance(bt_data, DataFrame) assert isinstance(bt_data, DataFrame)
assert set(bt_data.columns) == set(BT_DATA_COLUMNS_MID) assert set(bt_data.columns) == set(BT_DATA_COLUMNS_MID)
@ -128,7 +128,7 @@ def test_load_trades_from_db(default_conf, fee, mocker):
for col in BT_DATA_COLUMNS: for col in BT_DATA_COLUMNS:
if col not in ['index', 'open_at_end']: if col not in ['index', 'open_at_end']:
assert col in trades.columns assert col in trades.columns
trades = load_trades_from_db(db_url=default_conf['db_url'], strategy='DefaultStrategy') trades = load_trades_from_db(db_url=default_conf['db_url'], strategy='StrategyTestV2')
assert len(trades) == 4 assert len(trades) == 4
trades = load_trades_from_db(db_url=default_conf['db_url'], strategy='NoneStrategy') trades = load_trades_from_db(db_url=default_conf['db_url'], strategy='NoneStrategy')
assert len(trades) == 0 assert len(trades) == 0
@ -186,7 +186,7 @@ def test_load_trades(default_conf, mocker):
db_url=default_conf.get('db_url'), db_url=default_conf.get('db_url'),
exportfilename=default_conf.get('exportfilename'), exportfilename=default_conf.get('exportfilename'),
no_trades=False, no_trades=False,
strategy="DefaultStrategy", strategy="StrategyTestV2",
) )
assert db_mock.call_count == 1 assert db_mock.call_count == 1

View File

@ -380,7 +380,7 @@ def test_file_dump_json_tofile(testdatadir) -> None:
def test_get_timerange(default_conf, mocker, testdatadir) -> None: def test_get_timerange(default_conf, mocker, testdatadir) -> None:
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({'strategy': 'DefaultStrategy'}) default_conf.update({'strategy': 'StrategyTestV2'})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
data = strategy.advise_all_indicators( data = strategy.advise_all_indicators(
@ -398,7 +398,7 @@ def test_get_timerange(default_conf, mocker, testdatadir) -> None:
def test_validate_backtest_data_warn(default_conf, mocker, caplog, testdatadir) -> None: def test_validate_backtest_data_warn(default_conf, mocker, caplog, testdatadir) -> None:
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({'strategy': 'DefaultStrategy'}) default_conf.update({'strategy': 'StrategyTestV2'})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
data = strategy.advise_all_indicators( data = strategy.advise_all_indicators(
@ -422,7 +422,7 @@ def test_validate_backtest_data_warn(default_conf, mocker, caplog, testdatadir)
def test_validate_backtest_data(default_conf, mocker, caplog, testdatadir) -> None: def test_validate_backtest_data(default_conf, mocker, caplog, testdatadir) -> None:
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({'strategy': 'DefaultStrategy'}) default_conf.update({'strategy': 'StrategyTestV2'})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
timerange = TimeRange('index', 'index', 200, 250) timerange = TimeRange('index', 'index', 200, 250)

View File

@ -557,7 +557,7 @@ def test_reload_markets_exception(default_conf, mocker, caplog):
@pytest.mark.parametrize("stake_currency", ['ETH', 'BTC', 'USDT']) @pytest.mark.parametrize("stake_currency", ['ETH', 'BTC', 'USDT'])
def test_validate_stake_currency(default_conf, stake_currency, mocker, caplog): def test_validate_stakecurrency(default_conf, stake_currency, mocker, caplog):
default_conf['stake_currency'] = stake_currency default_conf['stake_currency'] = stake_currency
api_mock = MagicMock() api_mock = MagicMock()
type(api_mock).load_markets = MagicMock(return_value={ type(api_mock).load_markets = MagicMock(return_value={
@ -571,7 +571,7 @@ def test_validate_stake_currency(default_conf, stake_currency, mocker, caplog):
Exchange(default_conf) Exchange(default_conf)
def test_validate_stake_currency_error(default_conf, mocker, caplog): def test_validate_stakecurrency_error(default_conf, mocker, caplog):
default_conf['stake_currency'] = 'XRP' default_conf['stake_currency'] = 'XRP'
api_mock = MagicMock() api_mock = MagicMock()
type(api_mock).load_markets = MagicMock(return_value={ type(api_mock).load_markets = MagicMock(return_value={
@ -587,6 +587,13 @@ def test_validate_stake_currency_error(default_conf, mocker, caplog):
'Available currencies are: BTC, ETH, USDT'): 'Available currencies are: BTC, ETH, USDT'):
Exchange(default_conf) Exchange(default_conf)
type(api_mock).load_markets = MagicMock(side_effect=ccxt.NetworkError('No connection.'))
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
with pytest.raises(OperationalException,
match=r'Could not load markets, therefore cannot start\. Please.*'):
Exchange(default_conf)
def test_get_quote_currencies(default_conf, mocker): def test_get_quote_currencies(default_conf, mocker):
ex = get_patched_exchange(mocker, default_conf) ex = get_patched_exchange(mocker, default_conf)

View File

@ -16,7 +16,7 @@ def hyperopt_conf(default_conf):
hyperconf.update({ hyperconf.update({
'datadir': Path(default_conf['datadir']), 'datadir': Path(default_conf['datadir']),
'runmode': RunMode.HYPEROPT, 'runmode': RunMode.HYPEROPT,
'hyperopt': 'DefaultHyperOpt', 'hyperopt': 'HyperoptTestSepFile',
'hyperopt_loss': 'ShortTradeDurHyperOptLoss', 'hyperopt_loss': 'ShortTradeDurHyperOptLoss',
'hyperopt_path': str(Path(__file__).parent / 'hyperopts'), 'hyperopt_path': str(Path(__file__).parent / 'hyperopts'),
'epochs': 1, 'epochs': 1,

View File

@ -11,7 +11,7 @@ import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.optimize.hyperopt_interface import IHyperOpt from freqtrade.optimize.hyperopt_interface import IHyperOpt
class DefaultHyperOpt(IHyperOpt): class HyperoptTestSepFile(IHyperOpt):
""" """
Default hyperopt provided by the Freqtrade bot. Default hyperopt provided by the Freqtrade bot.
You can override it with your own Hyperopt You can override it with your own Hyperopt

View File

@ -1,7 +1,7 @@
# pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, unused-argument # pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, unused-argument
import random import random
from datetime import timedelta from datetime import datetime, timedelta, timezone
from pathlib import Path from pathlib import Path
from unittest.mock import MagicMock, PropertyMock from unittest.mock import MagicMock, PropertyMock
@ -159,7 +159,7 @@ def test_setup_optimize_configuration_without_arguments(mocker, default_conf, ca
args = [ args = [
'backtesting', 'backtesting',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'StrategyTestV2',
'--export', 'none' '--export', 'none'
] ]
@ -194,7 +194,7 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) ->
args = [ args = [
'backtesting', 'backtesting',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'StrategyTestV2',
'--datadir', '/foo/bar', '--datadir', '/foo/bar',
'--timeframe', '1m', '--timeframe', '1m',
'--enable-position-stacking', '--enable-position-stacking',
@ -244,7 +244,7 @@ def test_setup_optimize_configuration_stake_amount(mocker, default_conf, caplog)
args = [ args = [
'backtesting', 'backtesting',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'StrategyTestV2',
'--stake-amount', '1', '--stake-amount', '1',
'--starting-balance', '2' '--starting-balance', '2'
] ]
@ -255,7 +255,7 @@ def test_setup_optimize_configuration_stake_amount(mocker, default_conf, caplog)
args = [ args = [
'backtesting', 'backtesting',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'StrategyTestV2',
'--stake-amount', '1', '--stake-amount', '1',
'--starting-balance', '0.5' '--starting-balance', '0.5'
] ]
@ -273,7 +273,7 @@ def test_start(mocker, fee, default_conf, caplog) -> None:
args = [ args = [
'backtesting', 'backtesting',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'StrategyTestV2',
] ]
pargs = get_args(args) pargs = get_args(args)
start_backtesting(pargs) start_backtesting(pargs)
@ -306,7 +306,7 @@ def test_backtesting_init(mocker, default_conf, order_types) -> None:
def test_backtesting_init_no_timeframe(mocker, default_conf, caplog) -> None: def test_backtesting_init_no_timeframe(mocker, default_conf, caplog) -> None:
patch_exchange(mocker) patch_exchange(mocker)
del default_conf['timeframe'] del default_conf['timeframe']
default_conf['strategy_list'] = ['DefaultStrategy', default_conf['strategy_list'] = ['StrategyTestV2',
'SampleStrategy'] 'SampleStrategy']
mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.5)) mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.5))
@ -344,7 +344,7 @@ def test_data_to_dataframe_bt(default_conf, mocker, testdatadir) -> None:
assert len(processed['UNITTEST/BTC']) == 102 assert len(processed['UNITTEST/BTC']) == 102
# Load strategy to compare the result between Backtesting function and strategy are the same # Load strategy to compare the result between Backtesting function and strategy are the same
default_conf.update({'strategy': 'DefaultStrategy'}) default_conf.update({'strategy': 'StrategyTestV2'})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
processed2 = strategy.advise_all_indicators(data) processed2 = strategy.advise_all_indicators(data)
@ -445,6 +445,15 @@ def test_backtesting_no_pair_left(default_conf, mocker, caplog, testdatadir) ->
with pytest.raises(OperationalException, match='VolumePairList not allowed for backtesting.'): with pytest.raises(OperationalException, match='VolumePairList not allowed for backtesting.'):
Backtesting(default_conf) Backtesting(default_conf)
default_conf.update({
'pairlists': [{"method": "StaticPairList"}],
'timeframe_detail': '1d',
})
with pytest.raises(OperationalException,
match='Detail timeframe must be smaller than strategy timeframe.'):
Backtesting(default_conf)
def test_backtesting_pairlist_list(default_conf, mocker, caplog, testdatadir, tickers) -> None: def test_backtesting_pairlist_list(default_conf, mocker, caplog, testdatadir, tickers) -> None:
mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True))
@ -477,7 +486,7 @@ def test_backtesting_pairlist_list(default_conf, mocker, caplog, testdatadir, ti
Backtesting(default_conf) Backtesting(default_conf)
# Multiple strategies # Multiple strategies
default_conf['strategy_list'] = ['DefaultStrategy', 'TestStrategyLegacy'] default_conf['strategy_list'] = ['StrategyTestV2', 'TestStrategyLegacyV1']
with pytest.raises(OperationalException, with pytest.raises(OperationalException,
match='PrecisionFilter not allowed for backtesting multiple strategies.'): match='PrecisionFilter not allowed for backtesting multiple strategies.'):
Backtesting(default_conf) Backtesting(default_conf)
@ -495,7 +504,7 @@ def test_backtest__enter_trade(default_conf, fee, mocker) -> None:
pair = 'UNITTEST/BTC' pair = 'UNITTEST/BTC'
row = [ row = [
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0), pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0),
1, # Sell 1, # Buy
0.001, # Open 0.001, # Open
0.0011, # Close 0.0011, # Close
0, # Sell 0, # Sell
@ -549,6 +558,88 @@ def test_backtest__enter_trade(default_conf, fee, mocker) -> None:
backtesting.cleanup() backtesting.cleanup()
def test_backtest__get_sell_trade_entry(default_conf, fee, mocker) -> None:
default_conf['use_sell_signal'] = False
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
patch_exchange(mocker)
default_conf['timeframe_detail'] = '1m'
default_conf['max_open_trades'] = 2
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
pair = 'UNITTEST/BTC'
row = [
pd.Timestamp(year=2020, month=1, day=1, hour=4, minute=55, tzinfo=timezone.utc),
1, # Buy
200, # Open
201, # Close
0, # Sell
195, # Low
201.5, # High
'', # Buy Signal Name
]
trade = backtesting._enter_trade(pair, row=row)
assert isinstance(trade, LocalTrade)
row_sell = [
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0, tzinfo=timezone.utc),
0, # Buy
200, # Open
201, # Close
0, # Sell
195, # Low
210.5, # High
'', # Buy Signal Name
]
row_detail = pd.DataFrame(
[
[
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0, tzinfo=timezone.utc),
1, 200, 199, 0, 197, 200.1, '',
], [
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=1, tzinfo=timezone.utc),
0, 199, 199.5, 0, 199, 199.7, '',
], [
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=2, tzinfo=timezone.utc),
0, 199.5, 200.5, 0, 199, 200.8, '',
], [
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=3, tzinfo=timezone.utc),
0, 200.5, 210.5, 0, 193, 210.5, '', # ROI sell (?)
], [
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=4, tzinfo=timezone.utc),
0, 200, 199, 0, 193, 200.1, '',
],
], columns=["date", "buy", "open", "close", "sell", "low", "high", "buy_tag"]
)
# No data available.
res = backtesting._get_sell_trade_entry(trade, row_sell)
assert res is not None
assert res.sell_reason == SellType.ROI.value
assert res.close_date_utc == datetime(2020, 1, 1, 5, 0, tzinfo=timezone.utc)
# Enter new trade
trade = backtesting._enter_trade(pair, row=row)
assert isinstance(trade, LocalTrade)
# Assign empty ... no result.
backtesting.detail_data[pair] = pd.DataFrame(
[], columns=["date", "buy", "open", "close", "sell", "low", "high", "buy_tag"])
res = backtesting._get_sell_trade_entry(trade, row)
assert res is None
# Assign backtest-detail data
backtesting.detail_data[pair] = row_detail
res = backtesting._get_sell_trade_entry(trade, row_sell)
assert res is not None
assert res.sell_reason == SellType.ROI.value
# Sell at minute 3 (not available above!)
assert res.close_date_utc == datetime(2020, 1, 1, 5, 3, tzinfo=timezone.utc)
assert round(res.close_rate, 3) == round(209.0225, 3)
def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
default_conf['use_sell_signal'] = False default_conf['use_sell_signal'] = False
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
@ -704,7 +795,7 @@ def test_backtest_pricecontours(default_conf, fee, mocker, testdatadir,
def test_backtest_clash_buy_sell(mocker, default_conf, testdatadir): def test_backtest_clash_buy_sell(mocker, default_conf, testdatadir):
# Override the default buy trend function in our default_strategy # Override the default buy trend function in our StrategyTestV2
def fun(dataframe=None, pair=None): def fun(dataframe=None, pair=None):
buy_value = 1 buy_value = 1
sell_value = 1 sell_value = 1
@ -720,7 +811,7 @@ def test_backtest_clash_buy_sell(mocker, default_conf, testdatadir):
def test_backtest_only_sell(mocker, default_conf, testdatadir): def test_backtest_only_sell(mocker, default_conf, testdatadir):
# Override the default buy trend function in our default_strategy # Override the default buy trend function in our StrategyTestV2
def fun(dataframe=None, pair=None): def fun(dataframe=None, pair=None):
buy_value = 0 buy_value = 0
sell_value = 1 sell_value = 1
@ -849,7 +940,7 @@ def test_backtest_start_timerange(default_conf, mocker, caplog, testdatadir):
args = [ args = [
'backtesting', 'backtesting',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'StrategyTestV2',
'--datadir', str(testdatadir), '--datadir', str(testdatadir),
'--timeframe', '1m', '--timeframe', '1m',
'--timerange', '1510694220-1510700340', '--timerange', '1510694220-1510700340',
@ -920,8 +1011,8 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
'--enable-position-stacking', '--enable-position-stacking',
'--disable-max-market-positions', '--disable-max-market-positions',
'--strategy-list', '--strategy-list',
'DefaultStrategy', 'StrategyTestV2',
'TestStrategyLegacy', 'TestStrategyLegacyV1',
] ]
args = get_args(args) args = get_args(args)
start_backtesting(args) start_backtesting(args)
@ -943,8 +1034,8 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
'Backtesting with data from 2017-11-14 21:17:00 ' 'Backtesting with data from 2017-11-14 21:17:00 '
'up to 2017-11-14 22:58:00 (0 days).', 'up to 2017-11-14 22:58:00 (0 days).',
'Parameter --enable-position-stacking detected ...', 'Parameter --enable-position-stacking detected ...',
'Running backtesting for Strategy DefaultStrategy', 'Running backtesting for Strategy StrategyTestV2',
'Running backtesting for Strategy TestStrategyLegacy', 'Running backtesting for Strategy TestStrategyLegacyV1',
] ]
for line in exists: for line in exists:
@ -1024,8 +1115,8 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
'--enable-position-stacking', '--enable-position-stacking',
'--disable-max-market-positions', '--disable-max-market-positions',
'--strategy-list', '--strategy-list',
'DefaultStrategy', 'StrategyTestV2',
'TestStrategyLegacy', 'TestStrategyLegacyV1',
] ]
args = get_args(args) args = get_args(args)
start_backtesting(args) start_backtesting(args)
@ -1041,8 +1132,8 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
'Backtesting with data from 2017-11-14 21:17:00 ' 'Backtesting with data from 2017-11-14 21:17:00 '
'up to 2017-11-14 22:58:00 (0 days).', 'up to 2017-11-14 22:58:00 (0 days).',
'Parameter --enable-position-stacking detected ...', 'Parameter --enable-position-stacking detected ...',
'Running backtesting for Strategy DefaultStrategy', 'Running backtesting for Strategy StrategyTestV2',
'Running backtesting for Strategy TestStrategyLegacy', 'Running backtesting for Strategy TestStrategyLegacyV1',
] ]
for line in exists: for line in exists:
@ -1054,3 +1145,102 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
assert 'LEFT OPEN TRADES REPORT' in captured.out assert 'LEFT OPEN TRADES REPORT' in captured.out
assert '2017-11-14 21:17:00 -> 2017-11-14 22:58:00 | Max open trades : 1' in captured.out assert '2017-11-14 21:17:00 -> 2017-11-14 22:58:00 | Max open trades : 1' in captured.out
assert 'STRATEGY SUMMARY' in captured.out assert 'STRATEGY SUMMARY' in captured.out
@pytest.mark.filterwarnings("ignore:deprecated")
def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker,
caplog, testdatadir, capsys):
# Tests detail-data loading
default_conf.update({
"use_sell_signal": True,
"sell_profit_only": False,
"sell_profit_offset": 0.0,
"ignore_roi_if_buy_signal": False,
})
patch_exchange(mocker)
result1 = pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC'],
'profit_ratio': [0.0, 0.0],
'profit_abs': [0.0, 0.0],
'open_date': pd.to_datetime(['2018-01-29 18:40:00',
'2018-01-30 03:30:00', ], utc=True
),
'close_date': pd.to_datetime(['2018-01-29 20:45:00',
'2018-01-30 05:35:00', ], utc=True),
'trade_duration': [235, 40],
'is_open': [False, False],
'stake_amount': [0.01, 0.01],
'open_rate': [0.104445, 0.10302485],
'close_rate': [0.104969, 0.103541],
'sell_reason': [SellType.ROI, SellType.ROI]
})
result2 = pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC', 'ETH/BTC'],
'profit_ratio': [0.03, 0.01, 0.1],
'profit_abs': [0.01, 0.02, 0.2],
'open_date': pd.to_datetime(['2018-01-29 18:40:00',
'2018-01-30 03:30:00',
'2018-01-30 05:30:00'], utc=True
),
'close_date': pd.to_datetime(['2018-01-29 20:45:00',
'2018-01-30 05:35:00',
'2018-01-30 08:30:00'], utc=True),
'trade_duration': [47, 40, 20],
'is_open': [False, False, False],
'stake_amount': [0.01, 0.01, 0.01],
'open_rate': [0.104445, 0.10302485, 0.122541],
'close_rate': [0.104969, 0.103541, 0.123541],
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
})
backtestmock = MagicMock(side_effect=[
{
'results': result1,
'config': default_conf,
'locks': [],
'rejected_signals': 20,
'final_balance': 1000,
},
{
'results': result2,
'config': default_conf,
'locks': [],
'rejected_signals': 20,
'final_balance': 1000,
}
])
mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist',
PropertyMock(return_value=['XRP/ETH']))
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock)
patched_configuration_load_config_file(mocker, default_conf)
args = [
'backtesting',
'--config', 'config.json',
'--datadir', str(testdatadir),
'--strategy-path', str(Path(__file__).parents[1] / 'strategy/strats'),
'--timeframe', '5m',
'--timeframe-detail', '1m',
'--strategy-list',
'StrategyTestV2'
]
args = get_args(args)
start_backtesting(args)
# check the logs, that will contain the backtest result
exists = [
'Parameter -i/--timeframe detected ... Using timeframe: 5m ...',
'Parameter --timeframe-detail detected, using 1m for intra-candle backtesting ...',
f'Using data directory: {testdatadir} ...',
'Loading data from 2019-10-11 00:00:00 '
'up to 2019-10-13 11:10:00 (2 days).',
'Backtesting with data from 2019-10-11 01:40:00 '
'up to 2019-10-13 11:10:00 (2 days).',
'Running backtesting for Strategy StrategyTestV2',
]
for line in exists:
assert log_has(line, caplog)
captured = capsys.readouterr()
assert 'BACKTESTING REPORT' in captured.out
assert 'SELL REASON STATS' in captured.out
assert 'LEFT OPEN TRADES REPORT' in captured.out

View File

@ -16,7 +16,7 @@ def test_setup_optimize_configuration_without_arguments(mocker, default_conf, ca
args = [ args = [
'edge', 'edge',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'StrategyTestV2',
] ]
config = setup_optimize_configuration(get_args(args), RunMode.EDGE) config = setup_optimize_configuration(get_args(args), RunMode.EDGE)
@ -46,7 +46,7 @@ def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> N
args = [ args = [
'edge', 'edge',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'StrategyTestV2',
'--datadir', '/foo/bar', '--datadir', '/foo/bar',
'--timeframe', '1m', '--timeframe', '1m',
'--timerange', ':100', '--timerange', ':100',
@ -80,7 +80,7 @@ def test_start(mocker, fee, edge_conf, caplog) -> None:
args = [ args = [
'edge', 'edge',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'StrategyTestV2',
] ]
pargs = get_args(args) pargs = get_args(args)
start_edge(pargs) start_edge(pargs)

View File

@ -22,7 +22,7 @@ from freqtrade.strategy.hyper import IntParameter
from tests.conftest import (get_args, log_has, log_has_re, patch_exchange, from tests.conftest import (get_args, log_has, log_has_re, patch_exchange,
patched_configuration_load_config_file) patched_configuration_load_config_file)
from .hyperopts.default_hyperopt import DefaultHyperOpt from .hyperopts.hyperopt_test_sep_file import HyperoptTestSepFile
# TODO-lev: This file # TODO-lev: This file
@ -34,7 +34,7 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca
args = [ args = [
'hyperopt', 'hyperopt',
'--config', 'config.json', '--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt', '--hyperopt', 'HyperoptTestSepFile',
] ]
config = setup_optimize_configuration(get_args(args), RunMode.HYPEROPT) config = setup_optimize_configuration(get_args(args), RunMode.HYPEROPT)
@ -66,7 +66,7 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo
args = [ args = [
'hyperopt', 'hyperopt',
'--config', 'config.json', '--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt', '--hyperopt', 'HyperoptTestSepFile',
'--datadir', '/foo/bar', '--datadir', '/foo/bar',
'--timeframe', '1m', '--timeframe', '1m',
'--timerange', ':100', '--timerange', ':100',
@ -118,7 +118,7 @@ def test_setup_hyperopt_configuration_stake_amount(mocker, default_conf) -> None
args = [ args = [
'hyperopt', 'hyperopt',
'--config', 'config.json', '--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt', '--hyperopt', 'HyperoptTestSepFile',
'--stake-amount', '1', '--stake-amount', '1',
'--starting-balance', '2' '--starting-balance', '2'
] ]
@ -128,7 +128,7 @@ def test_setup_hyperopt_configuration_stake_amount(mocker, default_conf) -> None
args = [ args = [
'hyperopt', 'hyperopt',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'StrategyTestV2',
'--stake-amount', '1', '--stake-amount', '1',
'--starting-balance', '0.5' '--starting-balance', '0.5'
] ]
@ -139,7 +139,7 @@ def test_setup_hyperopt_configuration_stake_amount(mocker, default_conf) -> None
def test_hyperoptresolver(mocker, default_conf, caplog) -> None: def test_hyperoptresolver(mocker, default_conf, caplog) -> None:
patched_configuration_load_config_file(mocker, default_conf) patched_configuration_load_config_file(mocker, default_conf)
hyperopt = DefaultHyperOpt hyperopt = HyperoptTestSepFile
delattr(hyperopt, 'populate_indicators') delattr(hyperopt, 'populate_indicators')
delattr(hyperopt, 'populate_buy_trend') delattr(hyperopt, 'populate_buy_trend')
delattr(hyperopt, 'populate_sell_trend') delattr(hyperopt, 'populate_sell_trend')
@ -147,7 +147,7 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None:
'freqtrade.resolvers.hyperopt_resolver.HyperOptResolver.load_object', 'freqtrade.resolvers.hyperopt_resolver.HyperOptResolver.load_object',
MagicMock(return_value=hyperopt(default_conf)) MagicMock(return_value=hyperopt(default_conf))
) )
default_conf.update({'hyperopt': 'DefaultHyperOpt'}) default_conf.update({'hyperopt': 'HyperoptTestSepFile'})
x = HyperOptResolver.load_hyperopt(default_conf) x = HyperOptResolver.load_hyperopt(default_conf)
assert not hasattr(x, 'populate_indicators') assert not hasattr(x, 'populate_indicators')
assert not hasattr(x, 'populate_buy_trend') assert not hasattr(x, 'populate_buy_trend')
@ -187,7 +187,7 @@ def test_start_not_installed(mocker, default_conf, import_fails) -> None:
args = [ args = [
'hyperopt', 'hyperopt',
'--config', 'config.json', '--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt', '--hyperopt', 'HyperoptTestSepFile',
'--hyperopt-path', '--hyperopt-path',
str(Path(__file__).parent / "hyperopts"), str(Path(__file__).parent / "hyperopts"),
'--epochs', '5', '--epochs', '5',
@ -208,7 +208,7 @@ def test_start(mocker, hyperopt_conf, caplog) -> None:
args = [ args = [
'hyperopt', 'hyperopt',
'--config', 'config.json', '--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt', '--hyperopt', 'HyperoptTestSepFile',
'--hyperopt-loss', 'SharpeHyperOptLossDaily', '--hyperopt-loss', 'SharpeHyperOptLossDaily',
'--epochs', '5' '--epochs', '5'
] ]
@ -232,7 +232,7 @@ def test_start_no_data(mocker, hyperopt_conf) -> None:
args = [ args = [
'hyperopt', 'hyperopt',
'--config', 'config.json', '--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt', '--hyperopt', 'HyperoptTestSepFile',
'--hyperopt-loss', 'SharpeHyperOptLossDaily', '--hyperopt-loss', 'SharpeHyperOptLossDaily',
'--epochs', '5' '--epochs', '5'
] ]
@ -250,7 +250,7 @@ def test_start_filelock(mocker, hyperopt_conf, caplog) -> None:
args = [ args = [
'hyperopt', 'hyperopt',
'--config', 'config.json', '--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt', '--hyperopt', 'HyperoptTestSepFile',
'--hyperopt-loss', 'SharpeHyperOptLossDaily', '--hyperopt-loss', 'SharpeHyperOptLossDaily',
'--epochs', '5' '--epochs', '5'
] ]

View File

@ -167,9 +167,9 @@ def test__pprint_dict():
def test_get_strategy_filename(default_conf): def test_get_strategy_filename(default_conf):
x = HyperoptTools.get_strategy_filename(default_conf, 'DefaultStrategy') x = HyperoptTools.get_strategy_filename(default_conf, 'StrategyTestV2')
assert isinstance(x, Path) assert isinstance(x, Path)
assert x == Path(__file__).parents[1] / 'strategy/strats/default_strategy.py' assert x == Path(__file__).parents[1] / 'strategy/strats/strategy_test_v2.py'
x = HyperoptTools.get_strategy_filename(default_conf, 'NonExistingStrategy') x = HyperoptTools.get_strategy_filename(default_conf, 'NonExistingStrategy')
assert x is None assert x is None
@ -177,7 +177,7 @@ def test_get_strategy_filename(default_conf):
def test_export_params(tmpdir): def test_export_params(tmpdir):
filename = Path(tmpdir) / "DefaultStrategy.json" filename = Path(tmpdir) / "StrategyTestV2.json"
assert not filename.is_file() assert not filename.is_file()
params = { params = {
"params_details": { "params_details": {
@ -205,12 +205,12 @@ def test_export_params(tmpdir):
} }
} }
HyperoptTools.export_params(params, "DefaultStrategy", filename) HyperoptTools.export_params(params, "StrategyTestV2", filename)
assert filename.is_file() assert filename.is_file()
content = rapidjson.load(filename.open('r')) content = rapidjson.load(filename.open('r'))
assert content['strategy_name'] == 'DefaultStrategy' assert content['strategy_name'] == 'StrategyTestV2'
assert 'params' in content assert 'params' in content
assert "buy" in content["params"] assert "buy" in content["params"]
assert "sell" in content["params"] assert "sell" in content["params"]
@ -223,7 +223,7 @@ def test_try_export_params(default_conf, tmpdir, caplog, mocker):
default_conf['disableparamexport'] = False default_conf['disableparamexport'] = False
export_mock = mocker.patch("freqtrade.optimize.hyperopt_tools.HyperoptTools.export_params") export_mock = mocker.patch("freqtrade.optimize.hyperopt_tools.HyperoptTools.export_params")
filename = Path(tmpdir) / "DefaultStrategy.json" filename = Path(tmpdir) / "StrategyTestV2.json"
assert not filename.is_file() assert not filename.is_file()
params = { params = {
"params_details": { "params_details": {
@ -252,17 +252,17 @@ def test_try_export_params(default_conf, tmpdir, caplog, mocker):
FTHYPT_FILEVERSION: 2, FTHYPT_FILEVERSION: 2,
} }
HyperoptTools.try_export_params(default_conf, "DefaultStrategy22", params) HyperoptTools.try_export_params(default_conf, "StrategyTestV222", params)
assert log_has("Strategy not found, not exporting parameter file.", caplog) assert log_has("Strategy not found, not exporting parameter file.", caplog)
assert export_mock.call_count == 0 assert export_mock.call_count == 0
caplog.clear() caplog.clear()
HyperoptTools.try_export_params(default_conf, "DefaultStrategy", params) HyperoptTools.try_export_params(default_conf, "StrategyTestV2", params)
assert export_mock.call_count == 1 assert export_mock.call_count == 1
assert export_mock.call_args_list[0][0][1] == 'DefaultStrategy' assert export_mock.call_args_list[0][0][1] == 'StrategyTestV2'
assert export_mock.call_args_list[0][0][2].name == 'default_strategy.json' assert export_mock.call_args_list[0][0][2].name == 'strategy_test_v2.json'
def test_params_print(capsys): def test_params_print(capsys):

View File

@ -4,7 +4,7 @@ from unittest.mock import MagicMock
import pytest import pytest
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
from freqtrade.optimize.default_hyperopt_loss import ShortTradeDurHyperOptLoss from freqtrade.optimize.hyperopt_loss_short_trade_dur import ShortTradeDurHyperOptLoss
from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver

View File

@ -52,7 +52,7 @@ def test_text_table_bt_results():
def test_generate_backtest_stats(default_conf, testdatadir, tmpdir): def test_generate_backtest_stats(default_conf, testdatadir, tmpdir):
default_conf.update({'strategy': 'DefaultStrategy'}) default_conf.update({'strategy': 'StrategyTestV2'})
StrategyResolver.load_strategy(default_conf) StrategyResolver.load_strategy(default_conf)
results = {'DefStrat': { results = {'DefStrat': {

View File

@ -879,7 +879,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets):
'open_trade_value': 15.1668225, 'open_trade_value': 15.1668225,
'sell_reason': None, 'sell_reason': None,
'sell_order_status': None, 'sell_order_status': None,
'strategy': 'DefaultStrategy', 'strategy': 'StrategyTestV2',
'buy_tag': None, 'buy_tag': None,
'timeframe': 5, 'timeframe': 5,
'exchange': 'binance', 'exchange': 'binance',
@ -984,7 +984,7 @@ def test_api_forcebuy(botclient, mocker, fee):
close_rate=0.265441, close_rate=0.265441,
id=22, id=22,
timeframe=5, timeframe=5,
strategy="DefaultStrategy" strategy="StrategyTestV2"
)) ))
mocker.patch("freqtrade.rpc.RPC._rpc_forcebuy", fbuy_mock) mocker.patch("freqtrade.rpc.RPC._rpc_forcebuy", fbuy_mock)
@ -1034,7 +1034,7 @@ def test_api_forcebuy(botclient, mocker, fee):
'open_trade_value': 0.24605460, 'open_trade_value': 0.24605460,
'sell_reason': None, 'sell_reason': None,
'sell_order_status': None, 'sell_order_status': None,
'strategy': 'DefaultStrategy', 'strategy': 'StrategyTestV2',
'buy_tag': None, 'buy_tag': None,
'timeframe': 5, 'timeframe': 5,
'exchange': 'binance', 'exchange': 'binance',
@ -1101,7 +1101,7 @@ def test_api_pair_candles(botclient, ohlcv_history):
f"{BASE_URI}/pair_candles?limit={amount}&pair=XRP%2FBTC&timeframe={timeframe}") f"{BASE_URI}/pair_candles?limit={amount}&pair=XRP%2FBTC&timeframe={timeframe}")
assert_response(rc) assert_response(rc)
assert 'strategy' in rc.json() assert 'strategy' in rc.json()
assert rc.json()['strategy'] == 'DefaultStrategy' assert rc.json()['strategy'] == 'StrategyTestV2'
assert 'columns' in rc.json() assert 'columns' in rc.json()
assert 'data_start_ts' in rc.json() assert 'data_start_ts' in rc.json()
assert 'data_start' in rc.json() assert 'data_start' in rc.json()
@ -1139,19 +1139,19 @@ def test_api_pair_history(botclient, ohlcv_history):
# No pair # No pair
rc = client_get(client, rc = client_get(client,
f"{BASE_URI}/pair_history?timeframe={timeframe}" f"{BASE_URI}/pair_history?timeframe={timeframe}"
"&timerange=20180111-20180112&strategy=DefaultStrategy") "&timerange=20180111-20180112&strategy=StrategyTestV2")
assert_response(rc, 422) assert_response(rc, 422)
# No Timeframe # No Timeframe
rc = client_get(client, rc = client_get(client,
f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC" f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC"
"&timerange=20180111-20180112&strategy=DefaultStrategy") "&timerange=20180111-20180112&strategy=StrategyTestV2")
assert_response(rc, 422) assert_response(rc, 422)
# No timerange # No timerange
rc = client_get(client, rc = client_get(client,
f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}" f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
"&strategy=DefaultStrategy") "&strategy=StrategyTestV2")
assert_response(rc, 422) assert_response(rc, 422)
# No strategy # No strategy
@ -1163,14 +1163,14 @@ def test_api_pair_history(botclient, ohlcv_history):
# Working # Working
rc = client_get(client, rc = client_get(client,
f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}" f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
"&timerange=20180111-20180112&strategy=DefaultStrategy") "&timerange=20180111-20180112&strategy=StrategyTestV2")
assert_response(rc, 200) assert_response(rc, 200)
assert rc.json()['length'] == 289 assert rc.json()['length'] == 289
assert len(rc.json()['data']) == rc.json()['length'] assert len(rc.json()['data']) == rc.json()['length']
assert 'columns' in rc.json() assert 'columns' in rc.json()
assert 'data' in rc.json() assert 'data' in rc.json()
assert rc.json()['pair'] == 'UNITTEST/BTC' assert rc.json()['pair'] == 'UNITTEST/BTC'
assert rc.json()['strategy'] == 'DefaultStrategy' assert rc.json()['strategy'] == 'StrategyTestV2'
assert rc.json()['data_start'] == '2018-01-11 00:00:00+00:00' assert rc.json()['data_start'] == '2018-01-11 00:00:00+00:00'
assert rc.json()['data_start_ts'] == 1515628800000 assert rc.json()['data_start_ts'] == 1515628800000
assert rc.json()['data_stop'] == '2018-01-12 00:00:00+00:00' assert rc.json()['data_stop'] == '2018-01-12 00:00:00+00:00'
@ -1179,7 +1179,7 @@ def test_api_pair_history(botclient, ohlcv_history):
# No data found # No data found
rc = client_get(client, rc = client_get(client,
f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}" f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
"&timerange=20200111-20200112&strategy=DefaultStrategy") "&timerange=20200111-20200112&strategy=StrategyTestV2")
assert_response(rc, 502) assert_response(rc, 502)
assert rc.json()['error'] == ("Error querying /api/v1/pair_history: " assert rc.json()['error'] == ("Error querying /api/v1/pair_history: "
"No data for UNITTEST/BTC, 5m in 20200111-20200112 found.") "No data for UNITTEST/BTC, 5m in 20200111-20200112 found.")
@ -1217,21 +1217,21 @@ def test_api_strategies(botclient):
assert_response(rc) assert_response(rc)
assert rc.json() == {'strategies': [ assert rc.json() == {'strategies': [
'DefaultStrategy',
'HyperoptableStrategy', 'HyperoptableStrategy',
'TestStrategyLegacy' 'StrategyTestV2',
'TestStrategyLegacyV1'
]} ]}
def test_api_strategy(botclient): def test_api_strategy(botclient):
ftbot, client = botclient ftbot, client = botclient
rc = client_get(client, f"{BASE_URI}/strategy/DefaultStrategy") rc = client_get(client, f"{BASE_URI}/strategy/StrategyTestV2")
assert_response(rc) assert_response(rc)
assert rc.json()['strategy'] == 'DefaultStrategy' assert rc.json()['strategy'] == 'StrategyTestV2'
data = (Path(__file__).parents[1] / "strategy/strats/default_strategy.py").read_text() data = (Path(__file__).parents[1] / "strategy/strats/strategy_test_v2.py").read_text()
assert rc.json()['code'] == data assert rc.json()['code'] == data
rc = client_get(client, f"{BASE_URI}/strategy/NoStrat") rc = client_get(client, f"{BASE_URI}/strategy/NoStrat")
@ -1288,7 +1288,7 @@ def test_api_backtesting(botclient, mocker, fee, caplog):
# start backtesting # start backtesting
data = { data = {
"strategy": "DefaultStrategy", "strategy": "StrategyTestV2",
"timeframe": "5m", "timeframe": "5m",
"timerange": "20180110-20180111", "timerange": "20180110-20180111",
"max_open_trades": 3, "max_open_trades": 3,

View File

@ -1236,7 +1236,7 @@ def test_show_config_handle(default_conf, update, mocker) -> None:
assert msg_mock.call_count == 1 assert msg_mock.call_count == 1
assert '*Mode:* `{}`'.format('Dry-run') in msg_mock.call_args_list[0][0][0] assert '*Mode:* `{}`'.format('Dry-run') in msg_mock.call_args_list[0][0][0]
assert '*Exchange:* `binance`' in msg_mock.call_args_list[0][0][0] assert '*Exchange:* `binance`' in msg_mock.call_args_list[0][0][0]
assert '*Strategy:* `DefaultStrategy`' in msg_mock.call_args_list[0][0][0] assert '*Strategy:* `StrategyTestV2`' in msg_mock.call_args_list[0][0][0]
assert '*Stoploss:* `-0.1`' in msg_mock.call_args_list[0][0][0] assert '*Stoploss:* `-0.1`' in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock() msg_mock.reset_mock()
@ -1245,7 +1245,7 @@ def test_show_config_handle(default_conf, update, mocker) -> None:
assert msg_mock.call_count == 1 assert msg_mock.call_count == 1
assert '*Mode:* `{}`'.format('Dry-run') in msg_mock.call_args_list[0][0][0] assert '*Mode:* `{}`'.format('Dry-run') in msg_mock.call_args_list[0][0][0]
assert '*Exchange:* `binance`' in msg_mock.call_args_list[0][0][0] assert '*Exchange:* `binance`' in msg_mock.call_args_list[0][0][0]
assert '*Strategy:* `DefaultStrategy`' in msg_mock.call_args_list[0][0][0] assert '*Strategy:* `StrategyTestV2`' in msg_mock.call_args_list[0][0][0]
assert '*Initial Stoploss:* `-0.1`' in msg_mock.call_args_list[0][0][0] assert '*Initial Stoploss:* `-0.1`' in msg_mock.call_args_list[0][0][0]

View File

@ -5,5 +5,5 @@ import nonexiting_module # noqa
from freqtrade.strategy.interface import IStrategy from freqtrade.strategy.interface import IStrategy
class TestStrategyLegacy(IStrategy): class TestStrategyLegacyV1(IStrategy):
pass pass

View File

@ -10,7 +10,7 @@ from freqtrade.strategy.interface import IStrategy
# -------------------------------- # --------------------------------
# This class is a sample. Feel free to customize it. # This class is a sample. Feel free to customize it.
class TestStrategyLegacy(IStrategy): class TestStrategyLegacyV1(IStrategy):
""" """
This is a test strategy using the legacy function headers, which will be This is a test strategy using the legacy function headers, which will be
removed in a future update. removed in a future update.

View File

@ -7,9 +7,9 @@ import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.strategy.interface import IStrategy from freqtrade.strategy.interface import IStrategy
class DefaultStrategy(IStrategy): class StrategyTestV2(IStrategy):
""" """
Default Strategy provided by freqtrade bot. Strategy used by tests freqtrade bot.
Please do not modify this strategy, it's intended for internal use only. Please do not modify this strategy, it's intended for internal use only.
Please look at the SampleStrategy in the user_data/strategy directory Please look at the SampleStrategy in the user_data/strategy directory
or strategy repository https://github.com/freqtrade/freqtrade-strategies or strategy repository https://github.com/freqtrade/freqtrade-strategies

View File

@ -4,20 +4,20 @@ from pandas import DataFrame
from freqtrade.persistence.models import Trade from freqtrade.persistence.models import Trade
from .strats.default_strategy import DefaultStrategy from .strats.strategy_test_v2 import StrategyTestV2
def test_default_strategy_structure(): def test_strategy_test_v2_structure():
assert hasattr(DefaultStrategy, 'minimal_roi') assert hasattr(StrategyTestV2, 'minimal_roi')
assert hasattr(DefaultStrategy, 'stoploss') assert hasattr(StrategyTestV2, 'stoploss')
assert hasattr(DefaultStrategy, 'timeframe') assert hasattr(StrategyTestV2, 'timeframe')
assert hasattr(DefaultStrategy, 'populate_indicators') assert hasattr(StrategyTestV2, 'populate_indicators')
assert hasattr(DefaultStrategy, 'populate_buy_trend') assert hasattr(StrategyTestV2, 'populate_buy_trend')
assert hasattr(DefaultStrategy, 'populate_sell_trend') assert hasattr(StrategyTestV2, 'populate_sell_trend')
def test_default_strategy(result, fee): def test_strategy_test_v2(result, fee):
strategy = DefaultStrategy({}) strategy = StrategyTestV2({})
metadata = {'pair': 'ETH/BTC'} metadata = {'pair': 'ETH/BTC'}
assert type(strategy.minimal_roi) is dict assert type(strategy.minimal_roi) is dict

View File

@ -23,11 +23,11 @@ from freqtrade.strategy.interface import SellCheckTuple
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
from tests.conftest import log_has, log_has_re from tests.conftest import log_has, log_has_re
from .strats.default_strategy import DefaultStrategy from .strats.strategy_test_v2 import StrategyTestV2
# Avoid to reinit the same object again and again # Avoid to reinit the same object again and again
_STRATEGY = DefaultStrategy(config={}) _STRATEGY = StrategyTestV2(config={})
_STRATEGY.dp = DataProvider({}, None, None) _STRATEGY.dp = DataProvider({}, None, None)
@ -149,7 +149,7 @@ def test_get_signal_no_sell_column(default_conf, mocker, caplog, ohlcv_history):
def test_ignore_expired_candle(default_conf): def test_ignore_expired_candle(default_conf):
default_conf.update({'strategy': 'DefaultStrategy'}) default_conf.update({'strategy': 'StrategyTestV2'})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
strategy.ignore_buying_expired_candle_after = 60 strategy.ignore_buying_expired_candle_after = 60
@ -234,7 +234,7 @@ def test_assert_df(ohlcv_history, caplog):
def test_advise_all_indicators(default_conf, testdatadir) -> None: def test_advise_all_indicators(default_conf, testdatadir) -> None:
default_conf.update({'strategy': 'DefaultStrategy'}) default_conf.update({'strategy': 'StrategyTestV2'})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
timerange = TimeRange.parse_timerange('1510694220-1510700340') timerange = TimeRange.parse_timerange('1510694220-1510700340')
@ -245,7 +245,7 @@ def test_advise_all_indicators(default_conf, testdatadir) -> None:
def test_advise_all_indicators_copy(mocker, default_conf, testdatadir) -> None: def test_advise_all_indicators_copy(mocker, default_conf, testdatadir) -> None:
default_conf.update({'strategy': 'DefaultStrategy'}) default_conf.update({'strategy': 'StrategyTestV2'})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
aimock = mocker.patch('freqtrade.strategy.interface.IStrategy.advise_indicators') aimock = mocker.patch('freqtrade.strategy.interface.IStrategy.advise_indicators')
timerange = TimeRange.parse_timerange('1510694220-1510700340') timerange = TimeRange.parse_timerange('1510694220-1510700340')
@ -263,7 +263,7 @@ def test_min_roi_reached(default_conf, fee) -> None:
min_roi_list = [{20: 0.05, 55: 0.01, 0: 0.1}, min_roi_list = [{20: 0.05, 55: 0.01, 0: 0.1},
{0: 0.1, 20: 0.05, 55: 0.01}] {0: 0.1, 20: 0.05, 55: 0.01}]
for roi in min_roi_list: for roi in min_roi_list:
default_conf.update({'strategy': 'DefaultStrategy'}) default_conf.update({'strategy': 'StrategyTestV2'})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
strategy.minimal_roi = roi strategy.minimal_roi = roi
trade = Trade( trade = Trade(
@ -302,7 +302,7 @@ def test_min_roi_reached2(default_conf, fee) -> None:
}, },
] ]
for roi in min_roi_list: for roi in min_roi_list:
default_conf.update({'strategy': 'DefaultStrategy'}) default_conf.update({'strategy': 'StrategyTestV2'})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
strategy.minimal_roi = roi strategy.minimal_roi = roi
trade = Trade( trade = Trade(
@ -337,7 +337,7 @@ def test_min_roi_reached3(default_conf, fee) -> None:
30: 0.05, 30: 0.05,
55: 0.30, 55: 0.30,
} }
default_conf.update({'strategy': 'DefaultStrategy'}) default_conf.update({'strategy': 'StrategyTestV2'})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
strategy.minimal_roi = min_roi strategy.minimal_roi = min_roi
trade = Trade( trade = Trade(
@ -390,7 +390,7 @@ def test_min_roi_reached3(default_conf, fee) -> None:
def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, trailing, custom, def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, trailing, custom,
profit2, adjusted2, expected2, custom_stop) -> None: profit2, adjusted2, expected2, custom_stop) -> None:
default_conf.update({'strategy': 'DefaultStrategy'}) default_conf.update({'strategy': 'StrategyTestV2'})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
trade = Trade( trade = Trade(
@ -438,7 +438,7 @@ def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, traili
def test_custom_sell(default_conf, fee, caplog) -> None: def test_custom_sell(default_conf, fee, caplog) -> None:
default_conf.update({'strategy': 'DefaultStrategy'}) default_conf.update({'strategy': 'StrategyTestV2'})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
trade = Trade( trade = Trade(
@ -500,7 +500,7 @@ def test_analyze_ticker_default(ohlcv_history, mocker, caplog) -> None:
advise_sell=sell_mock, advise_sell=sell_mock,
) )
strategy = DefaultStrategy({}) strategy = StrategyTestV2({})
strategy.analyze_ticker(ohlcv_history, {'pair': 'ETH/BTC'}) strategy.analyze_ticker(ohlcv_history, {'pair': 'ETH/BTC'})
assert ind_mock.call_count == 1 assert ind_mock.call_count == 1
assert buy_mock.call_count == 1 assert buy_mock.call_count == 1
@ -531,7 +531,7 @@ def test__analyze_ticker_internal_skip_analyze(ohlcv_history, mocker, caplog) ->
advise_sell=sell_mock, advise_sell=sell_mock,
) )
strategy = DefaultStrategy({}) strategy = StrategyTestV2({})
strategy.dp = DataProvider({}, None, None) strategy.dp = DataProvider({}, None, None)
strategy.process_only_new_candles = True strategy.process_only_new_candles = True
@ -563,7 +563,7 @@ def test__analyze_ticker_internal_skip_analyze(ohlcv_history, mocker, caplog) ->
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_is_pair_locked(default_conf): def test_is_pair_locked(default_conf):
default_conf.update({'strategy': 'DefaultStrategy'}) default_conf.update({'strategy': 'StrategyTestV2'})
PairLocks.timeframe = default_conf['timeframe'] PairLocks.timeframe = default_conf['timeframe']
PairLocks.use_db = True PairLocks.use_db = True
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
@ -616,7 +616,7 @@ def test_is_pair_locked(default_conf):
def test_is_informative_pairs_callback(default_conf): def test_is_informative_pairs_callback(default_conf):
default_conf.update({'strategy': 'TestStrategyLegacy'}) default_conf.update({'strategy': 'TestStrategyLegacyV1'})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
# Should return empty # Should return empty
# Uses fallback to base implementation # Uses fallback to base implementation

View File

@ -18,7 +18,7 @@ def test_search_strategy():
s, _ = StrategyResolver._search_object( s, _ = StrategyResolver._search_object(
directory=default_location, directory=default_location,
object_name='DefaultStrategy', object_name='StrategyTestV2',
add_source=True, add_source=True,
) )
assert issubclass(s, IStrategy) assert issubclass(s, IStrategy)
@ -74,10 +74,10 @@ def test_load_strategy_base64(result, caplog, default_conf):
def test_load_strategy_invalid_directory(result, caplog, default_conf): def test_load_strategy_invalid_directory(result, caplog, default_conf):
default_conf['strategy'] = 'DefaultStrategy' default_conf['strategy'] = 'StrategyTestV2'
extra_dir = Path.cwd() / 'some/path' extra_dir = Path.cwd() / 'some/path'
with pytest.raises(OperationalException): with pytest.raises(OperationalException):
StrategyResolver._load_strategy('DefaultStrategy', config=default_conf, StrategyResolver._load_strategy('StrategyTestV2', config=default_conf,
extra_dir=extra_dir) extra_dir=extra_dir)
assert log_has_re(r'Path .*' + r'some.*path.*' + r'.* does not exist', caplog) assert log_has_re(r'Path .*' + r'some.*path.*' + r'.* does not exist', caplog)
@ -100,7 +100,7 @@ def test_load_strategy_noname(default_conf):
def test_strategy(result, default_conf): def test_strategy(result, default_conf):
default_conf.update({'strategy': 'DefaultStrategy'}) default_conf.update({'strategy': 'StrategyTestV2'})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
metadata = {'pair': 'ETH/BTC'} metadata = {'pair': 'ETH/BTC'}
@ -129,7 +129,7 @@ def test_strategy(result, default_conf):
def test_strategy_override_minimal_roi(caplog, default_conf): def test_strategy_override_minimal_roi(caplog, default_conf):
caplog.set_level(logging.INFO) caplog.set_level(logging.INFO)
default_conf.update({ default_conf.update({
'strategy': 'DefaultStrategy', 'strategy': 'StrategyTestV2',
'minimal_roi': { 'minimal_roi': {
"20": 0.1, "20": 0.1,
"0": 0.5 "0": 0.5
@ -146,7 +146,7 @@ def test_strategy_override_minimal_roi(caplog, default_conf):
def test_strategy_override_stoploss(caplog, default_conf): def test_strategy_override_stoploss(caplog, default_conf):
caplog.set_level(logging.INFO) caplog.set_level(logging.INFO)
default_conf.update({ default_conf.update({
'strategy': 'DefaultStrategy', 'strategy': 'StrategyTestV2',
'stoploss': -0.5 'stoploss': -0.5
}) })
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
@ -158,7 +158,7 @@ def test_strategy_override_stoploss(caplog, default_conf):
def test_strategy_override_trailing_stop(caplog, default_conf): def test_strategy_override_trailing_stop(caplog, default_conf):
caplog.set_level(logging.INFO) caplog.set_level(logging.INFO)
default_conf.update({ default_conf.update({
'strategy': 'DefaultStrategy', 'strategy': 'StrategyTestV2',
'trailing_stop': True 'trailing_stop': True
}) })
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
@ -171,7 +171,7 @@ def test_strategy_override_trailing_stop(caplog, default_conf):
def test_strategy_override_trailing_stop_positive(caplog, default_conf): def test_strategy_override_trailing_stop_positive(caplog, default_conf):
caplog.set_level(logging.INFO) caplog.set_level(logging.INFO)
default_conf.update({ default_conf.update({
'strategy': 'DefaultStrategy', 'strategy': 'StrategyTestV2',
'trailing_stop_positive': -0.1, 'trailing_stop_positive': -0.1,
'trailing_stop_positive_offset': -0.2 'trailing_stop_positive_offset': -0.2
@ -191,7 +191,7 @@ def test_strategy_override_timeframe(caplog, default_conf):
caplog.set_level(logging.INFO) caplog.set_level(logging.INFO)
default_conf.update({ default_conf.update({
'strategy': 'DefaultStrategy', 'strategy': 'StrategyTestV2',
'timeframe': 60, 'timeframe': 60,
'stake_currency': 'ETH' 'stake_currency': 'ETH'
}) })
@ -207,7 +207,7 @@ def test_strategy_override_process_only_new_candles(caplog, default_conf):
caplog.set_level(logging.INFO) caplog.set_level(logging.INFO)
default_conf.update({ default_conf.update({
'strategy': 'DefaultStrategy', 'strategy': 'StrategyTestV2',
'process_only_new_candles': True 'process_only_new_candles': True
}) })
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
@ -227,7 +227,7 @@ def test_strategy_override_order_types(caplog, default_conf):
'stoploss_on_exchange': True, 'stoploss_on_exchange': True,
} }
default_conf.update({ default_conf.update({
'strategy': 'DefaultStrategy', 'strategy': 'StrategyTestV2',
'order_types': order_types 'order_types': order_types
}) })
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
@ -241,12 +241,12 @@ def test_strategy_override_order_types(caplog, default_conf):
" 'stoploss_on_exchange': True}.", caplog) " 'stoploss_on_exchange': True}.", caplog)
default_conf.update({ default_conf.update({
'strategy': 'DefaultStrategy', 'strategy': 'StrategyTestV2',
'order_types': {'buy': 'market'} 'order_types': {'buy': 'market'}
}) })
# Raise error for invalid configuration # Raise error for invalid configuration
with pytest.raises(ImportError, with pytest.raises(ImportError,
match=r"Impossible to load Strategy 'DefaultStrategy'. " match=r"Impossible to load Strategy 'StrategyTestV2'. "
r"Order-types mapping is incomplete."): r"Order-types mapping is incomplete."):
StrategyResolver.load_strategy(default_conf) StrategyResolver.load_strategy(default_conf)
@ -260,7 +260,7 @@ def test_strategy_override_order_tif(caplog, default_conf):
} }
default_conf.update({ default_conf.update({
'strategy': 'DefaultStrategy', 'strategy': 'StrategyTestV2',
'order_time_in_force': order_time_in_force 'order_time_in_force': order_time_in_force
}) })
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
@ -273,12 +273,12 @@ def test_strategy_override_order_tif(caplog, default_conf):
" {'buy': 'fok', 'sell': 'gtc'}.", caplog) " {'buy': 'fok', 'sell': 'gtc'}.", caplog)
default_conf.update({ default_conf.update({
'strategy': 'DefaultStrategy', 'strategy': 'StrategyTestV2',
'order_time_in_force': {'buy': 'fok'} 'order_time_in_force': {'buy': 'fok'}
}) })
# Raise error for invalid configuration # Raise error for invalid configuration
with pytest.raises(ImportError, with pytest.raises(ImportError,
match=r"Impossible to load Strategy 'DefaultStrategy'. " match=r"Impossible to load Strategy 'StrategyTestV2'. "
r"Order-time-in-force mapping is incomplete."): r"Order-time-in-force mapping is incomplete."):
StrategyResolver.load_strategy(default_conf) StrategyResolver.load_strategy(default_conf)
@ -286,7 +286,7 @@ def test_strategy_override_order_tif(caplog, default_conf):
def test_strategy_override_use_sell_signal(caplog, default_conf): def test_strategy_override_use_sell_signal(caplog, default_conf):
caplog.set_level(logging.INFO) caplog.set_level(logging.INFO)
default_conf.update({ default_conf.update({
'strategy': 'DefaultStrategy', 'strategy': 'StrategyTestV2',
}) })
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
assert strategy.use_sell_signal assert strategy.use_sell_signal
@ -296,7 +296,7 @@ def test_strategy_override_use_sell_signal(caplog, default_conf):
assert default_conf['use_sell_signal'] assert default_conf['use_sell_signal']
default_conf.update({ default_conf.update({
'strategy': 'DefaultStrategy', 'strategy': 'StrategyTestV2',
'use_sell_signal': False, 'use_sell_signal': False,
}) })
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
@ -309,7 +309,7 @@ def test_strategy_override_use_sell_signal(caplog, default_conf):
def test_strategy_override_use_sell_profit_only(caplog, default_conf): def test_strategy_override_use_sell_profit_only(caplog, default_conf):
caplog.set_level(logging.INFO) caplog.set_level(logging.INFO)
default_conf.update({ default_conf.update({
'strategy': 'DefaultStrategy', 'strategy': 'StrategyTestV2',
}) })
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
assert not strategy.sell_profit_only assert not strategy.sell_profit_only
@ -319,7 +319,7 @@ def test_strategy_override_use_sell_profit_only(caplog, default_conf):
assert not default_conf['sell_profit_only'] assert not default_conf['sell_profit_only']
default_conf.update({ default_conf.update({
'strategy': 'DefaultStrategy', 'strategy': 'StrategyTestV2',
'sell_profit_only': True, 'sell_profit_only': True,
}) })
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
@ -332,7 +332,7 @@ def test_strategy_override_use_sell_profit_only(caplog, default_conf):
@pytest.mark.filterwarnings("ignore:deprecated") @pytest.mark.filterwarnings("ignore:deprecated")
def test_deprecate_populate_indicators(result, default_conf): def test_deprecate_populate_indicators(result, default_conf):
default_location = Path(__file__).parent / "strats" default_location = Path(__file__).parent / "strats"
default_conf.update({'strategy': 'TestStrategyLegacy', default_conf.update({'strategy': 'TestStrategyLegacyV1',
'strategy_path': default_location}) 'strategy_path': default_location})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
with warnings.catch_warnings(record=True) as w: with warnings.catch_warnings(record=True) as w:
@ -367,7 +367,7 @@ def test_deprecate_populate_indicators(result, default_conf):
def test_call_deprecated_function(result, monkeypatch, default_conf, caplog): def test_call_deprecated_function(result, monkeypatch, default_conf, caplog):
default_location = Path(__file__).parent / "strats" default_location = Path(__file__).parent / "strats"
del default_conf['timeframe'] del default_conf['timeframe']
default_conf.update({'strategy': 'TestStrategyLegacy', default_conf.update({'strategy': 'TestStrategyLegacyV1',
'strategy_path': default_location}) 'strategy_path': default_location})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
metadata = {'pair': 'ETH/BTC'} metadata = {'pair': 'ETH/BTC'}
@ -397,8 +397,7 @@ def test_call_deprecated_function(result, monkeypatch, default_conf, caplog):
def test_strategy_interface_versioning(result, default_conf): def test_strategy_interface_versioning(result, default_conf):
# Tests interface compatibility with Interface version 2. default_conf.update({'strategy': 'StrategyTestV2'})
default_conf.update({'strategy': 'DefaultStrategy'})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
metadata = {'pair': 'ETH/BTC'} metadata = {'pair': 'ETH/BTC'}

View File

@ -123,7 +123,7 @@ def test_parse_args_backtesting_custom() -> None:
'-c', 'test_conf.json', '-c', 'test_conf.json',
'--ticker-interval', '1m', '--ticker-interval', '1m',
'--strategy-list', '--strategy-list',
'DefaultStrategy', 'StrategyTestV2',
'SampleStrategy' 'SampleStrategy'
] ]
call_args = Arguments(args).get_parsed_arg() call_args = Arguments(args).get_parsed_arg()

View File

@ -404,7 +404,7 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
arglist = [ arglist = [
'backtesting', 'backtesting',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'StrategyTestV2',
] ]
args = Arguments(arglist).get_parsed_arg() args = Arguments(arglist).get_parsed_arg()
@ -441,7 +441,7 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non
arglist = [ arglist = [
'backtesting', 'backtesting',
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'DefaultStrategy', '--strategy', 'StrategyTestV2',
'--datadir', '/foo/bar', '--datadir', '/foo/bar',
'--userdir', "/tmp/freqtrade", '--userdir', "/tmp/freqtrade",
'--ticker-interval', '1m', '--ticker-interval', '1m',
@ -498,7 +498,7 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non
'--ticker-interval', '1m', '--ticker-interval', '1m',
'--export', 'trades', '--export', 'trades',
'--strategy-list', '--strategy-list',
'DefaultStrategy', 'StrategyTestV2',
'TestStrategy' 'TestStrategy'
] ]

View File

@ -185,7 +185,7 @@ def test_check_available_stake_amount(default_conf, ticker, mocker, fee, limit_b
limit_buy_order_open['id'] = str(i) limit_buy_order_open['id'] = str(i)
result = freqtrade.wallets.get_trade_stake_amount('ETH/BTC') result = freqtrade.wallets.get_trade_stake_amount('ETH/BTC')
assert pytest.approx(result) == expected[i] assert pytest.approx(result) == expected[i]
freqtrade.execute_buy('ETH/BTC', result) freqtrade.execute_entry('ETH/BTC', result)
else: else:
with pytest.raises(DependencyException): with pytest.raises(DependencyException):
freqtrade.wallets.get_trade_stake_amount('ETH/BTC') freqtrade.wallets.get_trade_stake_amount('ETH/BTC')
@ -584,8 +584,8 @@ def test_create_trades_preopen(default_conf, ticker, fee, mocker, limit_buy_orde
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
# Create 2 existing trades # Create 2 existing trades
freqtrade.execute_buy('ETH/BTC', default_conf['stake_amount']) freqtrade.execute_entry('ETH/BTC', default_conf['stake_amount'])
freqtrade.execute_buy('NEO/BTC', default_conf['stake_amount']) freqtrade.execute_entry('NEO/BTC', default_conf['stake_amount'])
assert len(Trade.get_open_trades()) == 2 assert len(Trade.get_open_trades()) == 2
# Change order_id for new orders # Change order_id for new orders
@ -777,7 +777,7 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None:
assert ("ETH/BTC", default_conf["timeframe"]) in refresh_mock.call_args[0][0] assert ("ETH/BTC", default_conf["timeframe"]) in refresh_mock.call_args[0][0]
def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order_open) -> None: def test_execute_entry(mocker, default_conf, fee, limit_buy_order, limit_buy_order_open) -> None:
patch_RPCManager(mocker) patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
@ -800,7 +800,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
) )
pair = 'ETH/BTC' pair = 'ETH/BTC'
assert not freqtrade.execute_buy(pair, stake_amount) assert not freqtrade.execute_entry(pair, stake_amount)
assert buy_rate_mock.call_count == 1 assert buy_rate_mock.call_count == 1
assert buy_mm.call_count == 0 assert buy_mm.call_count == 0
assert freqtrade.strategy.confirm_trade_entry.call_count == 1 assert freqtrade.strategy.confirm_trade_entry.call_count == 1
@ -808,7 +808,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
limit_buy_order_open['id'] = '22' limit_buy_order_open['id'] = '22'
freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True) freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True)
assert freqtrade.execute_buy(pair, stake_amount) assert freqtrade.execute_entry(pair, stake_amount)
assert buy_rate_mock.call_count == 1 assert buy_rate_mock.call_count == 1
assert buy_mm.call_count == 1 assert buy_mm.call_count == 1
call_args = buy_mm.call_args_list[0][1] call_args = buy_mm.call_args_list[0][1]
@ -827,7 +827,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
# Test calling with price # Test calling with price
limit_buy_order_open['id'] = '33' limit_buy_order_open['id'] = '33'
fix_price = 0.06 fix_price = 0.06
assert freqtrade.execute_buy(pair, stake_amount, fix_price) assert freqtrade.execute_entry(pair, stake_amount, fix_price)
# Make sure get_rate wasn't called again # Make sure get_rate wasn't called again
assert buy_rate_mock.call_count == 0 assert buy_rate_mock.call_count == 0
@ -845,7 +845,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
mocker.patch('freqtrade.exchange.Exchange.create_order', mocker.patch('freqtrade.exchange.Exchange.create_order',
MagicMock(return_value=limit_buy_order)) MagicMock(return_value=limit_buy_order))
assert freqtrade.execute_buy(pair, stake_amount) assert freqtrade.execute_entry(pair, stake_amount)
trade = Trade.query.all()[2] trade = Trade.query.all()[2]
assert trade assert trade
assert trade.open_order_id is None assert trade.open_order_id is None
@ -862,7 +862,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
limit_buy_order['id'] = '555' limit_buy_order['id'] = '555'
mocker.patch('freqtrade.exchange.Exchange.create_order', mocker.patch('freqtrade.exchange.Exchange.create_order',
MagicMock(return_value=limit_buy_order)) MagicMock(return_value=limit_buy_order))
assert freqtrade.execute_buy(pair, stake_amount) assert freqtrade.execute_entry(pair, stake_amount)
trade = Trade.query.all()[3] trade = Trade.query.all()[3]
assert trade assert trade
assert trade.open_order_id == '555' assert trade.open_order_id == '555'
@ -874,7 +874,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
limit_buy_order['id'] = '556' limit_buy_order['id'] = '556'
freqtrade.strategy.custom_stake_amount = lambda **kwargs: 150.0 freqtrade.strategy.custom_stake_amount = lambda **kwargs: 150.0
assert freqtrade.execute_buy(pair, stake_amount) assert freqtrade.execute_entry(pair, stake_amount)
trade = Trade.query.all()[4] trade = Trade.query.all()[4]
assert trade assert trade
assert trade.stake_amount == 150 assert trade.stake_amount == 150
@ -882,7 +882,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
# Exception case # Exception case
limit_buy_order['id'] = '557' limit_buy_order['id'] = '557'
freqtrade.strategy.custom_stake_amount = lambda **kwargs: 20 / 0 freqtrade.strategy.custom_stake_amount = lambda **kwargs: 20 / 0
assert freqtrade.execute_buy(pair, stake_amount) assert freqtrade.execute_entry(pair, stake_amount)
trade = Trade.query.all()[5] trade = Trade.query.all()[5]
assert trade assert trade
assert trade.stake_amount == 2.0 assert trade.stake_amount == 2.0
@ -897,20 +897,20 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
limit_buy_order['id'] = '66' limit_buy_order['id'] = '66'
mocker.patch('freqtrade.exchange.Exchange.create_order', mocker.patch('freqtrade.exchange.Exchange.create_order',
MagicMock(return_value=limit_buy_order)) MagicMock(return_value=limit_buy_order))
assert not freqtrade.execute_buy(pair, stake_amount) assert not freqtrade.execute_entry(pair, stake_amount)
# Fail to get price... # Fail to get price...
mocker.patch('freqtrade.exchange.Exchange.get_rate', MagicMock(return_value=0.0)) mocker.patch('freqtrade.exchange.Exchange.get_rate', MagicMock(return_value=0.0))
with pytest.raises(PricingError, match="Could not determine buy price."): with pytest.raises(PricingError, match="Could not determine buy price."):
freqtrade.execute_buy(pair, stake_amount) freqtrade.execute_entry(pair, stake_amount)
# In case of custom entry price # In case of custom entry price
mocker.patch('freqtrade.exchange.Exchange.get_rate', return_value=0.50) mocker.patch('freqtrade.exchange.Exchange.get_rate', return_value=0.50)
limit_buy_order['status'] = 'open' limit_buy_order['status'] = 'open'
limit_buy_order['id'] = '5566' limit_buy_order['id'] = '5566'
freqtrade.strategy.custom_entry_price = lambda **kwargs: 0.508 freqtrade.strategy.custom_entry_price = lambda **kwargs: 0.508
assert freqtrade.execute_buy(pair, stake_amount) assert freqtrade.execute_entry(pair, stake_amount)
trade = Trade.query.all()[6] trade = Trade.query.all()[6]
assert trade assert trade
assert trade.open_rate_requested == 0.508 assert trade.open_rate_requested == 0.508
@ -925,7 +925,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
get_rate=MagicMock(return_value=10), get_rate=MagicMock(return_value=10),
) )
assert freqtrade.execute_buy(pair, stake_amount) assert freqtrade.execute_entry(pair, stake_amount)
trade = Trade.query.all()[7] trade = Trade.query.all()[7]
assert trade assert trade
assert trade.open_rate_requested == 10 assert trade.open_rate_requested == 10
@ -934,13 +934,13 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
limit_buy_order['status'] = 'open' limit_buy_order['status'] = 'open'
limit_buy_order['id'] = '5568' limit_buy_order['id'] = '5568'
freqtrade.strategy.custom_entry_price = lambda **kwargs: "string price" freqtrade.strategy.custom_entry_price = lambda **kwargs: "string price"
assert freqtrade.execute_buy(pair, stake_amount) assert freqtrade.execute_entry(pair, stake_amount)
trade = Trade.query.all()[8] trade = Trade.query.all()[8]
assert trade assert trade
assert trade.open_rate_requested == 10 assert trade.open_rate_requested == 10
def test_execute_buy_confirm_error(mocker, default_conf, fee, limit_buy_order) -> None: def test_execute_entry_confirm_error(mocker, default_conf, fee, limit_buy_order) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
@ -958,18 +958,18 @@ def test_execute_buy_confirm_error(mocker, default_conf, fee, limit_buy_order) -
pair = 'ETH/BTC' pair = 'ETH/BTC'
freqtrade.strategy.confirm_trade_entry = MagicMock(side_effect=ValueError) freqtrade.strategy.confirm_trade_entry = MagicMock(side_effect=ValueError)
assert freqtrade.execute_buy(pair, stake_amount) assert freqtrade.execute_entry(pair, stake_amount)
limit_buy_order['id'] = '222' limit_buy_order['id'] = '222'
freqtrade.strategy.confirm_trade_entry = MagicMock(side_effect=Exception) freqtrade.strategy.confirm_trade_entry = MagicMock(side_effect=Exception)
assert freqtrade.execute_buy(pair, stake_amount) assert freqtrade.execute_entry(pair, stake_amount)
limit_buy_order['id'] = '2223' limit_buy_order['id'] = '2223'
freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True) freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True)
assert freqtrade.execute_buy(pair, stake_amount) assert freqtrade.execute_entry(pair, stake_amount)
freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=False) freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=False)
assert not freqtrade.execute_buy(pair, stake_amount) assert not freqtrade.execute_entry(pair, stake_amount)
def test_add_stoploss_on_exchange(mocker, default_conf, limit_buy_order) -> None: def test_add_stoploss_on_exchange(mocker, default_conf, limit_buy_order) -> None:
@ -2008,7 +2008,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open,
trade = Trade.query.first() trade = Trade.query.first()
trade.is_open = True trade.is_open = True
# FIX: sniffing logs, suggest handle_trade should not execute_sell # FIX: sniffing logs, suggest handle_trade should not execute_trade_exit
# instead that responsibility should be moved out of handle_trade(), # instead that responsibility should be moved out of handle_trade(),
# we might just want to check if we are in a sell condition without # we might just want to check if we are in a sell condition without
# executing # executing
@ -2635,7 +2635,7 @@ def test_handle_cancel_sell_cancel_exception(mocker, default_conf) -> None:
assert freqtrade.handle_cancel_sell(trade, order, reason) == 'error cancelling order' assert freqtrade.handle_cancel_sell(trade, order, reason) == 'error cancelling order'
def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> None: def test_execute_trade_exit_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> None:
rpc_mock = patch_RPCManager(mocker) rpc_mock = patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)
mocker.patch.multiple( mocker.patch.multiple(
@ -2663,16 +2663,16 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> N
fetch_ticker=ticker_sell_up fetch_ticker=ticker_sell_up
) )
# Prevented sell ... # Prevented sell ...
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'],
sell_reason=SellCheckTuple(sell_type=SellType.ROI)) sell_reason=SellCheckTuple(sell_type=SellType.ROI))
assert rpc_mock.call_count == 0 assert rpc_mock.call_count == 0
assert freqtrade.strategy.confirm_trade_exit.call_count == 1 assert freqtrade.strategy.confirm_trade_exit.call_count == 1
# Repatch with true # Repatch with true
freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=True) freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=True)
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'],
sell_reason=SellCheckTuple(sell_type=SellType.ROI)) sell_reason=SellCheckTuple(sell_type=SellType.ROI))
assert freqtrade.strategy.confirm_trade_exit.call_count == 1 assert freqtrade.strategy.confirm_trade_exit.call_count == 1
assert rpc_mock.call_count == 1 assert rpc_mock.call_count == 1
@ -2699,7 +2699,7 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> N
} == last_msg } == last_msg
def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker) -> None: def test_execute_trade_exit_down(default_conf, ticker, fee, ticker_sell_down, mocker) -> None:
rpc_mock = patch_RPCManager(mocker) rpc_mock = patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)
mocker.patch.multiple( mocker.patch.multiple(
@ -2724,8 +2724,8 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker)
fetch_ticker=ticker_sell_down fetch_ticker=ticker_sell_down
) )
freqtrade.execute_sell(trade=trade, limit=ticker_sell_down()['bid'], freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'],
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
assert rpc_mock.call_count == 2 assert rpc_mock.call_count == 2
last_msg = rpc_mock.call_args_list[-1][0][0] last_msg = rpc_mock.call_args_list[-1][0][0]
@ -2751,7 +2751,8 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker)
} == last_msg } == last_msg
def test_execute_sell_custom_exit_price(default_conf, ticker, fee, ticker_sell_up, mocker) -> None: def test_execute_trade_exit_custom_exit_price(default_conf, ticker, fee, ticker_sell_up,
mocker) -> None:
rpc_mock = patch_RPCManager(mocker) rpc_mock = patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)
mocker.patch.multiple( mocker.patch.multiple(
@ -2784,8 +2785,8 @@ def test_execute_sell_custom_exit_price(default_conf, ticker, fee, ticker_sell_u
# Set a custom exit price # Set a custom exit price
freqtrade.strategy.custom_exit_price = lambda **kwargs: 1.170e-05 freqtrade.strategy.custom_exit_price = lambda **kwargs: 1.170e-05
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'],
sell_reason=SellCheckTuple(sell_type=SellType.SELL_SIGNAL)) sell_reason=SellCheckTuple(sell_type=SellType.SELL_SIGNAL))
# Sell price must be different to default bid price # Sell price must be different to default bid price
@ -2815,8 +2816,8 @@ def test_execute_sell_custom_exit_price(default_conf, ticker, fee, ticker_sell_u
} == last_msg } == last_msg
def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fee, def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(default_conf, ticker, fee,
ticker_sell_down, mocker) -> None: ticker_sell_down, mocker) -> None:
rpc_mock = patch_RPCManager(mocker) rpc_mock = patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)
mocker.patch.multiple( mocker.patch.multiple(
@ -2846,8 +2847,8 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe
# Setting trade stoploss to 0.01 # Setting trade stoploss to 0.01
trade.stop_loss = 0.00001099 * 0.99 trade.stop_loss = 0.00001099 * 0.99
freqtrade.execute_sell(trade=trade, limit=ticker_sell_down()['bid'], freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'],
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
assert rpc_mock.call_count == 2 assert rpc_mock.call_count == 2
last_msg = rpc_mock.call_args_list[-1][0][0] last_msg = rpc_mock.call_args_list[-1][0][0]
@ -2874,7 +2875,8 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe
} == last_msg } == last_msg
def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, caplog) -> None: def test_execute_trade_exit_sloe_cancel_exception(
mocker, default_conf, ticker, fee, caplog) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order', mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order',
side_effect=InvalidOrderException()) side_effect=InvalidOrderException())
@ -2901,14 +2903,14 @@ def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, c
freqtrade.config['dry_run'] = False freqtrade.config['dry_run'] = False
trade.stoploss_order_id = "abcd" trade.stoploss_order_id = "abcd"
freqtrade.execute_sell(trade=trade, limit=1234, freqtrade.execute_trade_exit(trade=trade, limit=1234,
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
assert create_order_mock.call_count == 2 assert create_order_mock.call_count == 2
assert log_has('Could not cancel stoploss order abcd', caplog) assert log_has('Could not cancel stoploss order abcd', caplog)
def test_execute_sell_with_stoploss_on_exchange(default_conf, ticker, fee, ticker_sell_up, def test_execute_trade_exit_with_stoploss_on_exchange(default_conf, ticker, fee, ticker_sell_up,
mocker) -> None: mocker) -> None:
default_conf['exchange']['name'] = 'binance' default_conf['exchange']['name'] = 'binance'
rpc_mock = patch_RPCManager(mocker) rpc_mock = patch_RPCManager(mocker)
@ -2952,8 +2954,8 @@ def test_execute_sell_with_stoploss_on_exchange(default_conf, ticker, fee, ticke
fetch_ticker=ticker_sell_up fetch_ticker=ticker_sell_up
) )
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'],
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
trade = Trade.query.first() trade = Trade.query.first()
assert trade assert trade
@ -2961,8 +2963,8 @@ def test_execute_sell_with_stoploss_on_exchange(default_conf, ticker, fee, ticke
assert rpc_mock.call_count == 3 assert rpc_mock.call_count == 3
def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, ticker, fee, def test_may_execute_trade_exit_after_stoploss_on_exchange_hit(default_conf, ticker, fee,
mocker) -> None: mocker) -> None:
default_conf['exchange']['name'] = 'binance' default_conf['exchange']['name'] = 'binance'
rpc_mock = patch_RPCManager(mocker) rpc_mock = patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)
@ -3033,8 +3035,8 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, ticker, f
assert rpc_mock.call_args_list[2][0][0]['type'] == RPCMessageType.SELL assert rpc_mock.call_args_list[2][0][0]['type'] == RPCMessageType.SELL
def test_execute_sell_market_order(default_conf, ticker, fee, def test_execute_trade_exit_market_order(default_conf, ticker, fee,
ticker_sell_up, mocker) -> None: ticker_sell_up, mocker) -> None:
rpc_mock = patch_RPCManager(mocker) rpc_mock = patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)
mocker.patch.multiple( mocker.patch.multiple(
@ -3060,8 +3062,8 @@ def test_execute_sell_market_order(default_conf, ticker, fee,
) )
freqtrade.config['order_types']['sell'] = 'market' freqtrade.config['order_types']['sell'] = 'market'
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'],
sell_reason=SellCheckTuple(sell_type=SellType.ROI)) sell_reason=SellCheckTuple(sell_type=SellType.ROI))
assert not trade.is_open assert not trade.is_open
assert trade.close_profit == 0.0620716 assert trade.close_profit == 0.0620716
@ -3091,8 +3093,8 @@ def test_execute_sell_market_order(default_conf, ticker, fee,
} == last_msg } == last_msg
def test_execute_sell_insufficient_funds_error(default_conf, ticker, fee, def test_execute_trade_exit_insufficient_funds_error(default_conf, ticker, fee,
ticker_sell_up, mocker) -> None: ticker_sell_up, mocker) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade = get_patched_freqtradebot(mocker, default_conf)
mock_insuf = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_insufficient_funds') mock_insuf = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_insufficient_funds')
mocker.patch.multiple( mocker.patch.multiple(
@ -3119,8 +3121,8 @@ def test_execute_sell_insufficient_funds_error(default_conf, ticker, fee,
) )
sell_reason = SellCheckTuple(sell_type=SellType.ROI) sell_reason = SellCheckTuple(sell_type=SellType.ROI)
assert not freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], assert not freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'],
sell_reason=sell_reason) sell_reason=sell_reason)
assert mock_insuf.call_count == 1 assert mock_insuf.call_count == 1
@ -3376,8 +3378,8 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo
fetch_ticker=ticker_sell_down fetch_ticker=ticker_sell_down
) )
freqtrade.execute_sell(trade=trade, limit=ticker_sell_down()['bid'], freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'],
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
trade.close(ticker_sell_down()['bid']) trade.close(ticker_sell_down()['bid'])
assert freqtrade.strategy.is_pair_locked(trade.pair) assert freqtrade.strategy.is_pair_locked(trade.pair)

View File

@ -9,7 +9,7 @@ from freqtrade.strategy.interface import SellCheckTuple
from tests.conftest import get_patched_freqtradebot, patch_get_signal from tests.conftest import get_patched_freqtradebot, patch_get_signal
def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee, def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee,
limit_buy_order, mocker) -> None: limit_buy_order, mocker) -> None:
""" """
Tests workflow of selling stoploss_on_exchange. Tests workflow of selling stoploss_on_exchange.

View File

@ -70,7 +70,6 @@ def test_add_indicators(default_conf, testdatadir, caplog):
indicators1 = {"ema10": {}} indicators1 = {"ema10": {}}
indicators2 = {"macd": {"color": "red"}} indicators2 = {"macd": {"color": "red"}}
default_conf.update({'strategy': 'DefaultStrategy'})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
# Generate buy/sell signals and indicators # Generate buy/sell signals and indicators
@ -112,7 +111,6 @@ def test_add_areas(default_conf, testdatadir, caplog):
"fill_to": "macdhist"}} "fill_to": "macdhist"}}
ind_plain = {"macd": {"fill_to": "macdhist"}} ind_plain = {"macd": {"fill_to": "macdhist"}}
default_conf.update({'strategy': 'DefaultStrategy'})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
# Generate buy/sell signals and indicators # Generate buy/sell signals and indicators
@ -239,7 +237,6 @@ def test_generate_candlestick_graph_no_trades(default_conf, mocker, testdatadir)
data = history.load_pair_history(pair=pair, timeframe='1m', data = history.load_pair_history(pair=pair, timeframe='1m',
datadir=testdatadir, timerange=timerange) datadir=testdatadir, timerange=timerange)
default_conf.update({'strategy': 'DefaultStrategy'})
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
# Generate buy/sell signals and indicators # Generate buy/sell signals and indicators

View File

@ -157,13 +157,13 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_r
assert result == result1 assert result == result1
# create one trade, order amount should be 'balance / (max_open_trades - num_open_trades)' # create one trade, order amount should be 'balance / (max_open_trades - num_open_trades)'
freqtrade.execute_buy('ETH/USDT', result) freqtrade.execute_entry('ETH/USDT', result)
result = freqtrade.wallets.get_trade_stake_amount('LTC/USDT') result = freqtrade.wallets.get_trade_stake_amount('LTC/USDT')
assert result == result1 assert result == result1
# create 2 trades, order amount should be None # create 2 trades, order amount should be None
freqtrade.execute_buy('LTC/BTC', result) freqtrade.execute_entry('LTC/BTC', result)
result = freqtrade.wallets.get_trade_stake_amount('XRP/USDT') result = freqtrade.wallets.get_trade_stake_amount('XRP/USDT')
assert result == 0 assert result == 0

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long