Skip to content本文へ移動

Vault Development · Keeper Architecture · 5/7

Position Management

Reading Positions

Before making any trading decision, the keeper needs to know the current state. In the examples below, exchange is an Exchange created with vault_address=vault_address, so every action is signed for the vault — the SDK's order methods do not take a vault_address argument. Run them on testnet first.

from hyperliquid.exchange import Exchange
from hyperliquid.info import Info


def get_positions(info: Info, vault_address: str) -> dict:
    """Get all open positions for the vault."""
    state = info.user_state(vault_address)
    positions = {}

    for ap in state["assetPositions"]:
        p = ap["position"]
        coin = p["coin"]
        positions[coin] = {
            "size": float(p["szi"]),           # Positive = long, negative = short
            "entry_price": float(p["entryPx"]),
            "mark_price": float(p["positionValue"]) / abs(float(p["szi"])) if float(p["szi"]) != 0 else 0,
            "unrealized_pnl": float(p["unrealizedPnl"]),
            "leverage": float(p.get("leverage", {}).get("value", 1)),
            "liquidation_px": float(p.get("liquidationPx", 0) or 0),
        }

    return positions

Opening Positions

def open_position(exchange: Exchange, vault_address: str,
                  coin: str, side: str, size: float,
                  leverage: int = 10) -> dict:
    """Open a new position for the vault."""

    # Set leverage first
    exchange.update_leverage(leverage, coin, is_cross=True)

    # Place market order (an aggressive IOC limit order with 5% default slippage)
    is_buy = side == "long"
    result = exchange.market_open(coin, is_buy, size)

    return result

Closing Positions

def close_position(exchange: Exchange, vault_address: str,
                   coin: str, size: float) -> dict:
    """Close an existing position."""
    # market_close reads the vault's position, picks the closing side,
    # and sends a reduce-only IOC order.
    result = exchange.market_close(coin, sz=abs(size))

    return result

Adjusting Position Size

Sometimes you need to partially close or add to a position:

def adjust_position(exchange: Exchange, info: Info,
                    vault_address: str, coin: str,
                    target_size: float) -> dict | None:
    """Adjust position to target size."""
    positions = get_positions(info, vault_address)
    current = positions.get(coin, {}).get("size", 0.0)

    diff = target_size - current

    if abs(diff) < 0.001:  # Already at target (within tolerance)
        return None

    is_buy = diff > 0
    result = exchange.market_open(coin, is_buy, abs(diff))

    return result

Stop-Loss Management

def set_stop_loss(exchange: Exchange, vault_address: str,
                  coin: str, size: float, stop_price: float) -> dict:
    """Set a stop-loss order for an open position."""
    is_buy = size < 0  # Stop for shorts = buy, for longs = sell

    result = exchange.order(
        coin,
        is_buy,
        abs(size),
        stop_price,
        {"trigger": {
            "triggerPx": stop_price,
            "isMarket": True,
            "tpsl": "sl",
        }},
        reduce_only=True,
    )

    return result

Key Position Management Rules

  1. Always know your position — Read before writing
  2. Use reduce_only — For closing orders, use market_close or set reduce_only=True to prevent accidental flips
  3. Check margin before opening — Don't exceed your risk limits
  4. Handle partial fills — Market orders may partially fill in low liquidity
  5. Log everything — Every order, fill, and error should be logged for debugging

vault 開発 · keeper の構成 · 5/7

ポジションの管理

ポジションを読む

売買を判断する前に、keeper は今の状態を知る必要があります。以下の例の exchange は、vault_address=vault_address を指定して作った Exchange です。そのため、どの操作も vault のために署名されます。SDK の注文の関数は、vault_address の引数を取りません。まずテストネットで動かしてください。

from hyperliquid.exchange import Exchange
from hyperliquid.info import Info


def get_positions(info: Info, vault_address: str) -> dict:
    """Get all open positions for the vault."""
    state = info.user_state(vault_address)
    positions = {}

    for ap in state["assetPositions"]:
        p = ap["position"]
        coin = p["coin"]
        positions[coin] = {
            "size": float(p["szi"]),           # Positive = long, negative = short
            "entry_price": float(p["entryPx"]),
            "mark_price": float(p["positionValue"]) / abs(float(p["szi"])) if float(p["szi"]) != 0 else 0,
            "unrealized_pnl": float(p["unrealizedPnl"]),
            "leverage": float(p.get("leverage", {}).get("value", 1)),
            "liquidation_px": float(p.get("liquidationPx", 0) or 0),
        }

    return positions

ポジションを建てる

def open_position(exchange: Exchange, vault_address: str,
                  coin: str, side: str, size: float,
                  leverage: int = 10) -> dict:
    """Open a new position for the vault."""

    # Set leverage first
    exchange.update_leverage(leverage, coin, is_cross=True)

    # Place market order (an aggressive IOC limit order with 5% default slippage)
    is_buy = side == "long"
    result = exchange.market_open(coin, is_buy, size)

    return result

ポジションを閉じる

def close_position(exchange: Exchange, vault_address: str,
                   coin: str, size: float) -> dict:
    """Close an existing position."""
    # market_close reads the vault's position, picks the closing side,
    # and sends a reduce-only IOC order.
    result = exchange.market_close(coin, sz=abs(size))

    return result

ポジションの大きさを変える

ポジションの一部を閉じたり、買い増し・売り増ししたりすることもあります。

def adjust_position(exchange: Exchange, info: Info,
                    vault_address: str, coin: str,
                    target_size: float) -> dict | None:
    """Adjust position to target size."""
    positions = get_positions(info, vault_address)
    current = positions.get(coin, {}).get("size", 0.0)

    diff = target_size - current

    if abs(diff) < 0.001:  # Already at target (within tolerance)
        return None

    is_buy = diff > 0
    result = exchange.market_open(coin, is_buy, abs(diff))

    return result

損切りの管理

def set_stop_loss(exchange: Exchange, vault_address: str,
                  coin: str, size: float, stop_price: float) -> dict:
    """Set a stop-loss order for an open position."""
    is_buy = size < 0  # Stop for shorts = buy, for longs = sell

    result = exchange.order(
        coin,
        is_buy,
        abs(size),
        stop_price,
        {"trigger": {
            "triggerPx": stop_price,
            "isMarket": True,
            "tpsl": "sl",
        }},
        reduce_only=True,
    )

    return result

ポジション管理の大事な決まり

  1. 自分のポジションを常に知っておく — 書き込む前に読みます
  2. reduce_only を使う — 閉じる注文では、market_close を使うか reduce_only=True を指定します。うっかり反対側のポジションになるのを防げます
  3. 建てる前に証拠金を確かめる — 自分のリスクの上限を超えないようにします
  4. 一部だけの約定に備える — 流動性が薄いと、成行の注文が一部しか約定しないことがあります
  5. すべてを記録する — 注文、約定、エラーはすべてログに残し、原因を調べられるようにします