Merge pull request #5929 from dvdmchl/develop
Telegram and log prints strategy version.
This commit is contained in:
commit
243e59cabb
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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']),
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
###
|
###
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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:
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user