Workspace — AI teams that work for you 24/7
Explore

skillbase/hummingbot-strategy-design

Skill

Market making strategy design for Hummingbot: PMM, XEMM, Grid, CLMM LP. Avellaneda-Stoikov theory, spread/inventory parameter calculation, controller selection and config creation, opportunity assessment. Use when designing, evaluating, or tuning any market making strategy.

SKILL.md
40
## Hummingbot Controller Selection
41

42
Hummingbot v2 uses controllers (strategy templates) and executors (execution units). Choose the right controller for the market condition:
43

44
### Market Making Controllers
45

46
**`pmm_simple`** — Pure Market Making with fixed parameters.
47
Best for: stable markets, starting point for any MM pair. Fixed bid/ask spread, order amount, refresh interval. Simple but effective.
48

49
**`pmm_dynamic`** — PMM with volatility-adjusted spreads.
50
Best for: pairs with changing volatility. Automatically widens spread when volatility increases, tightens when calm. Based on Avellaneda-Stoikov principles internally.
51

52
**`dman_maker_v2`** — Advanced market maker with NATR-based spread.
53
Best for: more sophisticated MM on volatile pairs. Uses Normalized Average True Range for dynamic spread calculation. Better adverse selection protection than pmm_simple.
54

55
### Generic Controllers (relevant for MM)
56

57
**`grid_strike`** — Grid trading within a price range.
58
Best for: range-bound markets with clear support/resistance. Places orders at fixed intervals. Profits from mean reversion.
59

60
**`arbitrage_controller`** — Cross-exchange arbitrage.
61
Best for: same pair on two exchanges with persistent price divergence.
62

63
**`hedge_asset`** — Hedge positions across exchanges.
64
Best for: reducing inventory risk from other MM strategies.
65

66
### Executor Types for Direct Trading
67

68
When you need direct execution without a controller:
69

70
- **`order_executor`** — single order (MARKET, LIMIT, LIMIT_MAKER, LIMIT_CHASER). Use LIMIT_CHASER for best execution on large orders.
71
- **`position_executor`** — directional trade with built-in SL/TP. Use when you have a directional thesis alongside MM.
72
- **`grid_executor`** — standalone grid without a controller bot.
73
- **`dca_executor`** — dollar-cost average into a position.
74
- **`lp_executor`** — CLMM LP on Meteora/Raydium. On-chain market making.
75

76
## Strategy Design Process
77

78
### Step 1: Assess the opportunity
79

80
Before designing a strategy, determine if MM is viable on this pair:
81

82
**Pull data via MCP:**
83
```
84
get_market_data(data_type="order_book", connector_name="binance", trading_pair="ETH-USDT", query_type="snapshot")
85
get_market_data(data_type="candles", connector_name="binance", trading_pair="ETH-USDT", interval="1h", days=7)
86
get_market_data(data_type="prices", connector_name="binance", trading_pairs=["ETH-USDT"])
87
```
88

89
**Evaluate:**
90

91
| Metric | How to get it | MM viable if |
92
|--------|---------------|-------------|
93
| Quoted spread | order_book snapshot: best_ask - best_bid | > 2x maker fee |
94
| Effective spread | quoted_spread - 2 * fee | > 0 (positive = profitable) |
95
| Daily volume | candles sum or exchange stats | > $50K |
96
| Book depth at 0.5% | order_book snapshot | > 10x your order size |
97
| Volatility (24h) | candles: stdev of returns | < 5% daily (higher = wider spread needed) |
98
| Funding rate (perps) | `get_market_data(data_type="funding_rate")` | Useful signal, not a filter |
99

100
**For cross-exchange (XEMM):**
101
```
102
get_market_data(data_type="prices", connector_name="binance", trading_pairs=["ETH-USDT"])
103
get_market_data(data_type="prices", connector_name="kucoin", trading_pairs=["ETH-USDT"])
104
```
105
Compare: persistent divergence > combined fees on both exchanges = XEMM opportunity.
106

107
**For DEX pools:**
108
```
109
explore_geckoterminal(action="top_pools", network="solana")
110
explore_dex_pools(action="get_pool_info", network="solana", pool_address="...")
111
```
112

113
### Step 2: Choose controller and calculate parameters
114

115
**Spread calculation (Avellaneda-Stoikov simplified):**
116

117
```
118
minimum_spread = 2 * fee_rate + adverse_selection_cost + target_profit
119

120
Where:
121
  fee_rate = exchange maker fee (e.g., 0.001 for 0.1%)
122
  adverse_selection_cost ≈ hourly_volatility * sqrt(refresh_interval_hours)
123
  target_profit = your desired profit per round trip (e.g., 0.001 for 10bps)
124
```
125

126
For `pmm_simple`, set `bid_spread` and `ask_spread` to `minimum_spread / 2` each.
127
For `pmm_dynamic` and `dman_maker_v2`, the controller calculates spread from volatility — you set the risk aversion and base parameters.
128

129
**Order amount:**
130
```
131
max_order_size = book_depth_at_your_spread * 0.05   # don't exceed 5% of visible depth
132
position_limit = max_portfolio_risk / max_adverse_move  # e.g., $1000 risk / 0.20 move = $5000 max position
133
order_amount = min(max_order_size, position_limit / order_levels)
134
```
135

136
**Inventory management params:**
137
- `inventory_target_base_pct = 50` (neutral — equal base and quote)
138
- Enable inventory skew in all strategies — it widens spread on the overweight side
139

140
### Step 3: Create config via MCP
141

142
```
143
# 1. Get the schema
144
manage_controllers(action="describe", controller_name="pmm_simple")
145

146
# 2. Create config
147
manage_controllers(
148
  action="upsert",
149
  target="config",
150
  config_name="eth_usdt_pmm_v1",
151
  config_data={
152
    "controller_name": "pmm_simple",
153
    "connector_name": "binance",
154
    "trading_pair": "ETH-USDT",
155
    "bid_spread": 0.002,
156
    "ask_spread": 0.002,
157
    "order_amount": 0.1,
158
    "order_levels": 3,
159
    "order_refresh_time": 15,
160
    "inventory_skew_enabled": true,
161
    "inventory_target_base_pct": 50
162
  }
163
)
164
```
165

166
Always describe the controller first to get the exact schema — field names and valid ranges differ between controllers.
167

168
### Step 4: Document rationale
169

170
Every config should have a rationale that maps each parameter to the market condition that determined it:
171

172
```
173
## Config: eth_usdt_pmm_v1
174

175
Pair: ETH-USDT on Binance
176
Controller: pmm_simple
177
Date: YYYY-MM-DD
178

179
### Parameters
180
| Param | Value | Rationale |
181
|-------|-------|-----------|
182
| bid_spread | 0.2% | fee=0.1% + adverse_selection=0.05% + profit=0.05% |
183
| ask_spread | 0.2% | symmetric |
184
| order_amount | 0.1 ETH | ~$300, <5% of book depth at spread |
185
| order_levels | 3 | captures depth without excessive inventory |
186
| inventory_skew | enabled | prevent one-sided accumulation |
187

188
### Kill conditions
189
- Volatility > 8% daily: pause (spread can't compensate)
190
- Spread compresses below 0.15%: reduce order_amount or pause
191
- Inventory > 70% one side for > 2h: review
192
- Total P&L < -$200: stop and investigate
193

194
### Review schedule
195
- Daily: check fill rate, inventory balance, P&L
196
- Weekly: recalculate spread from updated volatility data
197
```
198

199
## Market Making Theory
200

201
### Avellaneda-Stoikov Framework
202

203
The model that modern MM strategies (including pmm_dynamic) build on:
204

205
**Reservation price** — where you WANT the mid-price given your inventory:
206
```
207
r = s - q * γ * σ² * (T - t)
208
```
209
- `s` = market mid-price
210
- `q` = inventory (positive = long, negative = short)
211
- `γ` = risk aversion (higher = faster inventory reversion)
212
- `σ` = volatility
213
- `T - t` = remaining time horizon
214

215
When you're long (q > 0), reservation price drops below mid → you want to sell. This is what inventory skew does mechanically.
216

217
**Optimal spread:**
218
```
219
δ = γ * σ² * (T - t) + (2/γ) * ln(1 + γ/k)
220
```
221
- `k` = order arrival intensity (how often orders hit your level)
222
- Higher volatility → wider spread (more adverse selection risk)
223
- Higher arrival rate → tighter spread (more fills compensate per-trade risk)
224

225
### Inventory Risk
226

227
The #1 risk in market making. You profit from spread but risk from price moves on accumulated inventory.
228

229
**Decompose P&L always:**
230
- **Spread P&L** = round_trips * effective_spread * order_size (should be positive)
231
- **Inventory P&L** = position * (current_price - avg_entry) (can be large and negative)
232
- **Fees** = total maker + taker fees paid
233

234
If |inventory P&L| consistently > spread P&L, the strategy is net speculating. Review immediately.
235

236
### When NOT to Market Make
237

238
- **Strong trend**: MM accumulates inventory against the trend. If 1h candles show 5+ consecutive same-direction closes, pause.
239
- **Volatility spike**: a 3x increase in hourly volatility means your spread is too tight. The adverse selection cost exceeds your spread.
240
- **Thin liquidity event**: if depth drops below 5x your order size, your orders become a significant part of the book — you ARE the market, not making it.
241
- **Pre-announcement**: known events (FOMC, token unlock, protocol upgrade) cause jumps that bypass any spread.
247
- Every parameter must have a rationale tied to market data or risk constraint — never set params by "feel"
248
- Always describe the controller schema via MCP before creating a config — field names differ between controllers
249
- Calculate minimum profitable spread before anything: 2*fee + adverse_selection + target. If effective spread < 0, the opportunity doesn't exist
250
- Include kill conditions in every strategy design — strategies expire as market conditions change
251
- Inventory management is not optional — enable skew in every strategy
252
- Start with pmm_simple, graduate to pmm_dynamic or dman_maker_v2 only after proving the pair is viable
253
- For XEMM: profitability must cover fees on BOTH exchanges plus the cost of keeping capital split between them