{"id":50304889,"url":"https://github.com/doedja/hibachi-go","last_synced_at":"2026-05-28T15:30:39.708Z","repository":{"id":352904789,"uuid":"1212949167","full_name":"doedja/hibachi-go","owner":"doedja","description":"Unofficial Go SDK for the Hibachi perpetual futures exchange — REST + WebSocket (market, account, trade) with auto-reconnect","archived":false,"fork":false,"pushed_at":"2026-04-21T16:02:16.000Z","size":52,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-21T17:43:32.111Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/doedja.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-16T22:30:02.000Z","updated_at":"2026-04-21T16:02:20.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/doedja/hibachi-go","commit_stats":null,"previous_names":["doedja/hibachi-go"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/doedja/hibachi-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doedja%2Fhibachi-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doedja%2Fhibachi-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doedja%2Fhibachi-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doedja%2Fhibachi-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/doedja","download_url":"https://codeload.github.com/doedja/hibachi-go/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doedja%2Fhibachi-go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33615489,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-28T15:30:37.531Z","updated_at":"2026-05-28T15:30:39.697Z","avatar_url":"https://github.com/doedja.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# hibachi-go\n\nUnofficial Go SDK for the [Hibachi](https://hibachi.xyz) decentralized perpetual futures exchange. Not affiliated with Hibachi.\n\n- Public REST endpoints for market data (no auth)\n- Authenticated REST endpoints for trading, accounts, and capital flows\n- WebSocket clients for market data, account streams, and trade execution\n- Auto-reconnect on all three WebSocket clients\n- Typed error hierarchy for precise handling\n\nRequires Go 1.25 or newer.\n\n## Sign up\n\nNew to Hibachi? Ride in on my referral: [hibachi.xyz/r/hoshii](https://hibachi.xyz/r/hoshii).\nFee rebate for you, kickback for me. Good deal.\n\n## Install\n\n```\ngo get github.com/doedja/hibachi-go\n```\n\n## Public market data (no auth)\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"log\"\n\n    hibachi \"github.com/doedja/hibachi-go\"\n)\n\nfunc main() {\n    ctx := context.Background()\n    client, err := hibachi.NewClient()\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    info, err := client.GetExchangeInfo(ctx)\n    if err != nil {\n        log.Fatal(err)\n    }\n    for _, c := range info.FutureContracts {\n        fmt.Printf(\"%-12s id=%d tick=%s step=%s\\n\", c.Symbol, c.ID, c.TickSize, c.StepSize)\n    }\n\n    p, err := client.GetPrices(ctx, \"BTC/USDT-P\")\n    if err != nil {\n        log.Fatal(err)\n    }\n    fmt.Printf(\"BTC mark=%s bid=%s ask=%s\\n\", p.MarkPrice, p.BidPrice, p.AskPrice)\n}\n```\n\nAvailable REST methods on `Client`:\n\n- Market: `GetExchangeInfo`, `GetInventory`, `GetPrices`, `GetStats`, `GetTrades`, `GetKlines`, `GetOpenInterest`, `GetOrderbook`\n- Account: `GetAccountInfo`, `GetAccountTrades`, `GetSettlementsHistory`, `GetPendingOrders`, `GetOrderDetails`\n- Trading: `PlaceMarketOrder`, `PlaceLimitOrder`, `UpdateOrder`, `CancelOrder`, `CancelAllOrders`, `BatchOrders`\n- Capital: `GetCapitalBalance`, `GetCapitalHistory`, `GetDepositInfo`, `Withdraw`, `Transfer`\n\n## Authenticated client\n\nAuthenticated endpoints require an API key, account ID, and ECDSA private key.\n\n```go\nclient, err := hibachi.NewClient(\n    hibachi.WithAPIKey(os.Getenv(\"HIBACHI_API_KEY\")),\n    hibachi.WithAccountID(accountID),\n    hibachi.WithPrivateKey(os.Getenv(\"HIBACHI_PRIVATE_KEY\")),\n)\n```\n\nPlace a post-only limit buy, fetching the contract on the fly:\n\n```go\nimport \"github.com/shopspring/decimal\"\n\nresult, err := client.PlaceLimitOrder(ctx,\n    \"BTC/USDT-P\",\n    hibachi.SideBid,\n    decimal.RequireFromString(\"0.001\"),\n    decimal.RequireFromString(\"50000\"),\n    decimal.RequireFromString(\"0.001\"),\n    hibachi.WithOrderFlags(hibachi.OrderFlagsPostOnly),\n)\nif err != nil {\n    log.Fatal(err)\n}\nfmt.Printf(\"placed: orderId=%s nonce=%s\\n\", result.OrderID, result.Nonce)\n```\n\nBatch orders in a single request:\n\n```go\nprice := decimal.RequireFromString(\"50000\")\norderID := int64(123)\n\nresp, err := client.BatchOrders(ctx, []hibachi.BatchOrderAction{\n    hibachi.CreateOrder{\n        Symbol:         \"BTC/USDT-P\",\n        Side:           hibachi.SideBid,\n        Quantity:       decimal.RequireFromString(\"0.001\"),\n        Price:          \u0026price,\n        MaxFeesPercent: decimal.RequireFromString(\"0.001\"),\n    },\n    hibachi.CancelOrder{OrderID: \u0026orderID},\n})\n```\n\n## WebSocket: market data\n\n```go\nclient := ws.NewMarketClient(ws.MarketClientOptions{})\nif err := client.Connect(ctx); err != nil {\n    log.Fatal(err)\n}\ndefer client.Disconnect()\n\nclient.On(\"mark_price\", func(data json.RawMessage) {\n    fmt.Printf(\"mark price: %s\\n\", data)\n})\n\nerr := client.Subscribe(ctx,\n    hibachi.WSSubscription{Topic: hibachi.WSTopicMarkPrice, Symbol: \"BTC/USDT-P\"},\n    hibachi.WSSubscription{Topic: hibachi.WSTopicTrades, Symbol: \"BTC/USDT-P\"},\n)\n```\n\nHandlers receive the raw message payload as `json.RawMessage`. Unmarshal it\ninto whatever shape the topic produces.\n\n## WebSocket: account stream\n\n```go\nclient := ws.NewAccountClient(ws.AccountClientOptions{\n    APIKey:    apiKey,\n    AccountID: accountID,\n})\nif err := client.Connect(ctx); err != nil {\n    log.Fatal(err)\n}\ndefer client.Disconnect()\n\nsnapshot, err := client.StreamStart(ctx)\nif err != nil {\n    log.Fatal(err)\n}\nfmt.Printf(\"balance: %s positions: %d\\n\",\n    snapshot.AccountSnapshot.Balance, len(snapshot.AccountSnapshot.Positions))\n\nclient.On(\"balance\", func(data json.RawMessage) { /* ... */ })\nclient.On(\"position\", func(data json.RawMessage) { /* ... */ })\nclient.On(\"order\", func(data json.RawMessage) { /* ... */ })\n\n// ListenLoop blocks, auto-pings every 10s, auto-reconnects and re-StreamStarts.\nif err := client.ListenLoop(ctx); err != nil \u0026\u0026 ctx.Err() == nil {\n    log.Fatal(err)\n}\n```\n\n## WebSocket: trading\n\nThe trade client does **not** auto-sign. You build the binary order payload,\nsign it, then pass the signature into `PlaceOrder`. This keeps the SDK\nagnostic to where the signer lives (local key, HSM, remote signer).\n\n```go\nsigner, _ := hibachi.NewSigner(privateKey)\n\nrest, _ := hibachi.NewClient()\ninfo, _ := rest.GetExchangeInfo(ctx)\nvar contract *hibachi.FutureContract\nfor i := range info.FutureContracts {\n    if info.FutureContracts[i].Symbol == \"BTC/USDT-P\" {\n        contract = \u0026info.FutureContracts[i]\n        break\n    }\n}\n\ntrade := ws.NewTradeClient(ws.TradeClientOptions{\n    APIKey:    apiKey,\n    AccountID: accountID,\n    Signer:    signer,\n})\nif err := trade.Connect(ctx); err != nil {\n    log.Fatal(err)\n}\ndefer trade.Disconnect()\n\n// Recommended: if the WS drops, let the exchange cancel all orders for us.\ntrade.EnableCancelOnDisconnect(ctx)\n\nnonce := time.Now().UnixNano() / 1000\nqty := decimal.RequireFromString(\"0.001\")\nprice := decimal.RequireFromString(\"50000\")\nfees := decimal.RequireFromString(\"0.001\")\n\npayload := hibachi.CreateOrderPayload(\n    nonce, contract.ID, qty, hibachi.SideBid, \u0026price, fees,\n    contract.UnderlyingDecimals, contract.SettlementDecimals,\n)\nsignature, _ := signer.Sign(payload)\n\npriceStr := hibachi.FullPrecisionString(price)\nresp, err := trade.PlaceOrder(ctx, hibachi.OrderPlaceParams{\n    AccountID:      accountID,\n    Nonce:          nonce,\n    Symbol:         \"BTC/USDT-P\",\n    OrderType:      \"LIMIT\",\n    Side:           string(hibachi.SideBid),\n    Quantity:       hibachi.FullPrecisionString(qty),\n    Price:          \u0026priceStr,\n    MaxFeesPercent: hibachi.FullPrecisionString(fees),\n    Signature:      signature,\n})\n```\n\n### Auto-reconnect\n\nAll three WebSocket clients reconnect automatically.\n\n- **Market**: reconnects in the read pump; re-subscribes to tracked topics.\n  `Done()` returns only if the reconnect budget is exhausted.\n- **Account**: reconnects in `ListenLoop`; re-issues `StreamStart` to get a\n  fresh snapshot + listen key, restarts the ping loop.\n- **Trade**: reconnects lazily on the next call after a failure. The call\n  that hits the dead connection returns `*hibachi.WSConnectionError` so the\n  caller's operation fails cleanly. Nothing is retried automatically, which\n  avoids duplicate orders.\n\nRegister `OnDisconnect(func(error))` and `OnReconnect(...)` to observe the\nlifecycle. Configure budget via `RetryOptions` (set `MaxRetries: -1` for\ninfinite).\n\n## Error handling\n\nThe SDK exposes a rooted error hierarchy. Every concrete error unwraps to\n`*HibachiError`, so `errors.As` works at any level.\n\n```\nHibachiError\n├── ExchangeError\n│   ├── APIError\n│   ├── MaintenanceError\n│   └── BadHTTPStatusError → {BadRequest,Unauthorized,Forbidden,NotFound,RateLimited,…}Error\n├── TransportError\n│   ├── ConnectionError\n│   ├── TimeoutError\n│   ├── WSConnectionError\n│   ├── WSMessageError\n│   ├── SerializationError\n│   └── DeserializationError\n└── ValidationError\n    └── MissingCredentialsError\n```\n\n```go\nvar rateLimited *hibachi.RateLimitedError\nif errors.As(err, \u0026rateLimited) {\n    backoff()\n}\n\nvar wsErr *hibachi.WSConnectionError\nif errors.As(err, \u0026wsErr) {\n    // trade client: operation failed, reconnect will be attempted lazily\n}\n```\n\n## Signing\n\nAll private endpoints and order operations are signed. Pass the hex private\nkey to `WithPrivateKey(...)` or build the signer directly with\n`NewSigner(...)`. A key that is 64 hex characters (with or without `0x`) is\ntreated as an ECDSA (secp256k1) key. Otherwise it is treated as raw HMAC-SHA256\ninput.\n\nThe order payload layout for reference:\n\n```\n 0..8   nonce          big-endian int64\n 8..12  contractId     big-endian int32\n12..20  quantity       big-endian int64, scaled by 10^underlyingDecimals\n20..24  side           0 = ASK / SELL, 1 = BID / BUY\n[24..32 price          big-endian int64, price * 2^32 * 10^(settlementDecimals - underlyingDecimals), LIMIT only]\nN..N+8  maxFeesPercent big-endian int64, scaled by 10^8\n```\n\nMarket orders omit the price block (32-byte payload); limit orders include\nit (40 bytes). `CreateOrderPayload` and `PriceToBytes` implement this.\n\n## Examples\n\nRunnable examples live in `examples/`:\n\n- `examples/rest`: public REST calls, authenticated trading is commented out\n- `examples/ws_market`: subscribe to `mark_price` and `trades`\n- `examples/ws_account`: stream balance, position, and order events\n- `examples/ws_trade`: place + cancel a limit order end-to-end\n\nRun the WebSocket examples with credentials in env vars:\n\n```\nHIBACHI_API_KEY=... HIBACHI_ACCOUNT_ID=... HIBACHI_PRIVATE_KEY=0x... \\\n    go run ./examples/ws_trade\n```\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoedja%2Fhibachi-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdoedja%2Fhibachi-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoedja%2Fhibachi-go/lists"}