Metadata-Version: 2.4
Name: hyperliquid-cli-python
Version: 0.1.11
Summary: Python CLI for Hyperliquid DEX
Author: hyperliquid-cli contributors
License-Expression: BSD-2-Clause
Project-URL: Source, https://github.com/haturatu/hl
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: rich>=13.7.1
Requires-Dist: hyperliquid-python-sdk>=0.18.0
Requires-Dist: eth-account>=0.13.4
Dynamic: license-file

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*

- [Hyperliquid CLI (Python)](#hyperliquid-cli-python)
  - [Setup](#setup)
    - [Requirements](#requirements)
    - [Installation](#installation)
    - [Bash Completion](#bash-completion)
  - [CLI Basics](#cli-basics)
    - [Global Options](#global-options)
    - [Supported Commands](#supported-commands)
    - [Configuration](#configuration)
    - [Security Notes](#security-notes)
  - [Development](#development)
    - [Run for Development](#run-for-development)
    - [JSON Pattern Tests](#json-pattern-tests)
  - [Order Features](#order-features)
    - [TWAP Orders](#twap-orders)
    - [Stake-Based Orders](#stake-based-orders)
  - [Project](#project)
    - [Acknowledgments](#acknowledgments)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

# Hyperliquid CLI (Python)

Python CLI for Hyperliquid account management, market data, and order execution.

## Setup

### Requirements

- Python 3.10+
- `hyperliquid-python-sdk` for API interactions
- `rich` for pretty console output
- `eth-account` for key management
- Optional: `make` for installation and uninstallation scripts

### Installation

Install from git and also set up Bash completion with `make install`:

```bash
git clone https://github.com/haturatu/hl.git
cd hl
make install
```

Install from source for local development:

```bash
cd hl
python3 -m pip install --user -e .
```

Install from PyPI:

```bash
pip install --user hyperliquid-cli-python
```

After installation, the `hl` command is available:

```bash
hl --help
```

### Bash Completion

`hl` can print a Bash completion script for top-level commands and subcommands.

Note: `pip install -e .` and `pip install hyperliquid-cli-python` install the
`hl` command, but they do not automatically enable Bash completion.

Enable it for the current shell:

```bash
eval "$(hl completion bash)"
```

Persist it in `~/.bashrc`:

```bash
echo 'eval "$(hl completion bash)"' >> ~/.bashrc
```

If you want the old helper flow that also edits `~/.bashrc`, `make install`
still exists.

To remove the package and any managed `~/.bashrc` completion line:

```bash
make uninstall
```

## CLI Basics

### Global Options

- `--json` Output JSON
- `--testnet` Use testnet

### Supported Commands

- `hl account add|ls|set-default|remove`
- `hl account positions|orders|balances|portfolio`
- `hl order ls|limit|market|tpsl|twap|twap-cancel|cancel|cancel-all|set-leverage|configure`
- `hl asset price|book|leverage`
- `hl markets ls|search`
- `hl referral set|status`

### Configuration

- DB: `~/.hl/hl.db`
- Order config: `~/.hl/order-config.json`

Environment variable fallback (when DB account is not configured):

- `HYPERLIQUID_PRIVATE_KEY`
- `HYPERLIQUID_WALLET_ADDRESS`

### Security Notes

Account data stored in `~/.hl/hl.db` is encrypted at rest for these fields:

- `user_address`
- `api_wallet_public_key`
- `api_wallet_private_key`

The current implementation derives a 32-byte key as follows:

1. Resolve the command path used to run `hl`
2. Hash that path with SHA-256
3. Use the resulting digest as the ChaCha20 key

Each stored value is encrypted independently with its own random nonce.

This means:

- the same installed command path can transparently decrypt the saved values
- changing the executable path can make existing saved account data undecryptable
- this is path-bound encryption, not password-based encryption

Example:

```bash
$ which hl
/home/haturatu/.local/bin/hl
```

If `hl` is installed at a user-local path like `/home/haturatu/.local/bin/hl`, then
the encryption key is effectively tied to that installed command path. In normal usage,
that often behaves like "the user who has this `hl` on their path can decrypt the DB".
So in practice it can look close to per-user decryption when each user has their own
home directory and their own local install path.

Important limitation:

- this mechanism does **not** prove OS user identity by itself
- if another OS user can both read `~/.hl/hl.db` and execute the same `hl` binary
  path, path-based derivation alone does not prevent that user from decrypting the data

So this mechanism is mainly useful as a coupling between the saved DB contents and the
specific installed command path. It helps prevent casual reuse of the DB from a different
binary location, but it is not a substitute for filesystem permissions or disk encryption.

Environment variable fallback still exists when DB account data is not configured:

- `HYPERLIQUID_PRIVATE_KEY`
- `HYPERLIQUID_WALLET_ADDRESS`

These environment variables are not stored in the encrypted DB. They are read as
plain process environment values at runtime.

Practical guidance:

- Use wallets or API keys that would not be catastrophic if leaked
- Restrict which OS user can run this tool
- If you need stronger protection, use disk encryption as the higher-level control

## Development

### Run for Development

```bash
cd hl
PYTHONPATH=src python -m hl_cli.cli.argparse_main --help
```

### JSON Pattern Tests

`tests/` validates that every subcommand pattern produces parseable raw JSON output in `--json` mode.

```bash
cd hl
PYTHONPATH=src python -m unittest -v tests.test_json_patterns
```

## Order Features

Order side semantics:

- `buy` / `sell` are for spot markets
- `long` / `short` are for perp markets
- `close` is for closing an open perp position

### TWAP Orders

`hyperliquid-python-sdk` does not provide a high-level TWAP method, so this CLI signs and submits the official
`exchange` actions `twapOrder` / `twapCancel`. Successful submissions store the returned `twapId`
locally under `~/.hl/twap_orders.json` so the CLI can show and cancel tracked TWAPs later.

TWAP is perp-only, so use `long` / `short`.

```bash
# 30-minute native TWAP
hl order twap long 1.0 BTC 30

# Derive total TWAP size from USD margin (stake * leverage)
hl order twap long 0 BTC 30 --stake 5

# Compatibility format: 5,10 is sent as total 50 minutes
hl order twap short 2.0 ETH 5,10 --randomize

# Cancel TWAP
hl order twap-cancel BTC 12345

# Or list tracked active TWAPs and pick one interactively
hl order twap-cancel
```

### Stake-Based Orders

`--stake` is used by the CLI to derive order size.

Mode-specific behavior:

- `buy` / `sell` derive spot order size
- `long` / `short` derive perp order size
- `--leverage`, `--cross`, `--isolated`, and `--reduce-only` are only supported with `long` / `short`

- If you pass `--stake 50 --leverage 20`, the CLI derives size from about `$1000`
  of notional (`50 * 20`).
- If you pass `--stake 50` without `--leverage`, the CLI derives size from about
  `$50` of notional.

Important: omitting `--leverage` does **not** mean your account or position is forced
to `1x`. It only means the CLI does not multiply `--stake` by leverage when calculating
the order size. If the exchange/account already has leverage set for that asset, the
resulting position can still show that existing leverage in `hl account positions`.

This means:

- `--stake 50` means the CLI sizes the order from about `$50` of notional
- `--stake 50 --leverage 20` means about `$1000` of position notional

So:

- `--leverage` changes how `--stake` is converted into order size
- existing leverage on the exchange can still affect margin usage and the leverage
  shown later in `hl account positions`

```bash
# Spot buy: CLI sizes the order from about $50 of spot notional
hl order market buy @142 --stake 50

# Spot sell: CLI sizes the order from about $50 of spot notional
hl order market sell @142 --stake 50

# Perp long with leverage 20: CLI sizes the order from about $1,000 of BTC notional
hl order limit long BTC 65000 --stake 50 --leverage 20 --cross

# Perp long with leverage 20: CLI sizes the order from about $1,000 of BTC notional
hl order market long BTC --stake 50 --leverage 20 --isolated

# Example:
# BTC at 69,000
# - --stake 50                 => about 0.000724 BTC of order size
# - --stake 50 --leverage 20   => about 0.01449 BTC
#
# ETH at 2,020
# - --stake 50                 => about 0.02475 ETH of order size
# - --stake 50 --leverage 20   => about 0.2475 ETH

# Set leverage and margin mode at order time
hl order limit long BTC 65000 --stake 50 --leverage 20 --cross
hl order market long BTC --stake 50 --leverage 20 --isolated

# Set leverage directly
hl order set-leverage BTC 20 --cross

# If leverage is invalid, show warning and retry with coin maxLeverage from /info type=meta
hl order set-leverage BTC 60

# Close full position by coin
hl order market close ETH
hl order market close xyz:TSLA

# Close 50% of a position
hl order market close ETH --ratio 0.5

# Set TP/SL trigger orders for an open position
hl order tpsl ETH --tp 1900 --sl 1800
hl order tpsl ETH --sl 1800 --ratio 0.5
```

## Project

### Acknowledgments

- https://app.hyperliquid.xyz/
- https://github.com/chrisling-dev/hyperliquid-cli
- https://github.com/ehfuzzz/hyperliquid-CLI

This project is primarily a Python implementation of
https://github.com/chrisling-dev/hyperliquid-cli.
Some features, including the TWAP order implementation, are also based on ideas from
https://github.com/ehfuzzz/hyperliquid-CLI.

This repository also includes changes such as expanded `order` subcommands, the
`--stake` option, and additional `market` subcommand functionality.

At the moment, I am not fully sure how this should be handled from a licensing and
attribution perspective, so this repository is being published under my BSD 2-Clause
License as a temporary choice. If you have a better idea for the appropriate license
notice or attribution, please open an issue.
