Merge pull request #5929 from dvdmchl/develop

Telegram and log prints strategy version.
This commit is contained in:
Matthias 2021-12-04 15:16:42 +01:00 committed by GitHub
commit 243e59cabb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 49 additions and 5 deletions

View File

@ -127,6 +127,21 @@ The provided exit-tag is then used as sell-reason - and shown as such in backtes
!!! Note !!! Note
`sell_reason` is limited to 100 characters, remaining data will be truncated. `sell_reason` is limited to 100 characters, remaining data will be truncated.
## Strategy version
You can implement custom strategy versioning by using the "version" method, and returning the version you would like this strategy to have.
``` python
def version(self) -> str:
"""
Returns version of the strategy.
"""
return "1.1"
```
!!! Note
You should make sure to implement proper version control (like a git repository) alongside this, as freqtrade will not keep historic versions of your strategy, so it's up to the user to be able to eventually roll back to a prior version of the strategy.
## Derived strategies ## Derived strategies
The strategies can be derived from other strategies. This avoids duplication of your custom strategy code. You can use this technique to override small parts of your main strategy, leaving the rest untouched: The strategies can be derived from other strategies. This avoids duplication of your custom strategy code. You can use this technique to override small parts of your main strategy, leaving the rest untouched:

View File

@ -145,6 +145,7 @@ class OrderTypes(BaseModel):
class ShowConfig(BaseModel): class ShowConfig(BaseModel):
version: str version: str
strategy_version: Optional[str]
api_version: float api_version: float
dry_run: bool dry_run: bool
stake_currency: str stake_currency: str

View File

@ -121,9 +121,11 @@ def edge(rpc: RPC = Depends(get_rpc)):
@router.get('/show_config', response_model=ShowConfig, tags=['info']) @router.get('/show_config', response_model=ShowConfig, tags=['info'])
def show_config(rpc: Optional[RPC] = Depends(get_rpc_optional), config=Depends(get_config)): def show_config(rpc: Optional[RPC] = Depends(get_rpc_optional), config=Depends(get_config)):
state = '' state = ''
strategy_version = None
if rpc: if rpc:
state = rpc._freqtrade.state state = rpc._freqtrade.state
resp = RPC._rpc_show_config(config, state) strategy_version = rpc._freqtrade.strategy.version()
resp = RPC._rpc_show_config(config, state, strategy_version)
resp['api_version'] = API_VERSION resp['api_version'] = API_VERSION
return resp return resp

View File

@ -98,7 +98,8 @@ class RPC:
self._fiat_converter = CryptoToFiatConverter() self._fiat_converter = CryptoToFiatConverter()
@staticmethod @staticmethod
def _rpc_show_config(config, botstate: Union[State, str]) -> Dict[str, Any]: def _rpc_show_config(config, botstate: Union[State, str],
strategy_version: Optional[str] = None) -> Dict[str, Any]:
""" """
Return a dict of config options. Return a dict of config options.
Explicitly does NOT return the full config to avoid leakage of sensitive Explicitly does NOT return the full config to avoid leakage of sensitive
@ -106,6 +107,7 @@ class RPC:
""" """
val = { val = {
'version': __version__, 'version': __version__,
'strategy_version': strategy_version,
'dry_run': config['dry_run'], 'dry_run': config['dry_run'],
'stake_currency': config['stake_currency'], 'stake_currency': config['stake_currency'],
'stake_currency_decimals': decimals_per_coin(config['stake_currency']), 'stake_currency_decimals': decimals_per_coin(config['stake_currency']),

View File

@ -1305,7 +1305,12 @@ class Telegram(RPCHandler):
:param update: message update :param update: message update
:return: None :return: None
""" """
self._send_msg('*Version:* `{}`'.format(__version__)) strategy_version = self._rpc._freqtrade.strategy.version()
version_string = f'*Version:* `{__version__}`'
if strategy_version is not None:
version_string += f', *Strategy version: * `{strategy_version}`'
self._send_msg(version_string)
@authorized_only @authorized_only
def _show_config(self, update: Update, context: CallbackContext) -> None: def _show_config(self, update: Update, context: CallbackContext) -> None:

View File

@ -394,6 +394,12 @@ class IStrategy(ABC, HyperStrategyMixin):
""" """
return [] return []
def version(self) -> Optional[str]:
"""
Returns version of the strategy.
"""
return None
### ###
# END - Intended to be overridden by strategy # END - Intended to be overridden by strategy
### ###

View File

@ -113,8 +113,12 @@ class Worker:
if self._heartbeat_interval: if self._heartbeat_interval:
now = time.time() now = time.time()
if (now - self._heartbeat_msg) > self._heartbeat_interval: if (now - self._heartbeat_msg) > self._heartbeat_interval:
version = __version__
strategy_version = self.freqtrade.strategy.version()
if (strategy_version is not None):
version += ', strategy_version: ' + strategy_version
logger.info(f"Bot heartbeat. PID={getpid()}, " logger.info(f"Bot heartbeat. PID={getpid()}, "
f"version='{__version__}', state='{state.name}'") f"version='{version}', state='{state.name}'")
self._heartbeat_msg = now self._heartbeat_msg = now
return state return state

View File

@ -533,6 +533,7 @@ def test_api_show_config(botclient):
assert rc.json()['timeframe_min'] == 5 assert rc.json()['timeframe_min'] == 5
assert rc.json()['state'] == 'running' assert rc.json()['state'] == 'running'
assert rc.json()['bot_name'] == 'freqtrade' assert rc.json()['bot_name'] == 'freqtrade'
assert rc.json()['strategy_version'] is None
assert not rc.json()['trailing_stop'] assert not rc.json()['trailing_stop']
assert 'bid_strategy' in rc.json() assert 'bid_strategy' in rc.json()
assert 'ask_strategy' in rc.json() assert 'ask_strategy' in rc.json()

View File

@ -1597,12 +1597,20 @@ def test_help_handle(default_conf, update, mocker) -> None:
def test_version_handle(default_conf, update, mocker) -> None: def test_version_handle(default_conf, update, mocker) -> None:
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
telegram._version(update=update, context=MagicMock()) telegram._version(update=update, context=MagicMock())
assert msg_mock.call_count == 1 assert msg_mock.call_count == 1
assert '*Version:* `{}`'.format(__version__) in msg_mock.call_args_list[0][0][0] assert '*Version:* `{}`'.format(__version__) in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock()
freqtradebot.strategy.version = lambda: '1.1.1'
telegram._version(update=update, context=MagicMock())
assert msg_mock.call_count == 1
assert '*Version:* `{}`'.format(__version__) in msg_mock.call_args_list[0][0][0]
assert '*Strategy version: * `1.1.1`' in msg_mock.call_args_list[0][0][0]
def test_show_config_handle(default_conf, update, mocker) -> None: def test_show_config_handle(default_conf, update, mocker) -> None: