<!-- https://zunder-design-preview.pages.dev/docs/integrations/freqtrade · Markdown version of the page -->

# Freqtrade

Put Guard in front of Freqtrade's Hyperliquid exchange through ccxt_config. The exact config keys.

:::note[Planned]
Guard 1.0 integration, not verified against a running Guard yet. Written from [Freqtrade's exchange notes for Hyperliquid](https://www.freqtrade.io/en/stable/exchanges/) and [configuration reference](https://www.freqtrade.io/en/stable/configuration/), read 6 Oct 2026, and from ccxt's source (see the [ccxt guide](https://zunder-design-preview.pages.dev/docs/integrations/ccxt)).
:::

Freqtrade trades Hyperliquid through ccxt. Its `exchange.ccxt_config` is passed to both of Freqtrade's ccxt instances (sync and async), so the [ccxt override](https://zunder-design-preview.pages.dev/docs/integrations/ccxt) goes there.

## Before you change anything

- Read [the setup journey](https://zunder-design-preview.pages.dev/docs/deploy) and start Guard in paper mode.
- Keep your real account address. Give the bot a **Guard client key**, while the API wallet key stays inside Guard.
- Check the network and isolated-margin requirements in this guide. These integration instructions are planned and not verified against a released Guard yet.

## The smallest change

```diff
+"trading_mode": "futures",
+"margin_mode": "isolated",
 "exchange": {
     "name": "hyperliquid",
     "walletAddress": "0xYourAccountAddress",
-    "privateKey": "0x…API wallet key…",
+    "privateKey": "0x…client key from Guard…",
+    "ccxt_config": {
+        "urls": {"api":  {"public": "http://127.0.0.1:8547", "private": "http://127.0.0.1:8547"},
+                 "test": {"public": "http://127.0.0.1:8547", "private": "http://127.0.0.1:8547"}},
+        "options": {"builderFee": false, "refSet": true}
+    }
 }
```

## The config, in full

The keys to merge into Freqtrade's `config.json` (the rest of your config stays as it is):

```json
{
    "trading_mode": "futures",
    "margin_mode": "isolated",
    "exchange": {
        "name": "hyperliquid",
        "walletAddress": "0xYourAccountAddress",
        "privateKey": "0x...",
        "ccxt_config": {
            "urls": {
                "api":  {"public": "http://127.0.0.1:8547", "private": "http://127.0.0.1:8547"},
                "test": {"public": "http://127.0.0.1:8547", "private": "http://127.0.0.1:8547"}
            },
            "options": {"builderFee": false, "refSet": true}
        }
    }
}
```

## The config keys

| Key | Value | Source |
|---|---|---|
| `trading_mode` | `futures` | Hyperliquid's perps; Freqtrade configuration reference |
| `margin_mode` | `isolated` | **required.** Freqtrade sends `updateLeverage` before every entry and every stop, and Guard refuses cross margin |
| `exchange.name` | `hyperliquid` | Freqtrade exchange notes |
| `exchange.walletAddress` | your **main** account address, `0x` + 40 hex digits, not the API wallet's. Freqtrade passes it to ccxt as `walletAddress`, the `user` of its account queries | Freqtrade exchange notes |
| `exchange.privateKey` | the Guard client key, `0x` + 64 hex digits. Freqtrade passes it to ccxt as `privateKey`, which signs every request. Freqtrade's notes say to use an API wallet key here; behind Guard, the client key takes its place | Freqtrade exchange notes |
| `exchange.ccxt_config.urls` | Guard's URL, for `api` and `test` | Freqtrade configuration reference; ccxt `sign()`, `set_sandbox_mode()` |
| `exchange.ccxt_config.options` | `builderFee: false`, `refSet: true`; never `approvedBuilderFee: true` | ccxt `initialize_client()`; why: [ccxt guide](https://zunder-design-preview.pages.dev/docs/integrations/ccxt#two-ccxt-defaults-behind-guard) |

Freqtrade's notes also show `ccxt_config.options.vaultAddress` and `subAccountAddress` for vaults and sub-accounts. Guard's support for those is not built.

## The stop

Guard [needs the stop with the entry](https://zunder-design-preview.pages.dev/docs/integrations#send-the-stop-with-the-entry). Freqtrade sends the entry first and, with `stoploss_on_exchange`, places its stop after the entry has filled, in a request of its own.

Under Guard's default rules (`stop = "attach"` in `guard.toml`) that works: Guard attaches its own reduce-only market stop to the entry, 2% away (`default_stop_distance`), and sizes the entry from it. When Freqtrade's stop-limit arrives, it rests beside Guard's stop but never replaces it, since a stop-limit may not fill. Freqtrade's later stop moves (cancel and replace of its own stop-limit) pass. With `stop = "refuse"`, Freqtrade's entries are refused (`no_protective_stop`).

So behind Guard a Freqtrade trade is sized from Guard's stop distance, not from Freqtrade's `stoploss`. Set `default_stop_distance` to the stop you want Guard to size from.

## Two layers of limits

Freqtrade has its own protections (stoploss, max open trades, `StoplossGuard` and others). Keep them. Guard is the layer that still holds when Freqtrade's own logic fails or its config is wrong.

**Example.** A config change sets `stake_amount` ten times too high. Freqtrade sends an order worth 30,000 on an account of 2,000. Guard sizes it from its stop 2% away: the trade may lose at most 2% of 2,000, so 40, at the stop, costs included (4.5 + 1 basis points per side, 0.11% for the round trip). That allows about 40 / (0.02 + 0.0011) ≈ 1,896 of value, and the trade opens at about that size, less if other positions use the open-risk or leverage room. Freqtrade's next status shows the smaller filled amount.

## Dry run

In `dry_run` mode Freqtrade simulates its own orders and sends none, so Guard sees nothing to judge. To see Guard's decisions without risk, run Freqtrade live against a Guard in [paper mode](https://zunder-design-preview.pages.dev/docs/concepts/networks#paper).

## Read the response

The reply uses Hyperliquid’s response format. Check the returned status and order size rather than assuming the requested size was accepted. A refusal includes a readable reason and a [veto code](https://zunder-design-preview.pages.dev/docs/reference/veto-codes); a smaller accepted order reflects the rule that bound its size. Paper mode sends no venue order. Test this path before using testnet.
