Merge branch 'freqtrade:develop' into develop
This commit is contained in:
commit
ba05e1a308
@ -20,7 +20,9 @@ All profit calculations of Freqtrade include fees. For Backtesting / Hyperopt /
|
|||||||
## Bot execution logic
|
## Bot execution logic
|
||||||
|
|
||||||
Starting freqtrade in dry-run or live mode (using `freqtrade trade`) will start the bot and start the bot iteration loop.
|
Starting freqtrade in dry-run or live mode (using `freqtrade trade`) will start the bot and start the bot iteration loop.
|
||||||
By default, loop runs every few seconds (`internals.process_throttle_secs`) and does roughly the following in the following sequence:
|
This will also run the `bot_start()` callback.
|
||||||
|
|
||||||
|
By default, the bot loop runs every few seconds (`internals.process_throttle_secs`) and performs the following actions:
|
||||||
|
|
||||||
* Fetch open trades from persistence.
|
* Fetch open trades from persistence.
|
||||||
* Calculate current list of tradable pairs.
|
* Calculate current list of tradable pairs.
|
||||||
@ -54,6 +56,7 @@ This loop will be repeated again and again until the bot is stopped.
|
|||||||
[backtesting](backtesting.md) or [hyperopt](hyperopt.md) do only part of the above logic, since most of the trading operations are fully simulated.
|
[backtesting](backtesting.md) or [hyperopt](hyperopt.md) do only part of the above logic, since most of the trading operations are fully simulated.
|
||||||
|
|
||||||
* Load historic data for configured pairlist.
|
* Load historic data for configured pairlist.
|
||||||
|
* Calls `bot_start()` once.
|
||||||
* Calls `bot_loop_start()` once.
|
* Calls `bot_loop_start()` once.
|
||||||
* Calculate indicators (calls `populate_indicators()` once per pair).
|
* Calculate indicators (calls `populate_indicators()` once per pair).
|
||||||
* Calculate entry / exit signals (calls `populate_entry_trend()` and `populate_exit_trend()` once per pair).
|
* Calculate entry / exit signals (calls `populate_entry_trend()` and `populate_exit_trend()` once per pair).
|
||||||
|
@ -959,6 +959,29 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
logger.debug(f'Found no {exit_signal_type} signal for %s.', trade)
|
logger.debug(f'Found no {exit_signal_type} signal for %s.', trade)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _check_and_execute_exit(self, trade: Trade, exit_rate: float,
|
||||||
|
enter: bool, exit_: bool, exit_tag: Optional[str]) -> bool:
|
||||||
|
"""
|
||||||
|
Check and execute trade exit
|
||||||
|
"""
|
||||||
|
exits: List[ExitCheckTuple] = self.strategy.should_exit(
|
||||||
|
trade,
|
||||||
|
exit_rate,
|
||||||
|
datetime.now(timezone.utc),
|
||||||
|
enter=enter,
|
||||||
|
exit_=exit_,
|
||||||
|
force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0
|
||||||
|
)
|
||||||
|
for should_exit in exits:
|
||||||
|
if should_exit.exit_flag:
|
||||||
|
exit_tag1 = exit_tag if should_exit.exit_type == ExitType.EXIT_SIGNAL else None
|
||||||
|
logger.info(f'Exit for {trade.pair} detected. Reason: {should_exit.exit_type}'
|
||||||
|
f'{f" Tag: {exit_tag1}" if exit_tag1 is not None else ""}')
|
||||||
|
exited = self.execute_trade_exit(trade, exit_rate, should_exit, exit_tag=exit_tag1)
|
||||||
|
if exited:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def create_stoploss_order(self, trade: Trade, stop_price: float) -> bool:
|
def create_stoploss_order(self, trade: Trade, stop_price: float) -> bool:
|
||||||
"""
|
"""
|
||||||
Abstracts creating stoploss orders from the logic.
|
Abstracts creating stoploss orders from the logic.
|
||||||
@ -1110,28 +1133,6 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
logger.warning(f"Could not create trailing stoploss order "
|
logger.warning(f"Could not create trailing stoploss order "
|
||||||
f"for pair {trade.pair}.")
|
f"for pair {trade.pair}.")
|
||||||
|
|
||||||
def _check_and_execute_exit(self, trade: Trade, exit_rate: float,
|
|
||||||
enter: bool, exit_: bool, exit_tag: Optional[str]) -> bool:
|
|
||||||
"""
|
|
||||||
Check and execute trade exit
|
|
||||||
"""
|
|
||||||
exits: List[ExitCheckTuple] = self.strategy.should_exit(
|
|
||||||
trade,
|
|
||||||
exit_rate,
|
|
||||||
datetime.now(timezone.utc),
|
|
||||||
enter=enter,
|
|
||||||
exit_=exit_,
|
|
||||||
force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0
|
|
||||||
)
|
|
||||||
for should_exit in exits:
|
|
||||||
if should_exit.exit_flag:
|
|
||||||
logger.info(f'Exit for {trade.pair} detected. Reason: {should_exit.exit_type}'
|
|
||||||
f'{f" Tag: {exit_tag}" if exit_tag is not None else ""}')
|
|
||||||
exited = self.execute_trade_exit(trade, exit_rate, should_exit, exit_tag=exit_tag)
|
|
||||||
if exited:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def manage_open_orders(self) -> None:
|
def manage_open_orders(self) -> None:
|
||||||
"""
|
"""
|
||||||
Management of open orders on exchange. Unfilled orders might be cancelled if timeout
|
Management of open orders on exchange. Unfilled orders might be cancelled if timeout
|
||||||
|
@ -189,6 +189,7 @@ class Backtesting:
|
|||||||
self.strategy.order_types['stoploss_on_exchange'] = False
|
self.strategy.order_types['stoploss_on_exchange'] = False
|
||||||
|
|
||||||
self.strategy.ft_bot_start()
|
self.strategy.ft_bot_start()
|
||||||
|
strategy_safe_wrapper(self.strategy.bot_loop_start, supress_error=True)()
|
||||||
|
|
||||||
def _load_protections(self, strategy: IStrategy):
|
def _load_protections(self, strategy: IStrategy):
|
||||||
if self.config.get('enable_protections', False):
|
if self.config.get('enable_protections', False):
|
||||||
@ -1140,8 +1141,6 @@ class Backtesting:
|
|||||||
backtest_start_time = datetime.now(timezone.utc)
|
backtest_start_time = datetime.now(timezone.utc)
|
||||||
self._set_strategy(strat)
|
self._set_strategy(strat)
|
||||||
|
|
||||||
strategy_safe_wrapper(self.strategy.bot_loop_start, supress_error=True)()
|
|
||||||
|
|
||||||
# Use max_open_trades in backtesting, except --disable-max-market-positions is set
|
# Use max_open_trades in backtesting, except --disable-max-market-positions is set
|
||||||
if self.config.get('use_max_market_positions', True):
|
if self.config.get('use_max_market_positions', True):
|
||||||
# Must come from strategy config, as the strategy may modify this setting.
|
# Must come from strategy config, as the strategy may modify this setting.
|
||||||
|
@ -2,17 +2,17 @@ numpy==1.23.0
|
|||||||
pandas==1.4.3
|
pandas==1.4.3
|
||||||
pandas-ta==0.3.14b
|
pandas-ta==0.3.14b
|
||||||
|
|
||||||
ccxt==1.89.14
|
ccxt==1.89.96
|
||||||
# Pin cryptography for now due to rust build errors with piwheels
|
# Pin cryptography for now due to rust build errors with piwheels
|
||||||
cryptography==37.0.2
|
cryptography==37.0.2
|
||||||
aiohttp==3.8.1
|
aiohttp==3.8.1
|
||||||
SQLAlchemy==1.4.39
|
SQLAlchemy==1.4.39
|
||||||
python-telegram-bot==13.12
|
python-telegram-bot==13.13
|
||||||
arrow==1.2.2
|
arrow==1.2.2
|
||||||
cachetools==4.2.2
|
cachetools==4.2.2
|
||||||
requests==2.28.0
|
requests==2.28.1
|
||||||
urllib3==1.26.9
|
urllib3==1.26.9
|
||||||
jsonschema==4.6.0
|
jsonschema==4.6.1
|
||||||
TA-Lib==0.4.24
|
TA-Lib==0.4.24
|
||||||
technical==1.3.0
|
technical==1.3.0
|
||||||
tabulate==0.8.10
|
tabulate==0.8.10
|
||||||
@ -28,14 +28,14 @@ py_find_1st==1.1.5
|
|||||||
# Load ticker files 30% faster
|
# Load ticker files 30% faster
|
||||||
python-rapidjson==1.6
|
python-rapidjson==1.6
|
||||||
# Properly format api responses
|
# Properly format api responses
|
||||||
orjson==3.7.3
|
orjson==3.7.6
|
||||||
|
|
||||||
# Notify systemd
|
# Notify systemd
|
||||||
sdnotify==0.3.2
|
sdnotify==0.3.2
|
||||||
|
|
||||||
# API Server
|
# API Server
|
||||||
fastapi==0.78.0
|
fastapi==0.78.0
|
||||||
uvicorn==0.17.6
|
uvicorn==0.18.2
|
||||||
pyjwt==2.4.0
|
pyjwt==2.4.0
|
||||||
aiofiles==0.8.0
|
aiofiles==0.8.0
|
||||||
psutil==5.9.1
|
psutil==5.9.1
|
||||||
@ -44,7 +44,7 @@ psutil==5.9.1
|
|||||||
colorama==0.4.5
|
colorama==0.4.5
|
||||||
# Building config files interactively
|
# Building config files interactively
|
||||||
questionary==1.10.0
|
questionary==1.10.0
|
||||||
prompt-toolkit==3.0.29
|
prompt-toolkit==3.0.30
|
||||||
# Extensions to datetime library
|
# Extensions to datetime library
|
||||||
python-dateutil==2.8.2
|
python-dateutil==2.8.2
|
||||||
|
|
||||||
|
@ -861,6 +861,7 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmpdir, fee) -> None:
|
|||||||
hyperopt.backtesting.exchange.get_max_leverage = MagicMock(return_value=1.0)
|
hyperopt.backtesting.exchange.get_max_leverage = MagicMock(return_value=1.0)
|
||||||
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
|
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
|
||||||
assert isinstance(hyperopt.backtesting.strategy.buy_rsi, IntParameter)
|
assert isinstance(hyperopt.backtesting.strategy.buy_rsi, IntParameter)
|
||||||
|
assert hyperopt.backtesting.strategy.bot_loop_started is True
|
||||||
|
|
||||||
assert hyperopt.backtesting.strategy.buy_rsi.in_space is True
|
assert hyperopt.backtesting.strategy.buy_rsi.in_space is True
|
||||||
assert hyperopt.backtesting.strategy.buy_rsi.value == 35
|
assert hyperopt.backtesting.strategy.buy_rsi.value == 35
|
||||||
|
@ -44,6 +44,11 @@ class HyperoptableStrategy(StrategyTestV2):
|
|||||||
})
|
})
|
||||||
return prot
|
return prot
|
||||||
|
|
||||||
|
bot_loop_started = False
|
||||||
|
|
||||||
|
def bot_loop_start(self):
|
||||||
|
self.bot_loop_started = True
|
||||||
|
|
||||||
def bot_start(self, **kwargs) -> None:
|
def bot_start(self, **kwargs) -> None:
|
||||||
"""
|
"""
|
||||||
Parameters can also be defined here ...
|
Parameters can also be defined here ...
|
||||||
|
@ -3951,9 +3951,9 @@ def test_ignore_roi_if_entry_signal(default_conf_usdt, limit_order, limit_order_
|
|||||||
|
|
||||||
# Test if entry-signal is absent (should sell due to roi = true)
|
# Test if entry-signal is absent (should sell due to roi = true)
|
||||||
if is_short:
|
if is_short:
|
||||||
patch_get_signal(freqtrade, enter_long=False, exit_short=False)
|
patch_get_signal(freqtrade, enter_long=False, exit_short=False, exit_tag='something')
|
||||||
else:
|
else:
|
||||||
patch_get_signal(freqtrade, enter_long=False, exit_long=False)
|
patch_get_signal(freqtrade, enter_long=False, exit_long=False, exit_tag='something')
|
||||||
assert freqtrade.handle_trade(trade) is True
|
assert freqtrade.handle_trade(trade) is True
|
||||||
assert trade.exit_reason == ExitType.ROI.value
|
assert trade.exit_reason == ExitType.ROI.value
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user