Add docs for External Signals API
This commit is contained in:
parent
914eccecec
commit
366c6c24d8
@ -225,7 +225,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi
|
||||
| `webhook.webhookexitcancel` | Payload to send on exit order cancel. Only required if `webhook.enabled` is `true`. See the [webhook documentation](webhook-config.md) for more details. <br> **Datatype:** String
|
||||
| `webhook.webhookexitfill` | Payload to send on exit order filled. Only required if `webhook.enabled` is `true`. See the [webhook documentation](webhook-config.md) for more details. <br> **Datatype:** String
|
||||
| `webhook.webhookstatus` | Payload to send on status calls. Only required if `webhook.enabled` is `true`. See the [webhook documentation](webhook-config.md) for more details. <br> **Datatype:** String
|
||||
| | **Rest API / FreqUI**
|
||||
| | **Rest API / FreqUI / External Signals**
|
||||
| `api_server.enabled` | Enable usage of API Server. See the [API Server documentation](rest-api.md) for more details. <br> **Datatype:** Boolean
|
||||
| `api_server.listen_ip_address` | Bind IP address. See the [API Server documentation](rest-api.md) for more details. <br> **Datatype:** IPv4
|
||||
| `api_server.listen_port` | Bind Port. See the [API Server documentation](rest-api.md) for more details. <br>**Datatype:** Integer between 1024 and 65535
|
||||
@ -233,6 +233,9 @@ Mandatory parameters are marked as **Required**, which means that they are requi
|
||||
| `api_server.username` | Username for API server. See the [API Server documentation](rest-api.md) for more details. <br>**Keep it in secret, do not disclose publicly.**<br> **Datatype:** String
|
||||
| `api_server.password` | Password for API server. See the [API Server documentation](rest-api.md) for more details. <br>**Keep it in secret, do not disclose publicly.**<br> **Datatype:** String
|
||||
| `bot_name` | Name of the bot. Passed via API to a client - can be shown to distinguish / name bots.<br> *Defaults to `freqtrade`*<br> **Datatype:** String
|
||||
| `api_server.enable_message_ws` | Enable external signal publishing. See the [API Server documentation](rest-api.md) for more details. <br> **Datatype:** Boolean
|
||||
| `api_server.ws_token` | API token for external signal publishing. Only required if `api_server.enable_message_ws` is `true`. See the [API Server documentation](rest-api.md) for more details. <br>**Keep it in secret, do not disclose publicly.** <br> **Datatype:** String
|
||||
| `external_message_consumer` | Enable consuming of external signals. See the [API Server documentation](rest-api.md) for more details. <br> **Datatype:** Dict
|
||||
| | **Other**
|
||||
| `initial_state` | Defines the initial application state. If set to stopped, then the bot has to be explicitly started via `/start` RPC command. <br>*Defaults to `stopped`.* <br> **Datatype:** Enum, either `stopped` or `running`
|
||||
| `force_entry_enable` | Enables the RPC Commands to force a Trade entry. More information below. <br> **Datatype:** Boolean
|
||||
|
146
docs/rest-api.md
146
docs/rest-api.md
@ -1,4 +1,4 @@
|
||||
# REST API & FreqUI
|
||||
# REST API, FreqUI & External Signals
|
||||
|
||||
## FreqUI
|
||||
|
||||
@ -460,3 +460,147 @@ The correct configuration for this case is `http://localhost:8080` - the main pa
|
||||
|
||||
!!! Note
|
||||
We strongly recommend to also set `jwt_secret_key` to something random and known only to yourself to avoid unauthorized access to your bot.
|
||||
|
||||
## External Signals API
|
||||
|
||||
FreqTrade provides a mechanism whereby a leader instance may provide analyzed dataframes to a subscriber/follower instance (or instances) using websockets.
|
||||
|
||||
Run a bot in Leader mode to broadcast any populated indicators in the dataframes for each pair to bots running in Follower mode. This allows the reuse of computed indicators in multiple bots without needing to compute them multiple times.
|
||||
|
||||
### Leader configuration
|
||||
|
||||
Enable the leader websocket api by adding `enable_message_ws` to your `api_server` section and setting it to `true`, and providing an api token with `ws_token`. See [Security](#security) above for advice on token generation.
|
||||
|
||||
!!! Note
|
||||
We strongly recommend to also set `ws_token` to something random and known only to yourself to avoid unauthorized access to your bot.
|
||||
|
||||
```jsonc
|
||||
{
|
||||
//...
|
||||
"api_server": {
|
||||
//...
|
||||
"enable_message_ws": true,
|
||||
"ws_token": "mysecretapitoken"
|
||||
//...
|
||||
}
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
The leader instance will listen for incoming reqests on the port configured by `api_server.listen_port` to forward its analyzed dataframes for each pair in its active whitelist calculated in its `populate_indicators()`.
|
||||
|
||||
### Follower configuration
|
||||
|
||||
Enable subscribing to a leader instance by adding the `external_message_consumer` section to the follower's config file.
|
||||
|
||||
```jsonc
|
||||
{
|
||||
//...
|
||||
"external_message_consumer": {
|
||||
"enabled": true,
|
||||
"producers": [
|
||||
{
|
||||
"name": "default",
|
||||
"host": "127.0.0.1",
|
||||
"port": 8080,
|
||||
"ws_token": "mysecretapitoken"
|
||||
}
|
||||
],
|
||||
"reply_timeout": 10,
|
||||
"ping_timeout": 5,
|
||||
"sleep_time": 5,
|
||||
"message_size_limit": 8, // in MB, default=8
|
||||
"remove_entry_exit_signals": false
|
||||
}
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
Instead of (or as well as) calculating indicators in `populate_indicators()` the follower instance opens a websocket connection to a leader instance (or multiple leader instances in advanced configurations) and requests the leader's most recently analyzed dataframes for each pair in the active whitelist.
|
||||
|
||||
A follower instance will then have a full copy of the analyzed dataframes without the need to calculate them itself.
|
||||
|
||||
### Example - Leader Strategy
|
||||
|
||||
A simple strategy with multiple indicators. No special considerations are required in the strategy itself, only in the configuration file to enable websocket listening.
|
||||
|
||||
```py
|
||||
class LeaderStrategy(IStrategy):
|
||||
#...
|
||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
Calculate indicators in the standard freqtrade way which can then be broadcast to other instances
|
||||
"""
|
||||
dataframe['rsi'] = ta.RSI(dataframe)
|
||||
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
|
||||
dataframe['bb_lowerband'] = bollinger['lower']
|
||||
dataframe['bb_middleband'] = bollinger['mid']
|
||||
dataframe['bb_upperband'] = bollinger['upper']
|
||||
dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9)
|
||||
|
||||
return dataframe
|
||||
|
||||
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
Populates the entry signal for the given dataframe
|
||||
"""
|
||||
dataframe.loc[
|
||||
(
|
||||
(qtpylib.crossed_above(dataframe['rsi'], self.buy_rsi.value)) &
|
||||
(dataframe['tema'] <= dataframe['bb_middleband']) &
|
||||
(dataframe['tema'] > dataframe['tema'].shift(1)) &
|
||||
(dataframe['volume'] > 0)
|
||||
),
|
||||
'enter_long'] = 1
|
||||
|
||||
return dataframe
|
||||
```
|
||||
|
||||
### Example - Follower Strategy
|
||||
|
||||
A logically equivalent strategy which calculates no indicators itself, but will have the same analyzed dataframes to make trading decisions from as the leader. In this example the follower has the same entry criteria, however this is not necessary. The follower may use different logic to enter/exit trades.
|
||||
|
||||
```py
|
||||
class FollowerStrategy(IStrategy):
|
||||
#...
|
||||
process_only_new_candles = False # required for followers
|
||||
|
||||
_columns_to_expect = ['rsi_default', 'tema_default', 'bb_middleband_default']
|
||||
|
||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
Use the websocket api to get pre-populated indicators from another FreqTrade instance.
|
||||
Use `self.dp.get_external_df(pair)` to get the dataframe
|
||||
"""
|
||||
pair = metadata['pair']
|
||||
timeframe = self.timeframe
|
||||
|
||||
leader_dataframe, _ = self.dp.get_external_df(pair)
|
||||
|
||||
if not leader_dataframe.empty:
|
||||
merged_dataframe = merge_informative_pair(dataframe, leader_dataframe,
|
||||
timeframe, timeframe,
|
||||
append_timeframe=False,
|
||||
suffix="default")
|
||||
return merged_dataframe
|
||||
else:
|
||||
dataframe[self._columns_to_expect] = 0
|
||||
|
||||
return dataframe
|
||||
|
||||
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
Populates the entry signal for the given dataframe
|
||||
"""
|
||||
# Use the dataframe columns as if we calculated them ourselves
|
||||
dataframe.loc[
|
||||
(
|
||||
(qtpylib.crossed_above(dataframe['rsi_default'], self.buy_rsi.value)) &
|
||||
(dataframe['tema_default'] <= dataframe['bb_middleband_default']) &
|
||||
(dataframe['tema_default'] > dataframe['tema_default'].shift(1)) &
|
||||
(dataframe['volume'] > 0)
|
||||
),
|
||||
'enter_long'] = 1
|
||||
|
||||
return dataframe
|
||||
```
|
Loading…
Reference in New Issue
Block a user