[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-80598":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":16,"stars30d":17,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":18,"rankGlobal":10,"rankLanguage":10,"license":10,"archived":19,"fork":19,"defaultBranch":20,"hasWiki":19,"hasPages":19,"topics":21,"createdAt":10,"pushedAt":10,"updatedAt":37,"readmeContent":38,"aiSummary":39,"trendingCount":16,"starSnapshotCount":16,"syncStatus":14,"lastSyncTime":40,"discoverSource":41},80598,"bybit-trading-bot","VtrAha\u002Fbybit-trading-bot","VtrAha","Bybit trading bot, perpetual trading bot, Bybit automated futures bot, USDT linear perp cryptocurrency trading bot Bybit V5 crypto bot automated, Bybit trading bot, perpetual trading bot, Bybit automated futures bot, USDT linear perp cryptocurrency trading bot Bybit V5 crypto bot automated,","https:\u002F\u002Fgithub.com\u002FVtrAha\u002Fbybit-trading-bot",null,"TypeScript",35,2626,2,1,0,91,10,false,"main",[22,23,24,25,26,27,28,29,30,31,32,33,34,35,36],"algo-trading","automated-trading","bybit","crypto-bot","crypto-trading-bot","cryptocurrency","derivatives","futures-trading","perpetual-futures","perpetual-trading","quantitative-trading","trading-bot","typescript","usdt-linear","v5-api","2026-06-12 02:04:04","# Bybit Trend Bot\n\n**Automated trend-following execution on Bybit V5 (USDT linear perpetuals)**\n\n**Repository:** [github.com\u002FVtrAha\u002Fbybit-trading-bot](https:\u002F\u002Fgithub.com\u002FVtrAha\u002Fbybit-trading-bot)\n\n**New to bots?** Start with the [Beginner guide: using this bot](#beginner-guide-using-this-bot) (step-by-step: install, keys, dry-run, testnet, then mainnet).\n\n---\n\n## Overview\n\nThis repository implements a **single-symbol, trend-following** trading loop for **Bybit linear USDT perpetuals**. The strategy combines **EMA crossover** for directional bias, **ADX** and **directional movement (+DI\u002F−DI)** for regime filtering, and **ATR** for stop distance, take-profit levels, and **risk-based position sizing**. Market data and execution are separated from indicator and strategy logic to support testnet, dry-run, and signed live trading on a predictable polling cadence.\n\n**This is research and execution software, not a product with guaranteed returns.** Past or hypothetical results do not predict live performance. Cryptocurrency derivatives are high risk; you are solely responsible for API keys, margin, compliance, and capital deployed.\n\n---\n\n## Beginner guide: using this bot\n\nIf you have never run a trading bot before, follow these steps in order. **Do not skip testnet and dry-run** until you know what the logs mean.\n\n### 0) Get the project on your computer (very first step)\n\n**Goal:** a folder on your machine that contains `package.json` and a `src\u002F` directory — that is the **project root** for every later command.\n\n**Option A — Git clone (recommended if the code is on GitHub, GitLab, or similar):**\n\n1. Install [Git](https:\u002F\u002Fgit-scm.com\u002Fdownloads) if you do not have it. Check with:\n\n   ```bash\n   git --version\n   ```\n\n2. In a folder where you keep projects, clone the repository (replace the URL with **your** repo or fork):\n\n   ```bash\n   git clone https:\u002F\u002Fgithub.com\u002FVtrAha\u002Fbybit-trading-bot.git\n   cd YOUR_REPO\n   ```\n\n   If you use **SSH** (with keys on your Git account):\n\n   ```bash\n   git clone git@github.com:VtrAha\u002Fbybit-trading-bot.git\n   cd YOUR_REPO\n   ```\n\n3. You should see `package.json` in the current directory:\n\n   ```bash\n   # Windows (PowerShell or cmd)\n   dir package.json\n\n   # macOS \u002F Linux\n   ls package.json\n   ```\n\n4. **Later, to pull updates** (new commits from the same remote), use:\n\n   ```bash\n   cd YOUR_REPO\n   git pull\n   ```\n\n   Then run `npm install` again in case `package.json` changed.\n\n**If the folder name has spaces** (e.g. `bybit trading bot`), always quote the path when you `cd`:\n\n```bash\ncd \"bybit trading bot\"\n```\n\n**Option B — You already have the folder (ZIP, USB, Cursor\u002FCopilot, or copy from a friend):**\n\n- Unzip or open that folder, then in a terminal **go into the folder** that directly contains `package.json` and `src\u002F`. That folder is your project root — same as after `git clone` + `cd`.\n\n**Option C — You opened this project in an IDE:**\n\n- Open the **integrated terminal** in the project root (the directory with `package.json`) so `npm` commands run in the right place.\n\n**Then continue to step 1 below.**\n\n### 1) What you need before you start\n\n| You need | Notes |\n|----------|--------|\n| A computer with **Node.js 20+** | [Download Node](https:\u002F\u002Fnodejs.org\u002F) (LTS is fine). In a terminal, run `node -v` — it should show `v20` or higher. |\n| The **project root** (from step 0) | The folder with `package.json` and `src\u002F`. All commands like `npm install` run **here**. |\n| **Git** (optional) | Only required if you use **Option A** in step 0. |\n| A **Bybit** account | You will create **API keys** on [Bybit](https:\u002F\u002Fwww.bybit.com\u002F) (main) or [Testnet](https:\u002F\u002Ftestnet.bybit.com\u002F) (fake money for learning). |\n| Patience to read the logs | The bot only prints text to the terminal. There is no built-in website or app UI. |\n\n**You do *not* need to know the math behind EMA\u002FADX\u002FATR on day one** — but you *do* need to understand **testnet vs mainnet** and **dry-run vs real orders** (see below).\n\n### 2) Four switches every beginner should understand\n\n| Switch | Set to | What it means in plain language |\n|--------|--------|---------------------------------|\n| `BYBIT_TESTNET` | `true` first | Talk to **Bybit’s practice server**; balances are not real mainnet money. |\n| `BYBIT_TESTNET` | `false` | Talk to **real Bybit** — real money and real risk. |\n| `DRY_RUN` | `true` | The bot may **read** prices and (with keys) your balance, but it **will not** place orders or set stop-loss\u002Ftake-profit. **Safest to learn signals.** |\n| `DRY_RUN` | `false` | The bot will **place real orders** (when keys are set and the strategy wants to trade). **Use only on testnet first, then small size on mainnet if you accept the risk.** |\n\n**Rule of thumb:** learn with **`DRY_RUN=true`**, then test orders with **`BYBIT_TESTNET=true`** and **`DRY_RUN=false`**, only then consider **mainnet** with **`BYBIT_TESTNET=false`**.\n\n### 4) Install dependencies and the `.env` file (one time)\n\n1. In the **project root** (where `package.json` is; see **0) Get the project on your computer** above), run:\n\n   ```bash\n   npm install\n   ```\n\n2. If that fails, check that you are in the correct directory and that Node 20+ is installed.\n\n3. Create your local settings file (do **not** share or upload this file):\n\n   ```bash\n   # Windows (Command Prompt)\n   copy .env.example .env\n\n   # macOS \u002F Linux\n   cp .env.example .env\n   ```\n\n4. Open `.env` in a text editor. You will come back to this file many times.\n\n### 5) Create Bybit API keys (beginner-friendly)\n\n1. **Use testnet first:** go to [Bybit testnet](https:\u002F\u002Ftestnet.bybit.com\u002F), create or log in, and go to the **API** section (wording may vary: “Create API key”).\n2. Create a key with **trading** permission for the account type you use (e.g. **unified** \u002F contract as required by the exchange). **Do not** enable **withdrawal** for a bot that only trades — that limits damage if a key leaks.\n3. Save the **key** and **secret** in a safe place. **You may only see the secret once.**\n4. Paste them into `.env` as `BYBIT_API_KEY` and `BYBIT_API_SECRET` (no quotes, no extra spaces, one key per line).\n5. In `.env`, set `BYBIT_TESTNET=true` so the bot uses the **testnet** API host. Keys from testnet and mainnet are **not** interchangeable.\n\nIf something says “unauthorized” or “invalid key,” the host (`BYBIT_TESTNET`) and the key origin (testnet vs main) **must match**.\n\n### 6) First run: signals only (no orders)\n\nGoal: see **`Starting`**, **`Instrument rules loaded`**, and **`Signal`** lines without spending anything.\n\n1. In `.env`, set:\n   - `DRY_RUN=true`\n   - `BYBIT_TESTNET=true`\n   - You may leave keys **empty** for a pure dry run of signals, **or** add keys to also see `equityUsd` \u002F `qty` in **dry-run** logs.\n2. Start the bot:\n\n   ```bash\n   npm start\n   ```\n\n3. **Good signs:** lines like `Signal` with `signal`, `close`, `adx`, `atr`. If you see **`Not enough data or indicators warming up`**, wait — the first bars may still be loading.\n4. **Stop the bot:** press **Ctrl+C** in the same terminal. It is normal to start and stop often while learning.\n\nIf the program exits immediately with an error, read the message; often it is a missing\u002Finvalid `FAST_EMA`\u002F`SLOW_EMA` pair or a typo in `.env`. See [Troubleshooting](#troubleshooting).\n\n### 7) Second run: same as live, but no orders (recommended)\n\n1. Keep `DRY_RUN=true` and `BYBIT_TESTNET=true`.\n2. With **testnet keys** in `.env`, the bot can **read** your testnet balance and show **`DRY_RUN (no orders)`** with a computed **`qty`**.\n3. Check that `qty` is not always `null` (if it is, your balance may be too small for the **minimum order size** or your **risk** settings are too low — see [Configuration](#configuration) and the sizing table in [User reference](#user-reference-data-costs-and-operations)).\n\nThis step proves your **API key works** and **sizing** is in a sensible range before any click-to-trade.\n\n### 8) Testnet *live* trading (real API orders, not real money on mainnet)\n\n1. In `.env` set: `DRY_RUN=false`, `BYBIT_TESTNET=true`, and your **testnet** keys.\n2. On **Bybit testnet**, set **leverage** and **position mode (one-way)** the way you want **before** relying on the bot. This repo does not set them for you.\n3. Set **small** values in `.env` while learning, e.g. low **`RISK_PER_TRADE`** and a tight **`MAX_ORDER_QTY`**, and an interval you understand (e.g. `INTERVAL=15` minutes).\n4. Run `npm start` and **watch the terminal**. You should only move on when you have seen an **open**, **brackets (SL\u002FTP)**, and understand how a **flip** or **error** looks in the log.\n5. **Stop** with Ctrl+C when done.\n\n**Testnet** can behave differently from mainnet (liquidity, fills). It is for **process** learning, not for proving profit.\n\n### 9) When you are ready for mainnet (read twice)\n\n1. You have completed **8)** and you understand what each log line means.\n2. Create **separate** **mainnet** API keys; never reuse testnet keys on main.\n3. Set `BYBIT_TESTNET=false` and point `.env` to **mainnet** keys.\n4. Use **only capital you can afford to lose**; start with the **smallest** `RISK_PER_TRADE` and `MAX_ORDER_QTY` you can.\n5. Keep your machine and `.env` **private**; read [Security](#security).\n\n### 10) On the Bybit app when the bot is running\n\nYou can always log in to **Bybit (web or app)** in parallel and check **positions**, **open orders**, and **wallet** balance. If the bot says it opened a position but you do not see it, wait a few seconds, refresh, and confirm the **symbol** and **account** (testnet vs main) match your `.env`.\n\n### 11) Common beginner mistakes\n\n| Mistake | How to avoid |\n|--------|---------------|\n| Mainnet key on testnet host (or the reverse) | `BYBIT_TESTNET` and key source must **match**. |\n| `DRY_RUN=false` on the first run | Start with `DRY_RUN=true` to learn. |\n| Expecting the bot to “set leverage” for you | Set leverage and one-way mode on **Bybit** before relying on size and risk. |\n| **Flat** signal with an open position | **Flat** does not auto-close; see [Strategy and position logic](#strategy-and-position-logic). |\n| Stopping the bot to “lock profit” | Open positions **remain on the exchange** after you stop the process. Close in the app or with rules you design. |\n\n### 12) Where to read next in this README\n\n| Goal | Section |\n|------|---------|\n| All environment variables | [Configuration](#configuration) |\n| What each line in the log means | [Logging](#logging) and [User reference](#user-reference-data-costs-and-operations) |\n| How signals and flips work | [Strategy and position logic](#strategy-and-position-logic) |\n| Command-line commands | [Quick start](#quick-start) |\n| Problems | [Troubleshooting](#troubleshooting) |\n\n---\n\n## At a glance\n\n| Topic | Value |\n|--------|--------|\n| **Package** | `bybit-trend-bot` (see `package.json`) |\n| **Runtime** | Node.js **20+** · ESM · native `fetch` |\n| **Exchange** | [Bybit V5](https:\u002F\u002Fbybit-exchange.github.io\u002Fdocs\u002Fv5\u002Fguide) |\n| **Market** | **USDT-margined linear perpetuals** (single `SYMBOL` per process) |\n| **Position mode** | **One-way** (`positionIdx: 0`); not hedge mode |\n| **Account** | **Unified** USDT balance parsing (typical `UNIFIED` wallet flow) |\n| **Entries \u002F exits** | **Market** orders; SL\u002FTP via **`\u002Fv5\u002Fposition\u002Ftrading-stop`** |\n| **Data feed** | **REST** polling of **closed** klines (no built-in WebSocket) |\n| **Min loop interval** | **5000 ms** (`POLL_MS` floor in `config.ts`) |\n| **Default risk** | `RISK_PER_TRADE=0.005` → **0.5%** of USDT equity per trade’s *stop-distance* risk budget (before `min`\u002F`max`\u002F`MAX_ORDER_QTY` caps) |\n| **Default ATR R:R (distance)** | `ATR_TP_MULT \u002F ATR_STOP_MULT` → **2:1** (e.g. 3.5 \u002F 1.75) — not guaranteed realized R |\n\n---\n\n## User reference: data, costs, and operations\n\nThis section answers common **“what will I see?”** and **“what should I set on Bybit?”** questions.\n\n### Kline intervals (from `.env.example`)\n\nSet `INTERVAL` to one of the values Bybit supports for your symbol, for example: **1, 3, 5, 15, 30, 60, 120, 240, 360, 720, D, W, M** (confirm in [Bybit kline docs](https:\u002F\u002Fbybit-exchange.github.io\u002Fdocs\u002Fv5\u002Fmarket\u002Fkline) if your contract differs). Shorter intervals → more often **new closed bars** and more frequent signal checks; they do **not** by themselves make the strategy more profitable.\n\n### History length and “warm-up”\n\nEach tick requests enough closed klines to compute **slow EMA** and **ADX** (and ATR) reliably. The code uses:\n\n`klineLimit = max(250, SLOW_EMA + ADX_PERIOD × 4 + 50)` bars (see `src\u002Fbot\u002Frunner.ts`).\n\nIf the API returns fewer bars or indicators are not ready, you may see **`Not enough data or indicators warming up; skipping tick.`** until history is sufficient.\n\n### Dry-run vs live (what is real)\n\n| Mode | Keys | Balance \u002F position reads | **Orders, SL, TP** |\n|------|------|----------------------------|---------------------|\n| `DRY_RUN=true` | Optional | If keys set: may read **equity** to show hypothetical `qty` | **None** (no `order\u002Fcreate`, no `trading-stop`) |\n| `DRY_RUN=false` | **Required** | **Equity** + **open position** each tick when trading | **Yes** (market + trading-stop) |\n\n`BYBIT_TESTNET=true` only changes the **host** to testnet; it does not replace careful sizing and key hygiene.\n\n### Approximate API traffic (planning, not a SLA)\n\n| Phase | Calls (typical) |\n|--------|------------------|\n| **Startup** | Server time sync; **instrument** rules; then loop |\n| **Each tick** | Klines (bulk GET); in **live** also position + balance when trading logic runs; **dry-run** with keys may sync time + equity for logging |\n| **Open position** | Market order; **position** polls (short retry loop) for **entry**; **trading-stop** for SL\u002FTP |\n| **Flip** | **Reduce-only** close market → brief sleep → new market; position poll; **trading-stop** again |\n\nBybit [rate limits](https:\u002F\u002Fbybit-exchange.github.io\u002Fdocs\u002Fv5\u002Frate-limit) apply; one symbol and multi-second polling are usually light, but testnet and mainnet limits differ.\n\n### What each run cares about (trader checklist)\n\n| Item | In this bot? | You handle on Bybit \u002F elsewhere |\n|------|----------------|----------------------------------|\n| **Symbol** | `SYMBOL` | Pick a listed **linear USDT** perpetual. |\n| **Leverage** | **No** | Set in Bybit (affects **liquidation** and PnL volatility). |\n| **Cross \u002F isolated, position mode** | **No** | Set in Bybit UI \u002F API. |\n| **Taker \u002F maker fees** | **Not modeled** (entries are **market** = usually **taker**). | See your [fee tier](https:\u002F\u002Fwww.bybit.com\u002Fen-US\u002Fhelp-center). |\n| **Funding** (perp) | **Not modeled** | Long\u002Fshorts pay or receive on schedule; can dominate short holds. |\n| **Liquidation** | **Not modeled** | Depends on margin, leverage, and mark price. |\n| **Tax \u002F reporting** | **No** | Export from exchange or your tools. |\n\n### Interpretation of logged signal fields\n\n| Field | Meaning | Why users watch it |\n|--------|---------|---------------------|\n| `signal` | `long` \u002F `short` \u002F `flat` | The strategy’s current bar decision. |\n| `close` | Last **closed** bar close | Price level used with ATR and sizing context. |\n| `adx` | ADX (period `ADX_PERIOD`) | **Trend strength**; compared to `ADX_MIN`. |\n| `atr` | ATR (period `ATR_PERIOD`) | **Volatility**; drives stop\u002FTP **distance** and `qty` via `stopDist`. |\n| `target` (dry-run) | `Buy` \u002F `Sell` \u002F `null` | What a live bot would *try* to align with (subject to `qty` and position rules). |\n| `qty` | Contract size (base) | The size the bot **would** or **did** use after rules and caps. |\n| `equityUsd` | USDT wallet equity used for risk | Drives `riskUsd = equity × RISK_PER_TRADE`. |\n\n### Sizing at a glance (arithmetic only)\n\n`RISK_PER_TRADE` is the fraction of **USDT equity** applied to the **notional** stop-distance budget `riskUsd`. The bot then sets `qty ≈ riskUsd \u002F stopDist` with `stopDist = ATR × ATR_STOP_MULT` (and exchange min\u002Fmax\u002Fstep). This is **not** a promise of how much you will lose in USDT in a real trade (slippage, fees, partial fills, and **liquidation** can differ).\n\n| Wallet equity (USDT) | `RISK_PER_TRADE` | `riskUsd` (used in formula) |\n|----------------------|------------------|-----------------------------|\n| 1,000 | 0.005 (0.5%) | **5** |\n| 5,000 | 0.005 | **25** |\n| 20,000 | 0.005 | **100** |\n| 1,000 | 0.01 (1%) | **10** |\n\nIf `qty` is `null` in logs, often **`riskUsd` is too small** for **minQty** at current price\u002FATR, or **`MAX_ORDER_QTY`** is very tight.\n\n---\n\n## Table of contents\n\n1. [Beginner guide: using this bot](#beginner-guide-using-this-bot)\n2. [At a glance](#at-a-glance)\n3. [User reference: data, costs, and operations](#user-reference-data-costs-and-operations)\n4. [Features](#features)\n5. [What the bot does and does not do](#what-the-bot-does-and-does-not-do)\n6. [Architecture](#architecture)\n7. [Strategy and position logic](#strategy-and-position-logic)\n8. [Risk and position sizing](#risk-and-position-sizing)\n9. [Bybit API surface](#bybit-api-surface)\n10. [Requirements](#requirements)\n11. [Quick start](#quick-start)\n12. [Configuration](#configuration)\n13. [Logging](#logging)\n14. [Performance and validation](#performance-and-validation)\n15. [Tuning and research notes](#tuning-and-research-notes)\n16. [Limitations and known gaps](#limitations-and-known-gaps)\n17. [Troubleshooting](#troubleshooting)\n18. [Security](#security)\n19. [Glossary and links](#glossary-and-links)\n20. [FAQ](#faq)\n21. [License](#license)\n\n---\n\n## Features\n\n- **Closed-bar signals** on configurable kline interval; long \u002F short \u002F flat from EMA + ADX + DI rules.\n- **Live mode** (when `DRY_RUN=false`): one-way linear positions (`positionIdx: 0`), **market** entries and flips, **trading-stop** for SL\u002FTP, **reduce-only** exit before reverse entry when the signal flips.\n- **Sizing** from USDT equity, ATR-based stop distance, and exchange `min` \u002F `max` \u002F `step` from instrument metadata, with a hard `MAX_ORDER_QTY` cap.\n- **Clock sync** via `GET \u002Fv5\u002Fmarket\u002Ftime` before signed requests.\n- **Structured logging** to stdout (JSON lines) for monitoring and log aggregation.\n- **Dry-run and testnet** for staged validation before mainnet use.\n\n---\n\n## What the bot does and does not do\n\n### In scope\n\n| Area | Behavior |\n|------|----------|\n| Data | Polls **closed** klines for `SYMBOL` and `INTERVAL`, recomputes indicators, emits **long \u002F short \u002F flat** each iteration. |\n| Live trading | With `DRY_RUN=false`, opens and manages **one** linear position per symbol; **market** orders; attaches SL\u002FTP via **trading-stop**; **flips** on signal reversal (close then new side). |\n| Sizing | Derives quantity from equity and ATR stop distance, rounded to **qtyStep** and bounded by `minQty` \u002F `maxQty` \u002F `MAX_ORDER_QTY`. |\n| Time | Synchronizes server time offset for request signing. |\n\n### Out of scope\n\n| Item | Note |\n|------|------|\n| Performance guarantees | No implied Sharpe, win rate, or drawdown limits. |\n| Other products | **No** spot, options, inverse contracts, or multi-symbol portfolio logic in a single process. |\n| Account setup | **No** automatic leverage, margin mode, or position mode; configure in Bybit. |\n| Latency | **REST polling** only; not designed for sub-second or tick scalping. |\n| Research suite | **No** in-repo backtest or walk-forward optimizer; export logs and analyze externally if needed. |\n\n---\n\n## Architecture\n\n```mermaid\nflowchart LR\n  subgraph inputs [Inputs]\n    ENV[\".env \u002F process.env\"]\n    API[\"Bybit REST V5\"]\n  end\n  subgraph core [Application]\n    CFG[\"config.ts\"]\n    MKT[\"market: klines + instrument\"]\n    IND[\"indicators: EMA ATR ADX\"]\n    STR[\"strategy: trendEmaAdx\"]\n    EXE[\"execution: balance positions orders\"]\n    RUN[\"bot\u002Frunner.ts loop\"]\n  end\n  ENV --> CFG\n  CFG --> RUN\n  API --> MKT\n  API --> EXE\n  MKT --> IND --> STR --> RUN\n  RUN --> EXE --> API\n```\n\n| Layer | Role | Main files |\n|--------|------|------------|\n| Entry | Process bootstrap | `src\u002Findex.ts` |\n| Config | Environment parsing and validation | `src\u002Fconfig.ts` |\n| Connectivity | HMAC-SHA256, HTTP GET\u002FPOST | `src\u002Fbybit\u002FrestClient.ts`, `src\u002Fbybit\u002Fsign.ts` |\n| Market data | Klines, contract rules | `src\u002Fmarket\u002Fklines.ts`, `src\u002Fmarket\u002Finstrument.ts` |\n| Indicators | EMA, ATR, ADX, +DI, −DI | `src\u002Findicators\u002F*.ts` |\n| Strategy | Signal from OHLC series | `src\u002Fstrategy\u002FtrendEmaAdx.ts` |\n| Execution | Balance, position, orders, brackets | `src\u002Fexecution\u002Faccount.ts`, `src\u002Fexecution\u002Forders.ts` |\n| Orchestration | Polling loop, dry-run path | `src\u002Fbot\u002Frunner.ts` |\n\n---\n\n## Strategy and position logic\n\n### Signal (last fully closed bar)\n\nLet **fast** and **slow** be EMA(close) with periods `FAST_EMA` and `SLOW_EMA`. **ADX**, **+DI**, and **−DI** use period `ADX_PERIOD` (Wilder-style smoothing as implemented in code).\n\n| Signal | Conditions (on the latest bar) |\n|--------|--------------------------------|\n| **Long** | fast > slow, ADX ≥ `ADX_MIN`, +DI > −DI |\n| **Short** | fast \u003C slow, ADX ≥ `ADX_MIN`, −DI > +DI |\n| **Flat** | All other cases |\n\n**Flat** means no new entry aligned with a trend; it does **not** by itself close an open position in live mode.\n\n### Live behavior (`DRY_RUN=false`)\n\n| Open position | Target signal | Action |\n|---------------|---------------|--------|\n| None | Long or Short | Market open; then **trading-stop** (SL\u002FTP) |\n| Long | Short | Reduce-only market sell to flat; then market new short; new brackets |\n| Short | Long | Reduce-only market buy to flat; then market new long; new brackets |\n| Any | Flat | **No** automated exit (position remains; SL\u002FTP or manual) |\n| Long \u002F Short | Unchanged side | **No** re-entry or bracket refresh in this version |\n\n**Post-entry:** the runner reads **`\u002Fv5\u002Fposition\u002Flist`** for average entry, then sets **`\u002Fv5\u002Fposition\u002Ftrading-stop`** with prices rounded to **tickSize**.\n\n---\n\n## Risk and position sizing\n\n**Stop distance (price):** `stopDist = ATR × ATR_STOP_MULT`\n\n**Notional risk budget (USDT):** `riskUsd = equityUsd × RISK_PER_TRADE`\n\n**Base quantity (before exchange constraints):** `qty ≈ riskUsd \u002F stopDist`\n\nImplementation: floor to **qtyStep**, enforce **minQty** \u002F **maxQty**, and cap with **MAX_ORDER_QTY** (`computeOrderQty` in `src\u002Fexecution\u002Forders.ts`).\n\n**Bracket levels (long; short is symmetric):**\n\n- Stop loss: `entry − ATR × ATR_STOP_MULT`\n- Take profit: `entry + ATR × ATR_TP_MULT`\n\nDefault multipliers (e.g. 3.5 \u002F 1.75) set a **distance** reward-to-risk ratio; realized outcomes depend on fees, slippage, and fills.\n\n---\n\n## Bybit API surface\n\n**Public (unsigned)**\n\n| Method | Path | Purpose |\n|--------|------|---------|\n| GET | `\u002Fv5\u002Fmarket\u002Ftime` | Server time; clock offset |\n| GET | `\u002Fv5\u002Fmarket\u002Fkline` | OHLC history |\n| GET | `\u002Fv5\u002Fmarket\u002Finstruments-info` | `qtyStep`, min\u002Fmax qty, `tickSize` |\n\n**Private (HMAC-SHA256, `X-BAPI-*` headers)**\n\n| Method | Path | Purpose |\n|--------|------|---------|\n| GET | `\u002Fv5\u002Faccount\u002Fwallet-balance` | USDT equity (`accountType=UNIFIED`) |\n| GET | `\u002Fv5\u002Fposition\u002Flist` | Open linear position for `SYMBOL` |\n| POST | `\u002Fv5\u002Forder\u002Fcreate` | Market entry\u002Fexit (`positionIdx: 0`) |\n| POST | `\u002Fv5\u002Fposition\u002Ftrading-stop` | Stop-loss and take-profit |\n\nSigning: **GET** — sorted query string; **POST** — exact JSON body string. `recv_window` = **5000** ms. See the [Bybit V5 documentation](https:\u002F\u002Fbybit-exchange.github.io\u002Fdocs\u002Fv5\u002Fguide) for current contract specifications.\n\n---\n\n## Requirements\n\n- **Node.js** 20 or newer (ES modules, `fetch`)\n- **Bybit** API key and secret (use **testnet** keys for development)\n\n---\n\n## Quick start\n\n**If you have not downloaded the project yet:** use [Beginner guide — step 0](#beginner-guide-using-this-bot) (`git clone …`, then `cd` into the folder). The examples below assume you are already in the **project root** (folder that contains `package.json`).\n\n```bash\n# Example: enter the repo (use your actual path or clone folder name)\ncd \"bybit trading bot\"\n\nnpm install\n```\n\nCreate environment from the example file:\n\n```bash\n# Windows (Command Prompt)\ncopy .env.example .env\n\n# macOS \u002F Linux\ncp .env.example .env\n```\n\nEdit `.env`. Do not commit real secrets; `.env` is intended to stay local or in a secure secret store.\n\n| Command | Description |\n|---------|-------------|\n| `npm start` | Run with `tsx` (typical for development) |\n| `npm run dev` | Same with `node --watch` auto-reload |\n| `npm run build` | Compile to `dist\u002F` |\n| `node dist\u002Findex.js` | Run compiled build |\n\nThe process runs until stopped (e.g. Ctrl+C or a process supervisor). In production, run under a restart policy, log rotation, and monitoring appropriate to your infrastructure.\n\n### Recommended validation sequence\n\n1. **`BYBIT_TESTNET=true`**, **`DRY_RUN=true`** (keys optional) — confirm startup, instrument load, and **Signal** logs.\n2. Add keys, keep **`DRY_RUN=true`** — confirm wallet read and **qty** when risk settings allow a valid size.\n3. **`DRY_RUN=false`** on **testnet** with small **`RISK_PER_TRADE`** and **`MAX_ORDER_QTY`** — exercise open, SL\u002FTP, and optional flip.\n4. **Mainnet** only after you accept remaining operational and market risk, with limits you can afford to lose.\n\n---\n\n## Configuration\n\nAll settings are read at startup from the environment. Source of truth: `src\u002Fconfig.ts`.\n\n| Variable | Example | Description |\n|----------|---------|-------------|\n| `BYBIT_API_KEY` | (set locally) | API key |\n| `BYBIT_API_SECRET` | (set locally) | API secret |\n| `BYBIT_TESTNET` | `true` | Testnet vs mainnet host |\n| `DRY_RUN` | `true` | If `true`, no `order\u002Fcreate` or `trading-stop` calls |\n| `SYMBOL` | `BTCUSDT` | Linear perpetual symbol |\n| `INTERVAL` | `15` | Kline interval (Bybit-supported values) |\n| `FAST_EMA` | `12` | Fast EMA period (strictly less than `SLOW_EMA`) |\n| `SLOW_EMA` | `26` | Slow EMA |\n| `ADX_PERIOD` | `14` | ADX \u002F DI period (≥ 2 after validation) |\n| `ADX_MIN` | `22` | Minimum ADX to allow long\u002Fshort |\n| `ATR_PERIOD` | `14` | ATR period |\n| `ATR_STOP_MULT` | `1.75` | Stop distance in ATR multiples |\n| `ATR_TP_MULT` | `3.5` | Take-profit distance in ATR multiples |\n| `RISK_PER_TRADE` | `0.005` | Fraction of USDT equity for stop-distance risk |\n| `MAX_ORDER_QTY` | `0.001` | Hard cap on order size (base), after sizing |\n| `POLL_MS` | `60000` | Sleep between loop iterations; **minimum 5000** ms enforced in code |\n\nBooleans accept `1`, `true`, `yes`, `on` (case-insensitive).\n\n---\n\n## Logging\n\nAll messages are on **stdout** in the form `[ISO-8601] \u003Clabel> { ...json }` so you can **pipe to a file**, **ship to ELK \u002F Grafana Loki \u002F CloudWatch**, or **grep** by label.\n\n| Label \u002F line | When | Typical fields |\n|---------------|------|-----------------|\n| `Starting Bybit trend bot` | Once at start | `symbol`, `interval`, `dryRun`, `testnet` |\n| `Instrument rules loaded` | After metadata fetch | `qtyStep`, `minQty`, `maxQty`, `tickSize` |\n| `Not enough data or indicators warming up` | Early or short history | (none) — wait for more klines |\n| `Signal` | Each tick with a valid snapshot | `signal`, `close`, `adx`, `atr` |\n| `DRY_RUN (no orders)` | Dry-run with optional sizing | `target`, `qty`, `equityUsd` (or `null` if no equity) |\n| `DRY_RUN: account read failed` | Dry-run, keys but balance error | `error` message |\n| `Qty below exchange minimum` | Live, sizing could not place min size | (text) — adjust risk or `MAX_ORDER_QTY` \u002F equity |\n| `Opened position` | New entry | `side`, `qty`, `entry` |\n| `Flipped position` | Reversal | `to`, `qty` |\n| `Tick error` | Caught exception in loop | `error` — process **continues** on next bar |\n\n| Field | Meaning |\n|--------|---------|\n| `tickSize` | Price increment for **orders and brackets** (rounding). |\n| `qtyStep` \u002F `minQty` \u002F `maxQty` | **Contract** size step and floor\u002Fceiling from the exchange. |\n| `entry` | **Average** entry from position list after open (or fallback to last close in edge cases). |\n\n---\n\n## Performance and validation\n\n**There is no backtest, paper-PnL, or live performance table in this repository.** The bot does not ship historical research results; any external performance numbers are not implied or endorsed here.\n\n- Validate configuration and **behavior** on **Bybit testnet** before mainnet.\n- For research, log signals and (if you enable it) your own post-trade analysis pipeline.\n- Be explicit about **fees**, **funding**, and **liquidation** risk on the exchange; the bot does not model all of these.\n\n---\n\n## Tuning and research notes\n\n- **More signals (noisier):** lower `ADX_MIN` and\u002For a shorter `INTERVAL` (more whipsaw risk).\n- **Fewer, stricter entries:** raise `ADX_MIN` or adjust EMA periods.\n- **Smaller per-trade risk:** lower `RISK_PER_TRADE` (may fall below `minQty`).\n- **Smaller size regardless of model:** lower `MAX_ORDER_QTY`.\n- **Different R-multiple shape:** change `ATR_STOP_MULT` and `ATR_TP_MULT` (also changes implicit size via `stopDist`).\n\nReconcile all changes with your exchange account settings and live fee tier.\n\n---\n\n## Limitations and known gaps\n\n- **Bar-close latency:** signals use closed candles; not for ultra-low-latency strategies.\n- **Fills and slippage:** market orders are assumed executable; bracket levels use ATR and observed entry.\n- **No in-position bracket refresh** for the same direction (no trailing stop in this version).\n- **Single strategy instance** per process; no cross-symbol hedging or portfolio optimizer.\n- **Wallet parsing** targets unified USDT; unusual account types may need code adjustments.\n\n---\n\n## Troubleshooting\n\n| Symptom | Likely cause | Suggested check |\n|--------|----------------|-----------------|\n| Startup error about API keys | `DRY_RUN=false` without keys | Set keys or `DRY_RUN=true` |\n| `FAST_EMA must be less than SLOW_EMA` | Invalid periods | Fix env |\n| Auth \u002F timestamp errors | Clock skew, wrong secret, testnet vs mainnet mismatch | OS time NTP; key environment matches host |\n| `qty` null in dry-run | Missing keys or balance read failed | Keys and API permissions for read |\n| `qty` null live | Risk too small for `minQty` | Increase equity, `RISK_PER_TRADE`, or `MAX_ORDER_QTY` within your limits |\n| Order rejected | Mode, symbol, or precision | Bybit UI + `instruments-info` response |\n| Signal always flat | Filters too strict | Lower `ADX_MIN` or review DI logic in strategy source |\n\n---\n\n## Security\n\n- Store credentials in **`.env`**, a secrets manager, or your orchestrator’s secret injection — never in source control.\n- Use the **minimum** API permissions required (e.g. trade without withdrawal where policy allows).\n- **IP allowlisting** for the key when you have a stable outbound IP.\n- Harden the host and restrict filesystem access to the working directory and env files.\n- **Rotate** keys if they may have been exposed; keep testnet and mainnet keys separate.\n\n---\n\n## Glossary and links\n\n### Terms\n\n| Term | Short meaning |\n|------|----------------|\n| **EMA** | Exponential moving average of **close**; `FAST_EMA` vs `SLOW_EMA` define trend *direction* on the bar. |\n| **ADX** | Average Directional Index — **trend strength** (not direction); compared to `ADX_MIN`. |\n| **+DI \u002F −DI** | Directional Indicators; which side of the market is *stronger* for the lookback. |\n| **ATR** | Average True Range — **volatility** in **price** units; used for stop\u002FTP *distance* and for sizing via `stopDist`. |\n| **Flat** | No long\u002Fshort *entry* signal this bar; **does not** mean “force exit.” |\n| **One-way** | A single net position per symbol (long *or* short, not long+short slots). |\n| **Unified (wallet)** | Bybit’s unified account type used for **USDT** equity reads in this code path. |\n| **Reduce-only** | Order flag that can only *decrease* position size (used when flipping). |\n| **R-multiple (distance)** | Take-profit distance divided by stop distance (here via ATR multipliers), **before** fees and slippage. |\n\n### Official and learning links\n\n| Resource | URL |\n|----------|-----|\n| Bybit V5 API overview | [bybit-exchange.github.io\u002Fdocs\u002Fv5\u002Fguide](https:\u002F\u002Fbybit-exchange.github.io\u002Fdocs\u002Fv5\u002Fguide) |\n| Kline (candles) | [V5 market kline](https:\u002F\u002Fbybit-exchange.github.io\u002Fdocs\u002Fv5\u002Fmarket\u002Fkline) |\n| Linear \u002F instrument info | [instruments-info](https:\u002F\u002Fbybit-exchange.github.io\u002Fdocs\u002Fv5\u002Fmarket\u002Finstrument) |\n| Positions, orders, trading-stop | [Contract trade](https:\u002F\u002Fbybit-exchange.github.io\u002Fdocs\u002Fv5\u002Fintro#contract-trading) (navigate to order\u002Fposition endpoints) |\n| Rate limits | [Rate limit](https:\u002F\u002Fbybit-exchange.github.io\u002Fdocs\u002Fv5\u002Frate-limit) |\n| Testnet (create keys \u002F use test host) | [Bybit testnet](https:\u002F\u002Ftestnet.bybit.com\u002F) and API base `https:\u002F\u002Fapi-testnet.bybit.com` (see code \u002F `.env.example`) |\n| Service updates | [Bybit announcements](https:\u002F\u002Fwww.bybit.com\u002Fen-US\u002Fannouncement\u002F) (maintenance, API changes) |\n\n---\n\n## FAQ\n\n**Why EMA and ADX?**  \nA minimal, well-documented trend template: EMAs summarize direction; ADX and DI summarize whether a directional regime dominates noise on the current period.\n\n**Does “flat” close the position?**  \nNo. Exits in automated flow come from an **opposite** signal (flip), **SL\u002FTP**, or **manual** close.\n\n**Multiple symbols?**  \nNot in one process. Multiple instances multiply API usage and require separate risk controls and capital allocation per symbol.\n\n**Minimum poll interval?**  \n`POLL_MS` is **clamped to at least 5000** ms in `config.ts`.\n\n**Is sizing in USDT or coins?**  \n**Risk** is budgeted in **USDT** from **equity**; **order size** is the **base asset quantity** of the contract (e.g. **BTC** for `BTCUSDT`), after **qtyStep** and caps.\n\n**Will I always get a fill at my SL\u002FTP?**  \nThe bot sets **trading-stop** at calculated prices. **Guaranteed** execution at exact levels depends on the exchange, liquidity, and market conditions; **gaps** and **liquidation** are outside this code.\n\n**Grid, Martingale, or DCA?**  \n**No.** This version is a **single** position, **one** direction at a time, **no** add-to-loser pyramiding in code.\n\n**Can I use spot or copy-trading?**  \n**No** spot and **no** copy-trading integration here — **linear perps** only, via the REST paths listed in this README.\n\n**Where do I set leverage?**  \nOnly in **Bybit** (UI or API). Wrong leverage is a common reason for **unexpected liquidation** or PnL swing size.\n\n**Does the bot pay Bybit for me?**  \n**No** — you need an account, **API keys**, and **funds**; **fees and funding** are debited by the exchange per their rules.\n\n---\n\n## License\n\nProvided **as-is**, without warranty. For redistribution, add a `LICENSE` file appropriate to your legal requirements.\n","该项目是一个基于Bybit V5 API的自动化趋势跟随交易机器人，专注于USDT线性永续合约。其核心功能包括使用EMA交叉确定方向偏移、ADX及方向移动指标（+DI\u002F-DI）进行市场状态过滤，并结合ATR设置止损距离和止盈水平，同时支持基于风险的位置大小调整。技术上采用TypeScript编写，分离了市场数据获取与策略逻辑，便于在测试网、模拟运行以及真实交易环境中部署。适用于对加密货币衍生品有一定了解并希望通过算法交易提高效率的投资者。","2026-06-11 04:01:21","CREATED_QUERY"]