{"id":47693384,"url":"https://github.com/khavrtrading/flowex","last_synced_at":"2026-04-07T01:01:28.800Z","repository":{"id":347458625,"uuid":"1193130896","full_name":"KhavrTrading/flowex","owner":"KhavrTrading","description":"Real-time multi-exchange crypto market data in Go — WebSocket streaming, order book metrics, and per-symbol actor workers for Binance, Bybit, and Bitget.","archived":false,"fork":false,"pushed_at":"2026-03-28T00:20:49.000Z","size":60,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-03T02:49:43.828Z","etag":null,"topics":["binance","bitget","bybit","crypto","go","golang","market-data","order-book","trading","websocket"],"latest_commit_sha":null,"homepage":null,"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/KhavrTrading.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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-03-26T22:52:12.000Z","updated_at":"2026-03-28T00:20:22.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/KhavrTrading/flowex","commit_stats":null,"previous_names":["khavrtrading/flowex"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/KhavrTrading/flowex","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KhavrTrading%2Fflowex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KhavrTrading%2Fflowex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KhavrTrading%2Fflowex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KhavrTrading%2Fflowex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KhavrTrading","download_url":"https://codeload.github.com/KhavrTrading/flowex/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KhavrTrading%2Fflowex/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31495466,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-06T17:22:55.647Z","status":"ssl_error","status_checked_at":"2026-04-06T17:22:54.741Z","response_time":112,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["binance","bitget","bybit","crypto","go","golang","market-data","order-book","trading","websocket"],"created_at":"2026-04-02T16:04:12.138Z","updated_at":"2026-04-07T01:01:28.783Z","avatar_url":"https://github.com/KhavrTrading.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# flowex\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/KhavrTrading/flowex.svg)](https://pkg.go.dev/github.com/KhavrTrading/flowex)\n[![Go Report Card](https://goreportcard.com/badge/github.com/KhavrTrading/flowex)](https://goreportcard.com/report/github.com/KhavrTrading/flowex)\n[![Go](https://github.com/KhavrTrading/flowex/actions/workflows/go.yml/badge.svg)](https://github.com/KhavrTrading/flowex/actions/workflows/go.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nProduction-tested Go library for real-time cryptocurrency market data via WebSocket. Supports **Binance Futures**, **Bybit Linear**, and **Bitget Futures** with a unified interface.\n\n## Install\n\n```bash\ngo get github.com/KhavrTrading/flowex\n```\n\nRequires **Go 1.22+**\n\n## Package Map\n\n```\nflowex/\n  binance/     — Binance Futures WebSocket manager\n  bybit/       — Bybit Linear WebSocket manager\n  bitget/      — Bitget Futures/Spot WebSocket manager\n  ws/          — Core engine: client, worker, manager, snapshots\n  models/      — Candle, Trade, Ticker types\n  depth/       — Order book metrics (75 fields) + time-bucketed store\n  candles/     — REST fetchers + timeframe aggregation\n  indicators/  — EMA, RSI, MACD, ATR, Bollinger, StochRSI, S/R\n  examples/    — Working examples\n```\n\n## Quick Start\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"os\"\n    \"os/signal\"\n    \"time\"\n\n    \"github.com/KhavrTrading/flowex/binance\"\n)\n\nfunc main() {\n    mgr := binance.NewManager()\n\n    // Subscribe to candles + depth + trades for BTCUSDT\n    mgr.SubscribeAll(\"BTCUSDT\", nil, nil, nil)\n\n    // Poll snapshots every 5 seconds\n    go func() {\n        for range time.Tick(5 * time.Second) {\n            snap := mgr.GetSnapshot(\"BTCUSDT\")\n            if snap == nil {\n                continue\n            }\n            fmt.Printf(\"Candles: %d | Trades: %d | Depth points: %d\\n\",\n                len(snap.Candles), len(snap.Trades), snap.DepthStore.Size())\n\n            if len(snap.Candles) \u003e 0 {\n                c := snap.Candles[len(snap.Candles)-1]\n                fmt.Printf(\"  Last: O=%.2f H=%.2f L=%.2f C=%.2f V=%.4f\\n\",\n                    c.Open, c.High, c.Low, c.Close, c.Volume)\n            }\n        }\n    }()\n\n    // Wait for Ctrl+C\n    ch := make(chan os.Signal, 1)\n    signal.Notify(ch, os.Interrupt)\n    \u003c-ch\n\n    mgr.Shutdown()\n}\n```\n\nSee [examples/basic/main.go](examples/basic/main.go) for a complete working example with worker hooks, snapshot polling, and metrics monitoring.\n\n---\n\n## Connecting to Exchanges\n\nEach exchange has its own manager. Create one, subscribe to symbols, read snapshots.\n\n### Binance Futures\n\n```go\nimport \"github.com/KhavrTrading/flowex/binance\"\n\nmgr := binance.NewManager()                        // default config\nmgr.SubscribeAll(\"BTCUSDT\", nil, nil, nil)          // candles + depth + trades\nmgr.SubscribeAll(\"ETHUSDT\", nil, nil, nil)          // subscribe to multiple symbols\n```\n\n### Bybit V5 Linear\n\n```go\nimport \"github.com/KhavrTrading/flowex/bybit\"\n\nmgr := bybit.NewManager()\nmgr.SubscribeAll(\"BTCUSDT\", nil, nil, nil)\n```\n\n### Bitget USDT-Futures\n\n```go\nimport \"github.com/KhavrTrading/flowex/bitget\"\n\nmgr := bitget.NewManager()                          // defaults to USDT-FUTURES\nmgr.SubscribeAll(\"BTCUSDT\", nil, nil, nil)\n\n// Or for spot:\ncfg := bitget.DefaultManagerConfig()\ncfg.InstType = bitget.InstSpot\nspotMgr := bitget.NewManagerWithConfig(cfg)\n```\n\n### Multi-Exchange (same symbol, all exchanges)\n\n```go\nbinanceMgr := binance.NewManager()\nbybitMgr   := bybit.NewManager()\nbitgetMgr  := bitget.NewManager()\n\nfor _, symbol := range []string{\"BTCUSDT\", \"ETHUSDT\", \"SOLUSDT\"} {\n    binanceMgr.SubscribeAll(symbol, nil, nil, nil)\n    bybitMgr.SubscribeAll(symbol, nil, nil, nil)\n    bitgetMgr.SubscribeAll(symbol, nil, nil, nil)\n}\n```\n\n---\n\n## Subscribe Selectively\n\nYou don't have to subscribe to everything. Pick what you need:\n\n```go\nmgr := binance.NewManager()\n\n// Only candles\nmgr.SubscribeCandle(\"BTCUSDT\", nil)\n\n// Only depth\nmgr.SubscribeDepth(\"ETHUSDT\", nil)\n\n// Only trades\nmgr.SubscribeTrade(\"SOLUSDT\", nil)\n\n// Unsubscribe one stream\nmgr.Unsubscribe(\"BTCUSDT\", ws.StreamCandle)\n\n// Unsubscribe everything for a symbol\nmgr.UnsubscribeAll(\"ETHUSDT\")\n```\n\n---\n\n## Depth Streams: Levels \u0026 Speed\n\nEach exchange offers different order book depth options.\n\n### Binance Depth Options\n\n```go\nimport \"github.com/KhavrTrading/flowex/binance\"\n\nmgr := binance.NewManager()\n\n// Default: 20 levels, exchange default speed\nmgr.SubscribeDepth(\"BTCUSDT\", nil)\n\n// 5 levels, 100ms updates (fastest)\nmgr.SubscribeDepthWithConfig(\"BTCUSDT\", binance.Depth5, binance.Speed100ms, nil)\n\n// 10 levels, 500ms updates (lowest bandwidth)\nmgr.SubscribeDepthWithConfig(\"ETHUSDT\", binance.Depth10, binance.Speed500ms, nil)\n\n// 20 levels, 100ms updates\nmgr.SubscribeDepthWithConfig(\"SOLUSDT\", binance.Depth20, binance.Speed100ms, nil)\n```\n\nAvailable options:\n| Levels | Constants |\n|--------|-----------|\n| 5 | `binance.Depth5` |\n| 10 | `binance.Depth10` |\n| 20 | `binance.Depth20` (default) |\n\n| Speed | Constants | Notes |\n|-------|-----------|-------|\n| 100ms | `binance.Speed100ms` | Fastest, highest bandwidth |\n| 250ms | `binance.Speed250ms` | |\n| 500ms | `binance.Speed500ms` | Lowest bandwidth |\n| default | `binance.SpeedDefault` | Exchange decides |\n\n### Bybit Depth Options\n\n```go\nimport \"github.com/KhavrTrading/flowex/bybit\"\n\nmgr := bybit.NewManager()\n\n// Default: 50 levels\nmgr.SubscribeDepth(\"BTCUSDT\", nil)\n\n// Top-of-book only (1 level) - minimal bandwidth\nmgr.SubscribeDepthWithLevel(\"BTCUSDT\", bybit.Depth1, nil)\n\n// 200 levels\nmgr.SubscribeDepthWithLevel(\"BTCUSDT\", bybit.Depth200, nil)\n\n// 500 levels - full book\nmgr.SubscribeDepthWithLevel(\"BTCUSDT\", bybit.Depth500, nil)\n```\n\nAvailable: `bybit.Depth1`, `bybit.Depth50` (default), `bybit.Depth200`, `bybit.Depth500`\n\n### Bitget Depth Options\n\n```go\nimport \"github.com/KhavrTrading/flowex/bitget\"\n\nmgr := bitget.NewManager()\n\n// Default: full book (\"books\")\nmgr.SubscribeDepth(\"BTCUSDT\", nil)\n\n// Top 5 levels only\nmgr.SubscribeDepthWithChannel(\"BTCUSDT\", bitget.DepthBooks5, nil)\n\n// Top 15 levels\nmgr.SubscribeDepthWithChannel(\"BTCUSDT\", bitget.DepthBooks15, nil)\n```\n\nAvailable: `bitget.DepthFull` (default), `bitget.DepthBooks5`, `bitget.DepthBooks15`\n\n---\n\n## Trade Streams\n\n### Binance: Aggregate vs Individual Trades\n\nBinance offers two trade stream types:\n\n```go\nmgr := binance.NewManager()\n\n// Default: aggregate trades (recommended - lower bandwidth)\nmgr.SubscribeTrade(\"BTCUSDT\", nil)\n\n// Individual trades (every single fill, higher volume)\nmgr.SubscribeTradeWithMode(\"BTCUSDT\", binance.TradeIndividual, nil)\n\n// Or set it globally in config:\ncfg := binance.DefaultManagerConfig()\ncfg.TradeMode = binance.TradeIndividual\nmgr = binance.NewManagerWithConfig(cfg)\n```\n\n| Mode | Stream | Notes |\n|------|--------|-------|\n| `binance.TradeAggregated` | `@aggTrade` | Trades at same price/time combined (default, lower bandwidth) |\n| `binance.TradeIndividual` | `@trade` | Every individual fill (higher volume, more granular) |\n\n### Bybit \u0026 Bitget\n\nThese exchanges have a single public trade stream each. No mode selection needed:\n\n```go\nbybitMgr.SubscribeTrade(\"BTCUSDT\", nil)   // publicTrade stream\nbitgetMgr.SubscribeTrade(\"BTCUSDT\", nil)  // trade stream\n```\n\n---\n\n## Candle Intervals\n\nAll exchanges default to 1-minute candles. You can change the interval:\n\n### Binance\n\n```go\nmgr := binance.NewManager()\n\nmgr.SubscribeCandle(\"BTCUSDT\", nil)                             // default 1m\nmgr.SubscribeCandleWithInterval(\"BTCUSDT\", \"5m\", nil)           // 5-minute\nmgr.SubscribeCandleWithInterval(\"ETHUSDT\", \"1h\", nil)           // 1-hour\nmgr.SubscribeCandleWithInterval(\"SOLUSDT\", \"4h\", nil)           // 4-hour\n```\n\nBinance intervals: `\"1m\"`, `\"3m\"`, `\"5m\"`, `\"15m\"`, `\"30m\"`, `\"1h\"`, `\"2h\"`, `\"4h\"`, `\"6h\"`, `\"8h\"`, `\"12h\"`, `\"1d\"`, `\"1w\"`\n\n### Bybit\n\n```go\nmgr := bybit.NewManager()\n\nmgr.SubscribeCandleWithInterval(\"BTCUSDT\", \"5\", nil)    // 5-minute\nmgr.SubscribeCandleWithInterval(\"BTCUSDT\", \"60\", nil)   // 1-hour\nmgr.SubscribeCandleWithInterval(\"BTCUSDT\", \"D\", nil)    // daily\n```\n\nBybit intervals: `\"1\"`, `\"3\"`, `\"5\"`, `\"15\"`, `\"30\"`, `\"60\"`, `\"120\"`, `\"240\"`, `\"360\"`, `\"720\"`, `\"D\"`, `\"W\"`, `\"M\"`\n\n### Bitget\n\n```go\nmgr := bitget.NewManager()\n\nmgr.SubscribeCandleWithInterval(\"BTCUSDT\", \"5m\", nil)   // 5-minute\nmgr.SubscribeCandleWithInterval(\"BTCUSDT\", \"1H\", nil)   // 1-hour\nmgr.SubscribeCandleWithInterval(\"BTCUSDT\", \"1D\", nil)   // daily\n```\n\nBitget intervals: `\"1m\"`, `\"5m\"`, `\"15m\"`, `\"30m\"`, `\"1H\"`, `\"4H\"`, `\"6H\"`, `\"12H\"`, `\"1D\"`, `\"1W\"`\n\n---\n\n## Setting Defaults via Config\n\nInstead of passing options on every call, set them once in the manager config:\n\n```go\n// Binance: 5-level depth at 100ms, individual trades, 5m candles\ncfg := binance.DefaultManagerConfig()\ncfg.DepthLevel = binance.Depth5\ncfg.DepthSpeed = binance.Speed100ms\ncfg.TradeMode  = binance.TradeIndividual\ncfg.Interval   = \"5m\"\n\nmgr := binance.NewManagerWithConfig(cfg)\nmgr.SubscribeAll(\"BTCUSDT\", nil, nil, nil) // uses all the config above\n```\n\n```go\n// Bybit: 200-level depth, 15m candles\ncfg := bybit.DefaultManagerConfig()\ncfg.DepthLevel = bybit.Depth200\ncfg.Interval   = \"15\"\n\nmgr := bybit.NewManagerWithConfig(cfg)\n```\n\n```go\n// Bitget: spot market, books5 depth\ncfg := bitget.DefaultManagerConfig()\ncfg.InstType     = bitget.InstSpot\ncfg.DepthChannel = bitget.DepthBooks5\ncfg.Interval     = \"5m\"\n\nmgr := bitget.NewManagerWithConfig(cfg)\n```\n\n---\n\n## Reading Data: Snapshots\n\nEvery symbol produces immutable snapshots every second (configurable). Read them lock-free from any goroutine.\n\n```go\n// Snapshot is an immutable, point-in-time view of a symbol's state.\ntype Snapshot struct {\n    Timestamp  time.Time               // when the snapshot was taken\n    Candles    []models.CandleHLCV     // historical + live candle bars\n    DepthStore *depth.Store            // order book metrics with time-bucketed storage\n    Trades     []models.NormalizedTrade // recent trades, normalized across exchanges\n}\n```\n\n```go\nsnap := mgr.GetSnapshot(\"BTCUSDT\")\nif snap == nil {\n    // No data yet\n    return\n}\n\n// Candles (OHLCV)\nfor _, c := range snap.Candles {\n    fmt.Printf(\"ts=%d O=%.2f H=%.2f L=%.2f C=%.2f V=%.4f\\n\",\n        c.Ts, c.Open, c.High, c.Low, c.Close, c.Volume)\n}\n\n// Trades (normalized across exchanges)\nfor _, t := range snap.Trades {\n    fmt.Printf(\"[%s] %s %.4f @ %.2f\\n\", t.Exchange, t.Side, t.SizeUSD, t.Price)\n}\n\n// Depth metrics\nlatest := snap.DepthStore.GetLatest()\nif latest != nil {\n    fmt.Printf(\"Spread: %.2f bps | Imbalance: %.3f | Mid: %.2f\\n\",\n        latest.SpreadBps, latest.ImbalanceRatio10, latest.MidPrice)\n}\n\n// Historical depth (last 30 seconds)\nrecent := snap.DepthStore.GetLastNSeconds(30)\n```\n\n---\n\n## Data Models\n\n### CandleHLCV\n\n```go\ntype CandleHLCV struct {\n    Ts     int64   // Unix millisecond timestamp\n    Open   float64\n    High   float64\n    Low    float64\n    Close  float64\n    Volume float64\n}\n```\n\nHelper methods: `GetTimestamp()`, `HL2()`, `HLC3()`.\n\n### CandleHLC\n\nLighter candle without Open/Volume — used by ATR, Bollinger, and Support/Resistance indicators.\n\n```go\ntype CandleHLC struct {\n    High  float64\n    Low   float64\n    Close float64\n}\n```\n\n### NormalizedTrade\n\nUnified trade format across all exchanges.\n\n```go\ntype NormalizedTrade struct {\n    Timestamp int64   // Unix milliseconds\n    Price     float64\n    Size      float64 // base currency\n    SizeUSD   float64\n    Side      string  // \"buy\" or \"sell\"\n    TradeID   string\n    Symbol    string  // e.g. \"BTCUSDT\"\n    Exchange  string  // \"binance\", \"bybit\", \"bitget\"\n}\n```\n\n---\n\n## Custom Processing Hooks\n\nWorkers fire callbacks on every state change. Plug your own logic:\n\n```go\nworker := mgr.GetOrCreateWorker(\"BTCUSDT\")\n\n// Called after every candle update (same-minute update or new bar)\nworker.SetOnCandleUpdate(func(candles []models.CandleHLCV) {\n    if len(candles) \u003e= 14 {\n        closes := make([]float64, len(candles))\n        for i, c := range candles {\n            closes[i] = c.Close\n        }\n        rsi := indicators.CalculateRSI(closes, 14)\n        fmt.Printf(\"RSI(14): %.2f\\n\", rsi)\n    }\n})\n\n// Called after every depth update with the computed metrics\nworker.SetOnDepthUpdate(func(m depth.DepthMetrics) {\n    fmt.Printf(\"Bid liq: $%.0f | Ask liq: $%.0f | Spread: %.2f bps\\n\",\n        m.BidLiquidity10, m.AskLiquidity10, m.SpreadBps)\n})\n\n// Called after every trade\nworker.SetOnTradeUpdate(func(t models.NormalizedTrade) {\n    if t.SizeUSD \u003e 50000 {\n        fmt.Printf(\"LARGE %s: $%.0f @ %.2f\\n\", t.Side, t.SizeUSD, t.Price)\n    }\n})\n```\n\n---\n\n## Worker Tuning\n\n```go\ncfg := ws.DefaultWorkerConfig()\n\ncfg.CandleChSize = 128           // Candle channel buffer (default 64)\ncfg.DepthChSize  = 4096          // Depth channel buffer (default 2048)\ncfg.TradeChSize  = 4096          // Trade channel buffer (default 2048)\n\ncfg.MaxCandles         = 1500    // Candle history length (default 750)\ncfg.MaxNormTrades      = 5000    // Normalized trade buffer (default 2000)\ncfg.MaxDepthMetrics    = 20000   // Depth metric storage (default 10000)\ncfg.MaxDepthSeconds    = 1800    // Keep 30 min of depth data (default 1000s)\ncfg.RecentMetricsSize  = 200     // Fast-access depth buffer (default 100)\n\ncfg.SnapshotInterval = 500 * time.Millisecond  // Snapshot frequency (default 1s)\n\n// Pass to any exchange manager\nmgr := binance.NewManagerWithConfig(binance.ManagerConfig{WorkerConfig: cfg})\n```\n\n---\n\n## Historical Data (REST)\n\nFetch candles from exchange REST APIs:\n\n```go\nimport \"github.com/KhavrTrading/flowex/candles\"\n\n// Binance: up to 1500 per request\ndata, err := candles.FetchBinanceCandles(\"BTCUSDT\", \"1m\", 750)\ndata, err := candles.FetchBinanceCandles(\"ETHUSDT\", \"5m\", 500)\n\n// Bybit: up to 200 per request\ndata, err := candles.FetchBybitCandles(\"BTCUSDT\", \"1\", 200)\n\n// Bitget: up to 200 per request\ndata, err := candles.FetchBitgetCandles(\"BTCUSDT\", \"1m\", 200)\n\n// Also available as CandleHLC (without open/volume):\nhlc, err := candles.FetchBinanceCandleHLC(\"BTCUSDT\", \"1m\", 750)\n```\n\n### Candle Aggregation\n\n```go\nimport \"github.com/KhavrTrading/flowex/candles\"\n\noneMin, _ := candles.FetchBinanceCandles(\"BTCUSDT\", \"1m\", 750)\n\nfiveMin   := candles.Aggregate1mTo5m(oneMin)   // 1m -\u003e 5m\nfifteenMin := candles.Aggregate1mTo15m(oneMin)  // 1m -\u003e 15m\n\n// Custom duration (e.g., 3 minutes)\nthreeMin := candles.Aggregate(oneMin, 3*60*1000)\n```\n\n---\n\n## Technical Indicators\n\nBuilt-in standard indicators that work on `[]float64` or `[]models.CandleHLC`:\n\n```go\nimport \"github.com/KhavrTrading/flowex/indicators\"\n\ncloses := []float64{100, 101, 99, 102, 103, ...}\n\n// EMA\nema20 := indicators.CalculateEMA(closes, 20)\nemaSeries := indicators.CalculateEMAList(closes, 20)  // full series\n\n// RSI\nrsi := indicators.CalculateRSI(closes, 14)\n\n// MACD (12/26/9)\nmacd, signal, histogram := indicators.CalculateMACD(closes)\n\n// Stochastic RSI\nstochRSI := indicators.CalculateStochRSI(closes, 14, 14)\n\n// ATR (needs CandleHLC with High/Low/Close)\natr := indicators.CalculateATR(hlcCandles, 14)\natr, threshold, rising, err := indicators.EvaluateATR(hlcCandles, 14, 0.02)\n\n// Bollinger Mean Deviation\nscore, oscSD := indicators.BMD(hlcCandles, \"1m\")\nscore, oscSD = indicators.BollingerMeanDeviation(hlcCandles, 20, 25)\n\n// Support/Resistance (pivot-based)\nsupportPct, resistancePct, srScore := indicators.SupportResistance(hlcCandles, 5, 20)\n```\n\n---\n\n## Architecture\n\n```\n                    +-----------+\n  WebSocket  -----\u003e |  Client   |  (per-symbol connection, auto-reconnect, heartbeat)\n                    +-----+-----+\n                          |\n                     callbacks (non-blocking)\n                          |\n                    +-----v-----+\n                    |  Worker   |  (per-symbol actor goroutine, owns ALL state)\n                    |           |\n                    |  candles  |  \u003c- channel (buf 64)\n                    |  depth    |  \u003c- channel (buf 2048)\n                    |  trades   |  \u003c- channel (buf 2048)\n                    |           |\n                    |  hooks    |  -\u003e user callbacks (OnCandle, OnDepth, OnTrade)\n                    |           |\n                    +-----+-----+\n                          |\n                    atomic.Store (every 1s)\n                          |\n                    +-----v-----+\n                    | Snapshot  |  (immutable, lock-free reads from any goroutine)\n                    +-----------+\n```\n\n- **One goroutine per symbol** -- no locks needed for state mutation\n- **Non-blocking enqueue** -- if channel is full, oldest message is dropped (never blocks WS read)\n- **Atomic snapshots** -- readers never contend with the writer\n- **Auto-reconnect** -- connection drops trigger reconnect + resubscribe to all active streams\n\n---\n\n## Packages\n\n| Package | Description |\n|---------|-------------|\n| `ws/` | Core: BaseClient, SymbolWorker (actor), BaseManager (pool), PubSub[T], interfaces |\n| `binance/` | Binance Futures adapter (depth5/10/20, aggTrade/trade, all candle intervals) |\n| `bybit/` | Bybit V5 Linear adapter (depth 1/50/200/500, all candle intervals) |\n| `bitget/` | Bitget V2 adapter (books/books5/books15, spot/futures, all candle intervals) |\n| `models/` | CandleHLC, CandleHLCV, NormalizedTrade, TickerData |\n| `depth/` | Order book metrics (75 fields) + time-bucketed store with enrichment |\n| `indicators/` | EMA, RSI, ATR, MACD, StochRSI, Bollinger, Support/Resistance |\n| `indicators/technical/` | Batch-optimized calculator, ADX, MMI, signal types, movement tracking |\n| `candles/` | REST fetchers (Binance/Bybit/Bitget) + timeframe aggregator |\n\nSee [DOCUMENTATION.md](DOCUMENTATION.md) for the full API reference — all 75 depth metric fields, store query methods, worker monitoring, historical data seeding, and more.\n\n## Dependencies\n\nOnly two:\n- `github.com/gorilla/websocket`\n- `github.com/sirupsen/logrus`\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkhavrtrading%2Fflowex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkhavrtrading%2Fflowex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkhavrtrading%2Fflowex/lists"}