Guia paso a paso para probar el ciclo completo de hedging en Hyperliquid testnet. Probado y verificado el 2026-03-10.
1. Setup: generar wallets → vault → DB
2. Fondear wallets en HL testnet
3. Arrancar bot + dashboard
4. Bot detecta precio fuera de rango → abre hedge (short/long)
5. Precio vuelve al rango → bot cierra hedge → registra PnL
| Herramienta | Version | Para que |
|---|---|---|
| Node.js | 22+ | Runtime (usa import.meta.dirname) |
| pnpm | 9+ | Package manager del monorepo |
| Rabby o MetaMask | - | Importar private keys, firmar transacciones en HL |
| ETH en Arbitrum | ~$2 | Gas para depositar USDC en HL mainnet |
Importante: Hyperliquid testnet requiere un deposito en mainnet antes de poder reclamar mock USDC del faucet. Necesitas ETH en Arbitrum L2 para pagar gas.
git clone https://github.com/frxnnk/rangeguard.git
cd rangeguard
pnpm installCrear el archivo .env en la raiz del monorepo:
cp .env.example .envEditar .env con estos valores minimos:
# Password para el vault (elegir algo seguro)
VAULT_PASSWORD=MiPasswordSeguro123!
# RPC de Arbitrum (el publico funciona bien para testing)
RPC_ARBITRUM=https://arb1.arbitrum.io/rpc
# Hyperliquid testnet (estos son los defaults)
HL_API_URL=https://api.hyperliquid-testnet.xyz
HL_WS_URL=wss://api.hyperliquid-testnet.xyz/wspnpm setup-testnetEste script:
- Genera 2 pares de claves secp256k1 (protection + profit)
- Encripta las private keys en el vault (
~/.rangeguard/vault.enc) - Crea las wallets en la DB con sus addresses reales
- Crea un pool ETH/USDC Arbitrum de prueba
Salida esperada:
=== Trading Bot Testnet Setup ===
Storing private keys in vault...
-> pool-eth-arb -> 0x769b...
-> profit-global -> 0xc091...
Vault contains 2 keys: pool-eth-arb, profit-global
Seeding database...
-> Wallets created in DB
-> Pool config created
=== Setup Complete ===
Wallets generated (SAVE THESE - needed to fund on HL testnet):
======================================================================
pool-eth-arb (protection)
Address: 0x769b1202df5275cf15365108bddba4754eb83847
Private Key: 0xe782d400c12f0d2df64af7e728d76c95f81807b840cfab9bf45de2c5dfdeea4e
profit-global (profit)
Address: 0xc09107ac7fc4e35d297bc920d2f18a3720fb0c66
Private Key: 0x28d52b90edfb36b941fab870b8b9375f7a66a1eb802acfb3f117093119c9e401
======================================================================
IMPORTANTE: Copiar las private keys. Las necesitas para importar en Rabby/MetaMask y para fondear en HL testnet.
- Abrir Rabby (o MetaMask)
- Agregar cuenta → Importar Private Key
- Pegar la private key de
pool-eth-arb(sin el prefijo0xsi Rabby lo rechaza) - Verificar que la address coincide con la que mostro el script
Solo necesitas importar
pool-eth-arb(la wallet de proteccion). La wallet de profit es opcional para testing.
Hyperliquid testnet requiere que la wallet exista en mainnet antes de poder reclamar mock USDC.
- Ir a https://app.hyperliquid.xyz
- Conectar la wallet
pool-eth-arbvia Rabby/MetaMask - Cambiar a red Arbitrum en tu wallet
- Depositar minimo 5 USDC desde Arbitrum
- Necesitas USDC (ERC-20) en Arbitrum
- Necesitas ETH en Arbitrum para gas (~$0.10)
- Confirmar la transaccion
Nota: Si no tenes USDC en Arbitrum, podes enviar ETH a Arbitrum via bridge y swapear a USDC en un DEX (Uniswap, 1inch).
- Ir a https://app.hyperliquid-testnet.xyz
- Conectar la misma wallet
- Click en "Click to claim drip" (da 1000 mock USDC)
- Los fondos aparecen en la seccion Spot
En testnet con "Unified Accounts", los fondos estan en Spot. Para operar perpetuos:
- En la pagina de testnet, ir a Portfolio → Balances
- Click en "Transfer" junto a USDC
- Transferir de Spot → Perps el monto deseado (ej: 100 USDC)
Alternativa: Si la UI muestra "Unified" mode, los fondos de Spot se comparten automaticamente con Perps. El bot detecta esto y usa el balance de Spot como fallback.
Si es la primera vez usando la wallet en HL:
- Ir a https://app.hyperliquid-testnet.xyz/trade/ETH
- Intentar colocar una orden minima
- Firmar la transaccion de "Enable Trading" que aparece
El script setup-testnet crea un pool con rango 2000-2500. Para forzar un test inmediato, podes ajustar el rango para que el precio actual este fuera:
# Ver precio actual de ETH/USDC
# (el bot lo muestra en los logs, o mirar en https://app.hyperliquid-testnet.xyz/trade/ETH)
# Si ETH esta a ~$2070, setear rango arriba del precio para forzar "out-of-range-below":
node --no-warnings --import tsx -e "
import { getDatabase, closeDatabase } from './packages/db/src/connection.js';
import { resolve } from 'path';
const db = getDatabase(resolve('data', 'bot.db'));
db.prepare('UPDATE pool_configs SET range_lower = 2200, range_upper = 2600, pool_amount = ?').run('20');
console.log('Pool range set to 2200-2600, pool_amount=20 USD');
closeDatabase();
"pool_amount: Con 20 USD y leverage 5x, el margen requerido es ~$4. Ajustar segun los fondos disponibles.
# Terminal 1: Bot
pnpm dev:bot
# Terminal 2: Dashboard (opcional pero recomendado)
pnpm dev:dashboardINFO (bot): Starting Trading Bot...
INFO (bot): Database initialized
INFO (key-manager): Loaded keys into memory count=2
INFO (hl-client): Wallet client initialized alias="pool-eth-arb" address="0x769b..."
INFO (hl-client): Wallet client initialized alias="profit-global" address="0xc091..."
INFO (range-detector): Pool registered for range detection
INFO (pool-watcher): Polling started intervalMs=5000
INFO (bot): Active pools loaded count=1
INFO (bot): Bot is running. Press Ctrl+C to stop.
Con el rango en 2200-2600 y ETH a ~$2070, el bot:
- Polls slot0 cada 5s del pool Uniswap V3
- Debounce: 3 polls consecutivos fuera de rango (~15s)
- Transicion confirmada:
in-range → out-of-range-below - HedgeCalculator:
size = poolAmount / price = 20 / 2070 = 0.0097 ETH - Ordena short en HL testnet con 5x leverage
- Stop-loss de backup colocado automaticamente
INFO (range-detector): Range state transition from="in-range" to="out-of-range-below" price=2071.74
INFO (hl-client): Leverage set asset="ETH" leverage=5
INFO (hl-client): Order filled side="short" size="0.0097" avgPrice="2062.9" latencyMs=2400
INFO (position-tracker): Hedge trade opened side="short" size="0.0097"
INFO (stop-loss): Backup SL placed triggerPrice="2244" orderId=344653409100
INFO (bot): Hedge opened event walletType="protection" side="short" size="0.0097"
- Ir a https://app.hyperliquid-testnet.xyz/trade/ETH
- Deberia verse la posicion short en la seccion Positions
- En Orders deberia verse el stop-loss trigger order
Para probar el cierre sin esperar que el precio suba a $2200+, cambiar el rango en la DB para que incluya el precio actual:
# Si ETH esta a ~$2070, poner rango 2000-2200
node --no-warnings --import tsx -e "
import { getDatabase, closeDatabase } from './packages/db/src/connection.js';
import { resolve } from 'path';
const db = getDatabase(resolve('data', 'bot.db'));
db.prepare('UPDATE pool_configs SET range_lower = 2000, range_upper = 2200').run();
console.log('Pool range updated to 2000-2200');
closeDatabase();
"Alternativa: Editar el rango desde el dashboard en
/pools/:id/edit.
El bot detecta el cambio en ~30s (syncPools corre cada 30s). Despues necesita 3 polls mas (~15s) para confirmar la transicion.
INFO (bot): Range parameters updated from DB rangeLower=2000 rangeUpper=2200
INFO (range-detector): Range state transition from="out-of-range-below" to="in-range" price=2072.34
INFO (stop-loss): Primary SL triggered: range return detected
INFO (order-executor): Closing protection hedge asset="ETH"
INFO (bot): Hedge closed event pnl="-0.09" closeReason="range_return"
INFO (position-tracker): Hedge trade closed realizedPnl="-0.09" isFalseBreakout=true
INFO (engine): All hedges closed on range return closedCount=1
node --no-warnings --import tsx -e "
import { getDatabase, closeDatabase } from './packages/db/src/connection.js';
import { resolve } from 'path';
const db = getDatabase(resolve('data', 'bot.db'));
const hedge = db.prepare('SELECT side, size, entry_price, exit_price, realized_pnl, status, close_reason FROM hedge_trades ORDER BY closed_at DESC LIMIT 1').get();
console.log('Last closed hedge:', hedge);
closeDatabase();
"Salida esperada:
{
"side": "short",
"size": "0.0097",
"entry_price": "2062.9",
"exit_price": "2072.34",
"realized_pnl": "-0.09",
"status": "closed",
"close_reason": "range_return"
}Despues de probar, restaurar un rango razonable:
node --no-warnings --import tsx -e "
import { getDatabase, closeDatabase } from './packages/db/src/connection.js';
import { resolve } from 'path';
const db = getDatabase(resolve('data', 'bot.db'));
db.prepare('UPDATE pool_configs SET range_lower = 2000, range_upper = 2500, status = ?').run('in-range');
console.log('Pool range restored to 2000-2500');
closeDatabase();
"Para repetir el ciclo, el bot detectara automaticamente la siguiente salida de rango.
La wallet no existe en Hyperliquid. Soluciones:
- Ir a https://app.hyperliquid-testnet.xyz con la wallet conectada
- Hacer un deposito (aunque sea 5 USDC) en mainnet primero
- Reclamar el drip en testnet
No hay suficiente margen para el hedge. Soluciones:
- Reducir
pool_amounten la DB (ej: 20 USD con 5x leverage requiere ~$4 de margen) - Transferir mas USDC de Spot a Perps
- Verificar el balance en la UI de HL testnet
El bot ya tiene un hedge abierto para ese pool. No abre duplicados. Soluciones:
- Esperar el range-return automatico
- Cerrar manualmente desde el dashboard (
/hedges→ Close) - Limpiar hedges huerfanos en DB:
DELETE FROM hedge_trades WHERE status = 'open';
El bot no encuentra el archivo .env. Verificar:
- Que
.envexiste en la raiz del monorepo (no enapps/bot/) - Que
VAULT_PASSWORDesta seteado en.env
La wallet existe en DB pero no en el vault. Soluciones:
- Correr
pnpm setup-testnetde nuevo (regenera todo) - O agregar la key manualmente:
pnpm setup-keys
El syncPools corre cada 30s. Esperar hasta 30s despues de editar la DB. Logs a buscar:
INFO (bot): Range parameters updated from DB
Necesitas ETH en Arbitrum L2, no en Ethereum mainnet. Opciones:
- Bridge ETH desde mainnet: https://bridge.arbitrum.io
- Comprar ETH directamente en Arbitrum via exchange
- Verificar que
RPC_ARBITRUMesta seteado en.env - El RPC publico (
arb1.arbitrum.io/rpc) funciona pero puede tener rate limits - Para mejor experiencia, usar un API key gratuito de Alchemy o Infura
Tiempo Evento Donde verificar
───── ────── ────────────────
0s Bot arranca, carga vault (2 keys), registra pool Logs: "Bot is running"
5s Primer poll de precio (slot0) Logs: "Price update"
15s 3 polls fuera de rango → transicion confirmada Logs: "Range state transition"
15s HedgeCalculator calcula size Logs: implícito
16s Leverage seteado en HL (5x cross) Logs: "Leverage set"
18s IOC order filled (short/long) Logs: "Order filled"
18s Hedge registrado en DB DB: hedge_trades.status = 'open'
19s Stop-loss de backup colocado Logs: "Backup SL placed"
19s Evento hedge-opened emitido Dashboard: /hedges
... (precio vuelve al rango)
+0s syncPools detecta cambio de rango Logs: "Range parameters updated"
+15s 3 polls in-range → transicion confirmada Logs: "Range state transition"
+15s SL cancelado + posicion cerrada en HL Logs: "Closing protection hedge"
+17s PnL calculado, hedge marcado closed en DB DB: hedge_trades.status = 'closed'
+17s Evento hedge-closed emitido Dashboard: /history
# 1. Ver hedge cerrado en DB
node --no-warnings --import tsx -e "
import { getDatabase, closeDatabase } from './packages/db/src/connection.js';
import { resolve } from 'path';
const db = getDatabase(resolve('data', 'bot.db'));
const h = db.prepare('SELECT * FROM hedge_trades ORDER BY closed_at DESC LIMIT 1').get();
console.log(JSON.stringify(h, null, 2));
closeDatabase();
"
# 2. Ver trade log (audit trail de requests a HL)
node --no-warnings --import tsx -e "
import { getDatabase, closeDatabase } from './packages/db/src/connection.js';
import { resolve } from 'path';
const db = getDatabase(resolve('data', 'bot.db'));
const trades = db.prepare('SELECT action, success, latency_ms FROM trade_log ORDER BY created_at DESC LIMIT 5').all();
console.table(trades);
closeDatabase();
"
# 3. Verificar que no quedan posiciones abiertas en HL testnet
# Ir a https://app.hyperliquid-testnet.xyz → Portfolio → Positions- pool_amount bajo: Usar 20-50 USD para minimizar margen necesario
- Rango estrecho: Un rango donde el precio actual este justo en el borde permite testing rapido
- Logs en debug: Setear
LOG_LEVEL=debugen.envpara ver el debounce acumulando - Dashboard simultaneo:
pnpm dev:dashboarden otra terminal para ver cambios en tiempo real - Reset rapido: Para empezar de cero, borrar
data/bot.dby correrpnpm setup-testnetde nuevo - Wallet de profit: Es opcional. El bot funciona sin ella (muestra warning pero continua)