mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-09 16:28:06 +00:00
Compare commits
132
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
958de8f2b8 | ||
|
|
59ccd1fea8 | ||
|
|
a9fa7f2e19 | ||
|
|
7529de334f | ||
|
|
1bfe4d58c0 | ||
|
|
4ce2b07e21 | ||
|
|
6bb0994cae | ||
|
|
486b911bed | ||
|
|
f48371ccd8 | ||
|
|
9ca35584b5 | ||
|
|
01e5ab9997 | ||
|
|
28bc1d5210 | ||
|
|
870fe2b8d7 | ||
|
|
e7f6341961 | ||
|
|
1baee3a207 | ||
|
|
e94cf1bda2 | ||
|
|
274e2f3d75 | ||
|
|
a91e87534b | ||
|
|
d0d1afa0ea | ||
|
|
0f70c6b6aa | ||
|
|
078b201d15 | ||
|
|
1832c4c13e | ||
|
|
73a88a8a0f | ||
|
|
265cb6df50 | ||
|
|
5bba8169b5 | ||
|
|
295c6a47b7 | ||
|
|
e7dedbba2c | ||
|
|
6196912cf4 | ||
|
|
f1ebfacaa2 | ||
|
|
ea5da20311 | ||
|
|
fabc8af679 | ||
|
|
189fed0f6e | ||
|
|
09ff4dc296 | ||
|
|
2627fa9db8 | ||
|
|
8b20e60753 | ||
|
|
f991215d86 | ||
|
|
6a3b842a3b | ||
|
|
b36c1dce0f | ||
|
|
dbe8df6934 | ||
|
|
400c7ad4b7 | ||
|
|
e96cbb6ff4 | ||
|
|
aa3d5e8c6c | ||
|
|
96cf4ea47b | ||
|
|
45061ed748 | ||
|
|
da3dacb549 | ||
|
|
51e0ce2bea | ||
|
|
8c13d20cb7 | ||
|
|
3d959ae579 | ||
|
|
4240cc6901 | ||
|
|
8d0e9578c4 | ||
|
|
95f07145a9 | ||
|
|
e3411c6791 | ||
|
|
640f1bfb4e | ||
|
|
f973fe433d | ||
|
|
730a75df93 | ||
|
|
de8a793bd5 | ||
|
|
bb072b2d1d | ||
|
|
95426e222b | ||
|
|
4365d73562 | ||
|
|
6067f759a0 | ||
|
|
b31b98f816 | ||
|
|
801543488e | ||
|
|
20c73eba21 | ||
|
|
ed49dca2a6 | ||
|
|
ca559d0f82 | ||
|
|
fef7604490 | ||
|
|
e524c225f2 | ||
|
|
39a97110dd | ||
|
|
8ad19b007b | ||
|
|
2e2990369b | ||
|
|
24f93fa8a7 | ||
|
|
d778e20c23 | ||
|
|
58f5de4a48 | ||
|
|
ad46635e20 | ||
|
|
6dde78f799 | ||
|
|
6b70aa0936 | ||
|
|
66e1b3e6f5 | ||
|
|
c6f279c51b | ||
|
|
9bad7f180e | ||
|
|
88292c9c49 | ||
|
|
5878a3dc0b | ||
|
|
ccb23ecf44 | ||
|
|
b6a6515677 | ||
|
|
5e65c7025d | ||
|
|
d7a95ceb36 | ||
|
|
1bd3e7edc6 | ||
|
|
28ba0613ca | ||
|
|
1dce3dfcfb | ||
|
|
d829e451cd | ||
|
|
0d2aa84b84 | ||
|
|
d2867ba98c | ||
|
|
e28240e799 | ||
|
|
cfd5f4309d | ||
|
|
ccc3a2bf89 | ||
|
|
f8a87f4557 | ||
|
|
263cbd7c21 | ||
|
|
d328758074 | ||
|
|
971a2a6adb | ||
|
|
fcb83bd16b | ||
|
|
d5c4b04746 | ||
|
|
65aaf26879 | ||
|
|
7635b7d467 | ||
|
|
f565c399e3 | ||
|
|
b43eaa0c64 | ||
|
|
9a419c36ac | ||
|
|
0e5026aaa1 | ||
|
|
6273f3b94a | ||
|
|
e478fb4330 | ||
|
|
599b4ec3f3 | ||
|
|
42061017f6 | ||
|
|
b7cd6eed99 | ||
|
|
abb56b917a | ||
|
|
9124c06315 | ||
|
|
56bcaddb22 | ||
|
|
4c4abb3b09 | ||
|
|
c8b0ab1c8e | ||
|
|
e83bdfccf9 | ||
|
|
d6533733c9 | ||
|
|
5c1283fac2 | ||
|
|
0e4ae67573 | ||
|
|
6ab0759d3d | ||
|
|
25ab1ababf | ||
|
|
e60b32a7f8 | ||
|
|
46c0551133 | ||
|
|
7e2d312fe3 | ||
|
|
e8dd36f540 | ||
|
|
47d44c7cb5 | ||
|
|
048dfb1698 | ||
|
|
fc1a74174c | ||
|
|
b45539b7c2 | ||
|
|
a998b3d3a1 | ||
|
|
d392f54f21 |
+87
-7
@@ -1,5 +1,5 @@
|
|||||||
# Exchange selection
|
# Exchange selection
|
||||||
EXCHANGE=aster # Pick aster (default) or grvt
|
EXCHANGE=aster # Pick aster (default) or grvt/lighter/backpack/paradex
|
||||||
|
|
||||||
# Aster API credentials
|
# Aster API credentials
|
||||||
ASTER_API_KEY=
|
ASTER_API_KEY=
|
||||||
@@ -10,11 +10,14 @@ TRADE_SYMBOL=BTCUSDT # Trading pair symbol
|
|||||||
TRADE_AMOUNT=0.001 # Base order quantity (base asset, e.g. BTC)
|
TRADE_AMOUNT=0.001 # Base order quantity (base asset, e.g. BTC)
|
||||||
|
|
||||||
# Risk management (USD amounts unless noted)
|
# Risk management (USD amounts unless noted)
|
||||||
LOSS_LIMIT=0.03 # Max loss per trade in USDT before forced close
|
LOSS_LIMIT=0.04 # Max loss per trade in USDT before forced close
|
||||||
TRAILING_PROFIT=0.2 # Trailing stop activation profit (USDT)
|
TRAILING_PROFIT=0.2 # Trailing stop activation profit (USDT)
|
||||||
TRAILING_CALLBACK_RATE=0.2 # Trailing callback percent (e.g. 0.2 => 0.2%)
|
TRAILING_CALLBACK_RATE=0.2 # Trailing callback percent (e.g. 0.2 => 0.2%)
|
||||||
PROFIT_LOCK_TRIGGER_USD=0.1 # Start moving base stop once unrealized PnL > this (USDT)
|
PROFIT_LOCK_TRIGGER_USD=0.08 # Start moving base stop once unrealized PnL > this (USDT)
|
||||||
PROFIT_LOCK_OFFSET_USD=0.05 # Base stop offset from entry after trigger (USDT)
|
PROFIT_LOCK_OFFSET_USD=0.04 # Base stop offset from entry after trigger (USDT)
|
||||||
|
BOLLINGER_LENGTH=20 # SMA window (minutes) used for Bollinger bandwidth
|
||||||
|
BOLLINGER_STD_MULTIPLIER=2 # Standard deviation multiplier for Bollinger bands
|
||||||
|
MIN_BOLLINGER_BANDWIDTH=0.001 # Require bandwidth >= this ratio before new entries
|
||||||
|
|
||||||
# Precision (per-symbol exchange filters)
|
# Precision (per-symbol exchange filters)
|
||||||
PRICE_TICK=0.1 # Price tick size (e.g. BTCUSDT uses 0.1)
|
PRICE_TICK=0.1 # Price tick size (e.g. BTCUSDT uses 0.1)
|
||||||
@@ -27,14 +30,29 @@ KLINE_INTERVAL=1m # Kline interval (e.g., 1m/3m/5m)
|
|||||||
MAX_CLOSE_SLIPPAGE_PCT=0.05 # Max allowed deviation vs mark when closing (0.05 => 5%)
|
MAX_CLOSE_SLIPPAGE_PCT=0.05 # Max allowed deviation vs mark when closing (0.05 => 5%)
|
||||||
|
|
||||||
# Maker-only settings
|
# Maker-only settings
|
||||||
MAKER_LOSS_LIMIT=0.03 # Maker loss cap (USDT). Defaults to LOSS_LIMIT if unset
|
MAKER_LOSS_LIMIT=0.05 # Maker loss cap (USDT). Defaults to LOSS_LIMIT if unset
|
||||||
MAKER_PRICE_CHASE=0.3 # Price chase threshold (USDT)
|
|
||||||
MAKER_BID_OFFSET=0 # Bid quote offset from top bid (USDT)
|
MAKER_BID_OFFSET=0 # Bid quote offset from top bid (USDT)
|
||||||
MAKER_ASK_OFFSET=0 # Ask quote offset from top ask (USDT)
|
MAKER_ASK_OFFSET=0 # Ask quote offset from top ask (USDT)
|
||||||
MAKER_REFRESH_INTERVAL_MS=1500 # Maker refresh cadence (ms)
|
MAKER_REFRESH_INTERVAL_MS=500 # Maker refresh cadence (ms)
|
||||||
MAKER_MAX_CLOSE_SLIPPAGE_PCT=0.05 # Maker close slippage guard (fallbacks to MAX_CLOSE_SLIPPAGE_PCT)
|
MAKER_MAX_CLOSE_SLIPPAGE_PCT=0.05 # Maker close slippage guard (fallbacks to MAX_CLOSE_SLIPPAGE_PCT)
|
||||||
MAKER_PRICE_TICK=0.1 # Maker price tick size (defaults to PRICE_TICK)
|
MAKER_PRICE_TICK=0.1 # Maker price tick size (defaults to PRICE_TICK)
|
||||||
|
|
||||||
|
# Grid strategy defaults
|
||||||
|
GRID_LOWER_PRICE=25000 # Grid lower bound price (quote currency)
|
||||||
|
GRID_UPPER_PRICE=35000 # Grid upper bound price
|
||||||
|
GRID_LEVELS=10 # Number of grid levels between bounds (>=2)
|
||||||
|
GRID_ORDER_SIZE=0.001 # Quantity per grid order (base asset units)
|
||||||
|
GRID_MAX_POSITION_SIZE=0.01 # Max inventory the grid may hold (base units)
|
||||||
|
GRID_REFRESH_INTERVAL_MS=1000 # Grid evaluation cadence (ms)
|
||||||
|
GRID_MAX_LOG_ENTRIES=200 # Grid trade log length (defaults to MAX_LOG_ENTRIES when unset)
|
||||||
|
GRID_DIRECTION=both # Order direction: both | long | short
|
||||||
|
GRID_STOP_LOSS_PCT=0.01 # Stop loss trigger percentage beyond bounds (0.01 => 1%)
|
||||||
|
GRID_RESTART_TRIGGER_PCT=0.01 # Restart buffer percentage inside bounds
|
||||||
|
GRID_AUTO_RESTART_ENABLED=true # Automatically resume grid when price re-enters range
|
||||||
|
GRID_MAX_CLOSE_SLIPPAGE_PCT=0.05 # Close-order slippage guard relative to mark price
|
||||||
|
# GRID_PRICE_TICK=0.1 # Optional override for grid price tick (falls back to PRICE_TICK)
|
||||||
|
# GRID_QTY_STEP=0.001 # Optional override for grid quantity step (falls back to QTY_STEP)
|
||||||
|
|
||||||
# GRVT authentication (set when EXCHANGE=grvt)
|
# GRVT authentication (set when EXCHANGE=grvt)
|
||||||
GRVT_API_KEY=
|
GRVT_API_KEY=
|
||||||
GRVT_API_SECRET=
|
GRVT_API_SECRET=
|
||||||
@@ -47,3 +65,65 @@ GRVT_ENV=prod
|
|||||||
# GRVT_COOKIE="gravity=..." # Pre-provisioned session cookie (auto-refresh uses API key when absent)
|
# GRVT_COOKIE="gravity=..." # Pre-provisioned session cookie (auto-refresh uses API key when absent)
|
||||||
# GRVT_ACCOUNT_ID= # Populated automatically after login
|
# GRVT_ACCOUNT_ID= # Populated automatically after login
|
||||||
# GRVT_SIGNER_PATH=./grvt-signer.cjs # Custom signature provider module
|
# GRVT_SIGNER_PATH=./grvt-signer.cjs # Custom signature provider module
|
||||||
|
|
||||||
|
# Lighter authentication (set when EXCHANGE=lighter)
|
||||||
|
LIGHTER_ACCOUNT_INDEX=
|
||||||
|
LIGHTER_API_PRIVATE_KEY= # 40-byte hex private key (e.g., 0x...)
|
||||||
|
LIGHTER_API_KEY_INDEX=0 # API key slot (default 0)
|
||||||
|
LIGHTER_SYMBOL=BTCUSDT # Trading pair (defaults to TRADE_SYMBOL when omitted)
|
||||||
|
LIGHTER_ENV=testnet # mainnet | testnet | staging | dev
|
||||||
|
# LIGHTER_BASE_URL=https://testnet.zklighter.elliot.ai
|
||||||
|
# LIGHTER_CHAIN_ID=300 # Override inferred chain id when needed
|
||||||
|
# LIGHTER_MARKET_ID=1 # Prefer explicit market id when symbols differ
|
||||||
|
# LIGHTER_PRICE_DECIMALS=3 # Manual override for price decimals (optional)
|
||||||
|
# LIGHTER_SIZE_DECIMALS=3 # Manual override for size decimals (optional)
|
||||||
|
|
||||||
|
# Backpack exchange configuration
|
||||||
|
# Fill these with your Backpack API credentials and preferences
|
||||||
|
|
||||||
|
BACKPACK_API_KEY=
|
||||||
|
BACKPACK_API_SECRET=
|
||||||
|
BACKPACK_PASSWORD=
|
||||||
|
BACKPACK_SUBACCOUNT=
|
||||||
|
|
||||||
|
# Use sandbox environment: "true" to enable, otherwise leave as false
|
||||||
|
BACKPACK_SANDBOX=false
|
||||||
|
|
||||||
|
# Default trading symbol (falls back to TRADE_SYMBOL or BTCUSDC)
|
||||||
|
BACKPACK_SYMBOL=BTC_USD_PERP
|
||||||
|
|
||||||
|
# Enable verbose adapter logging: set to "1" or "true"
|
||||||
|
BACKPACK_DEBUG=false
|
||||||
|
|
||||||
|
# Paradex exchange configuration
|
||||||
|
# Provide the EVM private key & wallet address for onboarded accounts.
|
||||||
|
# When EXCHANGE=paradex these values are used automatically.
|
||||||
|
|
||||||
|
PARADEX_SYMBOL=BTC-USD-PERP
|
||||||
|
PARADEX_PRIVATE_KEY=
|
||||||
|
PARADEX_WALLET_ADDRESS=
|
||||||
|
|
||||||
|
# Enable testnet endpoints by setting to "true"; defaults to false (mainnet).
|
||||||
|
# PARADEX_SANDBOX=false
|
||||||
|
|
||||||
|
# Force disabling ccxt.pro websocket usage by setting to "false" (pro is preferred when installed).
|
||||||
|
# PARADEX_USE_PRO=true
|
||||||
|
|
||||||
|
# Optional reconnect delay override (milliseconds, e.g., 2000). Leave blank for default.
|
||||||
|
# PARADEX_RECONNECT_DELAY_MS=
|
||||||
|
|
||||||
|
# Enable verbose adapter logging: set to "1" or "true"
|
||||||
|
# PARADEX_DEBUG=false
|
||||||
|
|
||||||
|
# Extended exchange configuration (Starknet)
|
||||||
|
# Set EXCHANGE=extended to activate. Requires on-chain Stark key + vault id.
|
||||||
|
EXTENDED_API_KEY=
|
||||||
|
EXTENDED_STARK_PRIVATE_KEY= # 0x-prefixed Stark private key for signing orders
|
||||||
|
EXTENDED_VAULT_ID= # Vault/sub-account id from Extended UI
|
||||||
|
EXTENDED_MARKET=BTC-USD # Defaults to TRADE_SYMBOL when omitted
|
||||||
|
|
||||||
|
# Optional overrides (defaults target mainnet host)
|
||||||
|
# EXTENDED_API_HOST=api.starknet.extended.exchange
|
||||||
|
# EXTENDED_STREAM_HOST=api.starknet.extended.exchange
|
||||||
|
# EXTENDED_PRIVATE_STREAM_HOST=api.starknet.extended.exchange
|
||||||
|
# EXTENDED_USER_AGENT=ritmex-bot
|
||||||
|
|||||||
@@ -1,19 +1,35 @@
|
|||||||
# Repository Guidelines
|
# Repository Guidelines
|
||||||
|
|
||||||
## Project Structure & Module Organization
|
## Project Structure & Module Organization
|
||||||
The Bun-based entry point (`index.ts`) lives at the root for quick smoke checks, alongside `tsconfig.json` and the Bun lockfile. The production-ready trading agents reside in `legacy/`, which is a pnpm-managed workspace. Inside `legacy/`, strategy scripts (`trendV2.ts`, `maker.ts`, `bot.ts`) and the CLI live at the top level, shared helpers are under `legacy/utils/`, and exchange adapters plus tests sit in `legacy/exchanges/`. Reference environment samples are provided in `legacy/env.example`, and longer-form docs are kept in `legacy/docs/`.
|
The Bun entry point (`index.ts`) lives at the repo root for quick CLI smoke checks. All production code is under `src/`:
|
||||||
|
|
||||||
|
- `src/strategy/` gathers every live trading engine (`maker`, `offset-maker`, `trend`). Shared helpers for strategy wiring sit in `src/strategy/common/`.
|
||||||
|
- `src/core/` keeps order coordination plus shared libs used by multiple strategies.
|
||||||
|
- `src/exchanges/` exposes the adapters and REST/websocket clients.
|
||||||
|
- `src/ui/` implements the Ink dashboards for each strategy (they remain independent dashboards).
|
||||||
|
- `src/logging/` contains the trade log helper.
|
||||||
|
- `src/utils/` and `src/config.ts` hold cross-cutting utilities and runtime config.
|
||||||
|
- `docs/` stores reference material, while `tests/` contains Vitest suites.
|
||||||
|
|
||||||
## Build, Test, and Development Commands
|
## Build, Test, and Development Commands
|
||||||
Run `bun install` at the root to satisfy the lightweight Bun demo. Change into `legacy/` for real work: `pnpm install` bootstraps dependencies, `pnpm start` launches the default trend strategy, `pnpm maker` starts the market-making loop, `pnpm cli:start` triggers the dual-exchange hedging flow, and `pnpm test` executes the full Vitest suite. Use `pnpm aster:test` when you only need the Aster adapter checks.
|
- `bun install` – install dependencies.
|
||||||
|
- `bun run index.ts` – launch the CLI menu.
|
||||||
|
- `bun x vitest run` – execute the full test suite; `bun x vitest --watch` for incremental runs.
|
||||||
|
|
||||||
|
Strategy-specific scripts still execute through the CLI; there is no separate `legacy/` workspace.
|
||||||
|
|
||||||
## Coding Style & Naming Conventions
|
## Coding Style & Naming Conventions
|
||||||
All code is modern TypeScript using native ES modules. Follow the existing two-space indentation, keep imports sorted from external to local, and prefer `camelCase` for variables/functions with `PascalCase` for classes and enums. Strategy files stay in the project root with descriptive verbs (for example `maker.ts`), while shared utilities belong under `legacy/utils/`. Comments should stay concise and explain non-obvious trading logic.
|
Use modern TypeScript with ES modules, two-space indentation, and sorted imports (external → internal). Favor `camelCase` for variables/functions and `PascalCase` for classes/enums. Place new strategies under `src/strategy/`, shared utilities under `src/utils/` or `src/strategy/common/` when they only apply to strategies. Keep comments focused on non-obvious trading logic.
|
||||||
|
|
||||||
## Testing Guidelines
|
## Testing Guidelines
|
||||||
Vitest powers unit and integration checks. Place new suites beside their subjects using the `<feature>.test.ts` pattern (e.g., `legacy/exchanges/aster.test.ts`). Ensure strategies ship with coverage for order flow, risk guardrails, and websocket edge cases before opening a pull request. Run `pnpm test --watch` during development to keep feedback tight.
|
Vitest powers unit/integration tests. Co-locate new tests next to their subject using `<feature>.test.ts`. Strategies should have coverage for order lifecycle, risk guards, and websocket edge cases. Run `bun x vitest --watch` during development for fast feedback.
|
||||||
|
|
||||||
## Commit & Pull Request Guidelines
|
## Commit & Pull Request Guidelines
|
||||||
The repository history is empty, so adopt a lightweight Conventional Commits style (for example `feat: add hedging status panel`) to keep future changelogs clear. Commits should stay scoped to one strategy or module. Pull requests need a concise summary, reproduction or validation notes (commands run, environments touched), and screenshots or log excerpts when behavior changes. Link tracking issues or tasks to help downstream coordination.
|
Follow lightweight Conventional Commits (e.g. `feat: add hedging status panel`). Scope each commit to a single module or strategy. PRs should include:
|
||||||
|
- Summary of changes.
|
||||||
|
- Validation notes (commands run, environments touched).
|
||||||
|
- Relevant logs or screenshots for behavior changes.
|
||||||
|
Link issues/tasks when available.
|
||||||
|
|
||||||
## Environment & Secrets
|
## Environment & Secrets
|
||||||
Copy `legacy/env.example` to `.env` and populate API keys for Bitget and AsterDex before running live strategies. Never commit secrets; rely on local dotenv files or your deployment platform's secret manager. Rotate keys immediately if logs or configs leave the sandbox.
|
Duplicate `.env.example` to `.env` and populate API keys (Aster/GRVT) before running strategies. Do not commit secrets—use local `.env` or deployment secret managers. Rotate keys if they leak into logs or backups.
|
||||||
|
|||||||
@@ -1,219 +1,189 @@
|
|||||||
# ritmex-bot
|
# ritmex-bot
|
||||||
|
|
||||||
一个基于 Bun 的 Aster 永续合约终端机器人,内置趋势跟随(SMA30)与做市策略,使用 websocket 实时行情,命令行界面由 Ink 驱动,可在断线后自动恢复运行。
|
基于 Bun 的多交易所永续合约量化终端,内置趋势跟随(SMA30)、Guardian 防守与做市策略,支持快速恢复、实时行情订阅、日志追踪与 CLI 仪表盘。
|
||||||
|
|
||||||
|
如果您希望获取优惠并支持本项目,请考虑使用以下注册链接:
|
||||||
|
|
||||||
|
* [Aster 30% 手续费优惠注册链接](https://www.asterdex.com/zh-CN/referral/4665f3)
|
||||||
|
* [Binance 手续费优惠注册链接](https://www.binance.com/join?ref=KNKCA9XC)
|
||||||
|
* [GRVT 手续费优惠注册链接](https://grvt.io/exchange/sign-up?ref=sea)
|
||||||
|
* [Backpack 手续费优惠注册链接](https://backpack.exchange/join/ritmex)
|
||||||
|
* [edgex 手续费优惠注册链接](https://pro.edgex.exchange/referral/BULL)
|
||||||
|
* [Paradex 手续费优惠注册链接](https://paradex.io/ref/xingxingjun)
|
||||||
|
* [Apex 手续费优惠注册链接](https://join.omni.apex.exchange/SEA)
|
||||||
|
|
||||||
|
## 文档索引
|
||||||
|
- [English README](README_en.md)
|
||||||
|
- [简明上手指南(零基础)](simple-readme.md)
|
||||||
|
- [基础网格策略使用教程](grid-trading.md)
|
||||||
|
|
||||||
|
## 核心特性
|
||||||
|
- **实时行情与风控**:Websocket + REST 自动同步账户、挂单与仓位,断线后自动恢复。
|
||||||
|
- **趋势策略**:SMA30 穿越入场,内置止损、移动止盈、布林带带宽过滤与步进锁盈。
|
||||||
|
- **Guardian 策略**:不主动开单,实时监听账户仓位并强制补挂/移动止损与动态止盈,防止裸奔。
|
||||||
|
- **做市策略**:支持双边追价、风险阈值控制与订单自愈。
|
||||||
|
- **模块化架构**:策略引擎、交易所适配器与 Ink CLI 相互解耦,新增交易所或策略更容易。
|
||||||
|
|
||||||
|
## 支持的交易所
|
||||||
|
| 交易所 | 合约类型 | 必填环境变量 | 备注 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Aster | USDT 永续 | `ASTER_API_KEY`, `ASTER_API_SECRET` | 默认交易所;兼容脚本引导
|
||||||
|
| GRVT | USDT 永续 | `GRVT_API_KEY`, `GRVT_API_SECRET`, `GRVT_SUB_ACCOUNT_ID` | `GRVT_ENV` 可切换 `prod`/`testnet`
|
||||||
|
| Lighter | zkLighter 永续 | `LIGHTER_ACCOUNT_INDEX`, `LIGHTER_API_PRIVATE_KEY` | 默认 `LIGHTER_ENV=testnet`
|
||||||
|
| Backpack | USDC 永续 | `BACKPACK_API_KEY`, `BACKPACK_API_SECRET`, `BACKPACK_PASSWORD` | `BACKPACK_SANDBOX=true` 启用沙盒
|
||||||
|
| Paradex | StarkEx 永续 | `PARADEX_PRIVATE_KEY`, `PARADEX_WALLET_ADDRESS` | `PARADEX_SANDBOX=true` 使用测试网
|
||||||
|
|
||||||
|
## 系统要求
|
||||||
|
- Bun ≥ 1.2(需同时包含 `bun`、`bunx` 命令)
|
||||||
|
- macOS、Linux 或 Windows (推荐 WSL)
|
||||||
|
- Node.js 仅在部分工具链场景需要,可选
|
||||||
|
|
||||||
## 快速上手
|
## 快速上手
|
||||||
|
### 一键脚本(macOS / Linux / WSL)
|
||||||
使用优惠码获取 30% 手续费折扣:[注册 Aster 获取手续费优惠](https://www.asterdex.com/zh-CN/referral/4665f3)
|
|
||||||
|
|
||||||
如果你完全不懂代码,可以查看 **[小白教程](simple-readme.md) 了解使用方法。**
|
|
||||||
|
|
||||||
遇到Bug,反馈问题,请到 [Telegram群组](https://t.me/+4fdo0quY87o4Mjhh)
|
|
||||||
|
|
||||||
### 一键脚本
|
|
||||||
|
|
||||||
一键安装并启动(macOS / Linux):
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://github.com/discountry/ritmex-bot/raw/refs/heads/main/setup.sh | bash
|
curl -fsSL https://github.com/discountry/ritmex-bot/raw/refs/heads/main/setup.sh | bash
|
||||||
```
|
```
|
||||||
脚本会自动安装 Bun、安装依赖、引导输入 API Key/Secret,生成 `.env` 并启动程序。
|
脚本会安装 Bun、项目依赖,收集 Aster API 凭证,生成 `.env` 并启动 CLI。运行前请准备好对应交易所的 API Key/Secret。
|
||||||
|
|
||||||
Windows 使用 WSL(推荐):
|
### 手动安装
|
||||||
1. 先安装并启用 WSL,参考微软官方文档:[在 Windows 上安装 WSL](https://learn.microsoft.com/zh-cn/windows/wsl/install)
|
1. **获取代码**
|
||||||
2. 在命令行或者 PowerShell 输入 `wsl` 并按下回车打开 WSL。
|
|
||||||
3. 在 WSL 中运行一键脚本:
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://github.com/discountry/ritmex-bot/raw/refs/heads/main/setup.sh | bash
|
git clone https://github.com/discountry/ritmex-bot.git
|
||||||
|
cd ritmex-bot
|
||||||
```
|
```
|
||||||
按提示输入 ASTER_API_KEY / ASTER_API_SECRET,脚本将自动安装 Bun、依赖并启动程序。
|
不便使用 Git 时,可在仓库页面下载 ZIP 后手动解压。
|
||||||
|
2. **安装 Bun**
|
||||||
## 手动安装
|
- macOS / Linux:`curl -fsSL https://bun.sh/install | bash`
|
||||||
|
- Windows PowerShell:`powershell -c "irm bun.sh/install.ps1 | iex"`
|
||||||
1. **下载代码**
|
安装完成后重新打开终端,确认 `bun -v` 正常输出版本号。
|
||||||
- 如果会使用 Git:`git clone https://github.com/discountry/ritmex-bot.git`
|
3. **安装依赖**
|
||||||
- 如果不会使用 Git:点击仓库页面的 `Code` → `Download ZIP`,将压缩包解压到如 `桌面/ritmex-bot` 的目录。
|
|
||||||
2. **打开命令行并进入项目目录**
|
|
||||||
- macOS:通过 Spotlight (`⌘ + 空格`) 搜索 “Terminal” 并打开。
|
|
||||||
- Windows:在开始菜单搜索 “PowerShell” 或 “Windows Terminal” 并打开。
|
|
||||||
- 使用 `cd` 切换到项目目录,例如:
|
|
||||||
```bash
|
|
||||||
# macOS / Linux
|
|
||||||
cd ~/Desktop/ritmex-bot
|
|
||||||
# Windows
|
|
||||||
cd C:\Users\用户名\Desktop\ritmex-bot
|
|
||||||
```
|
|
||||||
3. **安装 [Bun](https://bun.com) ≥ 1.2**
|
|
||||||
- macOS / Linux:
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://bun.sh/install | bash
|
|
||||||
```
|
|
||||||
- Windows(PowerShell):
|
|
||||||
```powershell
|
|
||||||
powershell -c "irm bun.sh/install.ps1 | iex"
|
|
||||||
```
|
|
||||||
安装完成后关闭并重新打开终端,运行 `bun -v` 确认命令可用。
|
|
||||||
|
|
||||||
如果上述命令无法完成安装,请尝试 [bun官网](https://bun.com/get) 提供的各种安装方式。
|
|
||||||
|
|
||||||
Windows 用户如果无法正常安装,可以尝试先[安装 nodejs](https://nodejs.org/en/download)
|
|
||||||
|
|
||||||
然后使用 `npm` 安装 `bun`:
|
|
||||||
```bash
|
|
||||||
npm install -g bun
|
|
||||||
```
|
|
||||||
4. **安装依赖**
|
|
||||||
```bash
|
```bash
|
||||||
bun install
|
bun install
|
||||||
```
|
```
|
||||||
5. **配置环境变量**
|
4. **复制环境变量模板并填写**
|
||||||
复制 `.env.example` 为 `.env` 并填入你的 Aster API Key/Secret:
|
|
||||||
```bash
|
```bash
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
```
|
```
|
||||||
然后根据需要修改 `.env` 中的配置项:
|
按下文指南修改 `.env`,至少需要正确配置一个交易所的凭证。
|
||||||
- API KEY 获取地址 [https://www.asterdex.com/zh-CN/api-management](https://www.asterdex.com/zh-CN/api-management)
|
5. **运行 CLI**
|
||||||
- `ASTER_API_KEY` / `ASTER_API_SECRET`:Aster 交易所提供的 API 凭证。
|
|
||||||
- `TRADE_SYMBOL`:策略运行的交易对(默认 `BTCUSDT`),需与 API 权限范围一致。
|
|
||||||
- `TRADE_AMOUNT`:单次下单数量(合约张数折算后单位为标的货币,例如 BTC)。
|
|
||||||
- `LOSS_LIMIT`:单笔允许的最大亏损(USDT),触发即强制平仓。
|
|
||||||
- `TRAILING_PROFIT` / `TRAILING_CALLBACK_RATE`:趋势策略的动态止盈触发值(单位 USDT)与回撤百分比(百分数,如 0.2 表示 0.2%)。
|
|
||||||
- `PROFIT_LOCK_TRIGGER_USD` / `PROFIT_LOCK_OFFSET_USD`:达到一定浮盈后,将基础止损上调(做多)或下调(做空)到开仓价的偏移量(单位 USDT)。
|
|
||||||
- `PRICE_TICK` / `QTY_STEP`:交易对的最小价格变动单位与最小下单数量步长(例如 BTCUSDT 分别为 0.1 与 0.001)。
|
|
||||||
- `MAKER_*` 参数:做市策略追价阈值、报价偏移、刷新频率等,可按流动性需求调节。
|
|
||||||
6. **运行机器人**
|
|
||||||
```bash
|
```bash
|
||||||
bun run index.ts
|
bun run index.ts
|
||||||
```
|
```
|
||||||
在终端中按 ↑/↓ 选择 “趋势策略” 或 “做市策略”,回车启动。按 `Esc` 可返回选择菜单,`Ctrl+C` 退出。
|
方向键选择策略并回车启动;`Esc` 返回菜单,`Ctrl+C` 退出。
|
||||||
7. **风险提示**
|
|
||||||
建议先在小额或仿真环境中测试策略;真实资金操作前请确认 API 仅开启必要权限,并逐步验证配置。
|
|
||||||
|
|
||||||
A Bun-powered trading workstation for Aster perpetual contracts. The project ships two production strategies—an SMA30 trend follower and a dual-sided maker—that share a modular gateway, UI, and runtime state derived entirely from the exchange. Everything runs in the terminal via Ink, with live websocket refresh and automatic recovery from restarts or network failures.
|
## 通用环境变量
|
||||||
|
`.env.example` 提供了所有默认键值,下表概括最常用参数:
|
||||||
|
|
||||||
## Features
|
| 变量 | 说明 |
|
||||||
- **Live data over websockets** with REST fallbacks and automatic re-sync after reconnects.
|
| --- | --- |
|
||||||
- **Trend strategy**: SMA30 crossover entries, automated stop-loss / trailing-stop, and P&L tracking.
|
| `EXCHANGE` | 选择交易所(`aster`/`grvt`/`lighter`/`backpack`/`paradex`) |
|
||||||
- **Maker strategy**: adaptive bid/ask chasing, risk stops, and target order introspection.
|
| `TRADE_SYMBOL` | 交易对(默认 `BTCUSDT`) |
|
||||||
- **Extensibility**: exchange gateway, engines, and UI components are modular for new venues or strategies.
|
| `TRADE_AMOUNT` | 单笔下单数量(标的资产计) |
|
||||||
|
| `LOSS_LIMIT` | 单笔最大亏损触发的强平额度(USDT) |
|
||||||
|
| `TRAILING_PROFIT` / `TRAILING_CALLBACK_RATE` | 动态止盈触发值(USDT)与回撤百分比 |
|
||||||
|
| `PROFIT_LOCK_TRIGGER_USD` / `PROFIT_LOCK_OFFSET_USD` | 浮盈超过阈值后上调止损的触发金额与偏移 |
|
||||||
|
| `BOLLINGER_*` | 趋势策略布林带过滤参数 |
|
||||||
|
| `PRICE_TICK` / `QTY_STEP` | 交易所要求的最小报价与数量精度 |
|
||||||
|
| `POLL_INTERVAL_MS` | 趋势策略循环间隔(毫秒) |
|
||||||
|
| `MAX_CLOSE_SLIPPAGE_PCT` | 平仓时相对标记价允许的最大偏差 |
|
||||||
|
| `MAKER_*` | 做市策略专属参数(追价阈值、报价偏移、刷新频率等) |
|
||||||
|
|
||||||
## Requirements
|
> 可通过命令行临时覆盖交易所与策略(优先级高于 `.env`):
|
||||||
- [Bun](https://bun.com) ≥ 1.2
|
> ```bash
|
||||||
- Node.js (optional, only if you prefer `npm` tooling)
|
> bun run index.ts --exchange grvt --strategy maker
|
||||||
- Valid Aster API credentials with futures access
|
> bun run index.ts -e lighter -s offset-maker --silent
|
||||||
|
> ```
|
||||||
|
|
||||||
## Installation
|
## 交易所配置指南
|
||||||
|
### Aster
|
||||||
|
1. 将 `EXCHANGE` 保持为 `aster`(默认值)。
|
||||||
|
2. 填写 `ASTER_API_KEY` 与 `ASTER_API_SECRET`。
|
||||||
|
3. 根据交易对调整 `TRADE_SYMBOL`、`PRICE_TICK`、`QTY_STEP` 等精度参数。
|
||||||
|
4. 一键脚本会自动写入这些变量,手动部署时需自行维护。
|
||||||
|
|
||||||
|
### GRVT
|
||||||
|
1. 在 `.env` 中设置 `EXCHANGE=grvt`。
|
||||||
|
2. 填写 `GRVT_API_KEY`、`GRVT_API_SECRET`、`GRVT_SUB_ACCOUNT_ID`。
|
||||||
|
3. 若使用测试网,可将 `GRVT_ENV=testnet` 并调整 `GRVT_INSTRUMENT`/`GRVT_SYMBOL`。
|
||||||
|
4. 可选:提供 `GRVT_COOKIE` 或自定义 `GRVT_SIGNER_PATH` 以复用已有登录态。
|
||||||
|
|
||||||
|
### Lighter
|
||||||
|
1. 设置 `EXCHANGE=lighter`。
|
||||||
|
2. 填写 `LIGHTER_ACCOUNT_INDEX` 与 `LIGHTER_API_PRIVATE_KEY`(40 字节十六进制私钥),其中`LIGHTER_ACCOUNT_INDEX`是你的账户索引,需要你在官网按F12观察接口请求获取,`LIGHTER_API_PRIVATE_KEY`是你的API私钥。
|
||||||
|
3. 如需切换环境,将 `LIGHTER_ENV` 改为 `mainnet`/`staging`/`dev`;必要时指定 `LIGHTER_BASE_URL`。
|
||||||
|
4. 交易对默认为 `LIGHTER_SYMBOL=BTCUSDT`,也可按需重写价格与数量小数位。
|
||||||
|
|
||||||
|
### Backpack
|
||||||
|
1. 设置 `EXCHANGE=backpack`。
|
||||||
|
2. 填写 `BACKPACK_API_KEY`、`BACKPACK_API_SECRET`、`BACKPACK_PASSWORD`;如有分账户,补充 `BACKPACK_SUBACCOUNT`,默认填写主账户ID。
|
||||||
|
3. 使用测试环境时将 `BACKPACK_SANDBOX=true`,并确认 `BACKPACK_SYMBOL` 与实际符号一致(默认 `BTC_USD_PERP`)。
|
||||||
|
4. 可通过 `BACKPACK_DEBUG=true` 观察适配器详细日志。
|
||||||
|
|
||||||
|
### Paradex
|
||||||
|
1. 设置 `EXCHANGE=paradex`。
|
||||||
|
2. 提供 `PARADEX_PRIVATE_KEY`(EVM 私钥)与 `PARADEX_WALLET_ADDRESS` 注意这是你EVM钱包的地址和私钥,建议创建全新钱包,不要放置无关资产。
|
||||||
|
3. 默认连接主网,若需测试网,将 `PARADEX_SANDBOX=true` 并根据需要调整 `PARADEX_SYMBOL`。
|
||||||
|
4. 复杂环境可额外设置 `PARADEX_USE_PRO`、`PARADEX_RECONNECT_DELAY_MS` 或调试开关。
|
||||||
|
|
||||||
|
## 命令速查
|
||||||
```bash
|
```bash
|
||||||
bun install
|
bun run index.ts # 启动 CLI(默认入口)
|
||||||
|
bun run start # 等价于运行 index.ts
|
||||||
|
bun run dev # 调试模式
|
||||||
|
bun x vitest run # 执行全部测试
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## 静默启动与后台运行
|
||||||
Create an `.env` (or export environment variables) with at least:
|
### 直接静默启动
|
||||||
|
无需进入 Ink 菜单,可用命令行直接拉起指定策略:
|
||||||
```bash
|
```bash
|
||||||
ASTER_API_KEY=your_key
|
bun run index.ts --strategy trend --silent
|
||||||
ASTER_API_SECRET=your_secret
|
bun run index.ts --strategy maker --silent
|
||||||
TRADE_SYMBOL=BTCUSDT # optional, defaults to BTCUSDT
|
bun run index.ts --strategy offset-maker --silent
|
||||||
TRADE_AMOUNT=0.001 # position size used by both strategies
|
|
||||||
LOSS_LIMIT=0.03 # per-trade USD loss cap
|
|
||||||
TRAILING_PROFIT=0.2 # trailing activation profit in USDT
|
|
||||||
TRAILING_CALLBACK_RATE=0.2 # trailing callback in percent, e.g. 0.2 => 0.2%
|
|
||||||
PROFIT_LOCK_TRIGGER_USD=0.1 # profit threshold to start moving base stop (USDT)
|
|
||||||
PROFIT_LOCK_OFFSET_USD=0.05 # base stop offset from entry after trigger (USDT)
|
|
||||||
PRICE_TICK=0.1 # price tick size; set per symbol
|
|
||||||
QTY_STEP=0.001 # quantity step size; set per symbol
|
|
||||||
```
|
```
|
||||||
Additional maker-specific knobs (`MAKER_*`) live in `src/config.ts` and may be overridden via env vars:
|
如需同时指定交易所,可叠加 `--exchange/-e` 参数。
|
||||||
|
|
||||||
|
### 项目内置脚本
|
||||||
|
`package.json` 提供了便捷脚本:
|
||||||
```bash
|
```bash
|
||||||
# Maker-specific (units in USDT unless noted)
|
bun run start:trend:silent
|
||||||
MAKER_LOSS_LIMIT=0.03 # override maker risk stop; defaults to LOSS_LIMIT
|
bun run start:maker:silent
|
||||||
MAKER_PRICE_CHASE=0.3 # chase threshold
|
bun run start:offset:silent
|
||||||
MAKER_BID_OFFSET=0 # bid offset from top bid (USDT)
|
|
||||||
MAKER_ASK_OFFSET=0 # ask offset from top ask (USDT)
|
|
||||||
MAKER_REFRESH_INTERVAL_MS=1500 # maker refresh cadence (ms)
|
|
||||||
MAKER_MAX_CLOSE_SLIPPAGE_PCT=0.05 # allowed deviation vs mark when closing
|
|
||||||
MAKER_PRICE_TICK=0.1 # maker tick size; defaults to PRICE_TICK
|
|
||||||
```
|
```
|
||||||
|
|
||||||
To switch the entire CLI to GRVT instead of Aster, set `EXCHANGE=grvt` and provide the programmable API credentials:
|
### 使用 pm2 守护并自动重启
|
||||||
|
安装 `pm2`(示例:`bun add -d pm2`)后,可在项目内直接运行:
|
||||||
```bash
|
```bash
|
||||||
EXCHANGE=grvt
|
bunx pm2 start bun --name ritmex-trend --cwd . --restart-delay 5000 -- run index.ts --strategy trend --silent
|
||||||
GRVT_API_KEY=your_api_key
|
|
||||||
GRVT_API_SECRET=0xabc123... # private key used for EIP-712 order signatures
|
|
||||||
GRVT_SUB_ACCOUNT_ID=your_sub_account # sub account that actually trades
|
|
||||||
GRVT_INSTRUMENT=BTC_USDT_Perp # instrument as defined by GRVT
|
|
||||||
GRVT_SYMBOL=BTCUSDT # optional display symbol; defaults to instrument sans underscores
|
|
||||||
GRVT_ENV=prod # optional environment switch (prod/testnet/staging/dev)
|
|
||||||
# Optional overrides
|
|
||||||
# GRVT_SIGNER_PATH=./grvt-signer.cjs # replace the built-in signer if you run an external service
|
|
||||||
# GRVT_COOKIE="gravity=..." # pre-provisioned session cookie (auto-fetched via API key when absent)
|
|
||||||
# GRVT_ACCOUNT_ID=... # populated automatically after login
|
|
||||||
```
|
```
|
||||||
|
或调用预置脚本:
|
||||||
The adapter logs in with `GRVT_API_KEY`, refreshes cookies automatically, and signs orders locally using `GRVT_API_SECRET` following GRVT's EIP‑712 schema. If you prefer to delegate signing to another process, set `GRVT_SIGNER_PATH`; the module should export a function (default export also works) that receives a context object (unsigned order, nonce/expiration, instrument metadata, chain id, etc.) and returns `{ signer, r, s, v, expiration, nonce }`. If you leave `GRVT_SIGNER_PATH` unset, the built-in signer uses `GRVT_API_SECRET` directly.
|
|
||||||
|
|
||||||
### 切换到 GRVT 交易所
|
|
||||||
|
|
||||||
将环境变量 `EXCHANGE` 设为 `grvt` 后,所有策略会改用 GRVT 适配器:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
EXCHANGE=grvt
|
bun run pm2:start:trend
|
||||||
GRVT_API_KEY=你的APIKey
|
bun run pm2:start:maker
|
||||||
GRVT_API_SECRET=0xabc123... # 用于订单签名的私钥
|
bun run pm2:start:offset
|
||||||
GRVT_SUB_ACCOUNT_ID=your_sub_account_id # 具体交易子账号
|
|
||||||
GRVT_INSTRUMENT=BTC_USDT_Perp # 交易品种,需与策略一致
|
|
||||||
GRVT_SYMBOL=BTCUSDT # 可选,内部用于展示,默认由 instrument 推导
|
|
||||||
GRVT_ENV=prod # 可选:prod / testnet / staging / dev,默认 prod
|
|
||||||
# 可选覆盖项
|
|
||||||
# GRVT_SIGNER_PATH=./grvt-signer.cjs # 如果你希望由外部服务签名
|
|
||||||
# GRVT_COOKIE="gravity=..." # 预先获取到的 Cookie(若未提供,将使用 API Key 自动登录)
|
|
||||||
# GRVT_ACCOUNT_ID=... # 登录后自动填充
|
|
||||||
```
|
```
|
||||||
|
完成配置后可执行 `pm2 save` 持久化进程列表。
|
||||||
|
|
||||||
适配器会基于 `GRVT_API_SECRET` 自动完成 EIP‑712 签名并提交订单。如果你需要自定义签名流程,可通过 `GRVT_SIGNER_PATH` 指定模块(CommonJS 或 ESM 均可)。模块需导出一个函数,接收签名上下文(未签名订单、nonce/expiration、合约元信息、链 ID 等)并返回包含 `signer/r/s/v/expiration/nonce` 字段的对象,例如:
|
## 测试
|
||||||
|
项目使用 Vitest:
|
||||||
```js
|
|
||||||
// grvt-signer.cjs
|
|
||||||
module.exports = async function signOrder(context) {
|
|
||||||
// 调用你自己的签名服务,或者使用本地私钥完成签名
|
|
||||||
const signature = await mySigner(context);
|
|
||||||
return {
|
|
||||||
signer: signature.signer,
|
|
||||||
r: signature.r,
|
|
||||||
s: signature.s,
|
|
||||||
v: signature.v,
|
|
||||||
expiration: signature.expiration,
|
|
||||||
nonce: signature.nonce,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
如未自定义 `GRVT_SIGNER_PATH`,适配器会直接使用 `GRVT_API_SECRET` 完成签名。
|
|
||||||
|
|
||||||
## Running the CLI
|
|
||||||
```bash
|
```bash
|
||||||
bun run index.ts # or: bun run dev / bun run start
|
bun run test
|
||||||
|
bun x vitest --watch
|
||||||
```
|
```
|
||||||
Pick a strategy with the arrow keys. Press `Esc` to return to the menu. The dashboard shows live order books, holdings, pending orders, and recent events. 状态完全以交易所数据为准,重新启动时会自动同步账户和挂单。
|
|
||||||
|
|
||||||
## Testing
|
## 常见问题
|
||||||
```bash
|
- 至少准备 50–100 USDT 资金以覆盖策略运行需求。
|
||||||
bun run test # bun x vitest run
|
- 杠杆需在交易所提前设置(建议 ~50 倍),程序不会自动调整。
|
||||||
bun run test:watch # stay in watch mode
|
- 请确保服务器/电脑时间同步真实世界时间,避免签名过期。
|
||||||
```
|
- 账户需保持单向持仓模式。
|
||||||
Current tests cover the order coordinator utilities and strategy helpers; add unit tests beside new modules as you extend the system.
|
- `.env` 未读取:确认文件位于项目根目录且变量名无误。
|
||||||
|
- API 拒绝访问:检查交易所后台权限,确保开启合约读写。
|
||||||
|
- 精度错误:同步交易对的最小价格与数量步长。
|
||||||
|
更多排查细节可参见 [简明上手指南](simple-readme.md)。
|
||||||
|
|
||||||
## Project Layout
|
## 社区与支持
|
||||||
- `src/config.ts` – shared runtime configuration
|
- Telegram 交流群:[https://t.me/+4fdo0quY87o4Mjhh](https://t.me/+4fdo0quY87o4Mjhh)
|
||||||
- `src/core/` – trend & maker engines plus order coordination
|
- 欢迎通过 Issue 或 PR 提交反馈、特性建议
|
||||||
- `src/exchanges/` – Aster REST/WS gateway and adapters
|
|
||||||
- `src/ui/` – Ink components and strategy dashboards
|
|
||||||
- `src/utils/` – math helpers and strategy utilities
|
|
||||||
- `tests/` – Vitest suites for critical modules
|
|
||||||
|
|
||||||
## Troubleshooting
|
## 风险提示
|
||||||
- **Websocket reconnect loops**: ensure outbound access to `wss://fstream.asterdex.com/ws` and REST endpoints.
|
量化交易具备风险。请先在仿真或小额账户中验证策略表现,妥善保管 API 密钥,仅开启必要权限。
|
||||||
- **429 or 5xx responses**: the gateway backs off automatically, but check your rate limits and credentials.
|
|
||||||
- **CLI input errors**: run in a real TTY; non-interactive shells disable keyboard shortcuts but the UI still renders.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
Issues and PRs are welcome. When adding strategies or exchanges, follow the modular patterns in `src/core` and add tests under `tests/`.
|
|
||||||
|
|||||||
+186
@@ -0,0 +1,186 @@
|
|||||||
|
# ritmex-bot
|
||||||
|
|
||||||
|
A Bun-powered multi-exchange perpetuals workstation that ships an SMA30 trend engine, a Guardian stop sentinel, and two market-making modes. It offers instant restarts, realtime market data, structured logging, and an Ink-based CLI dashboard.
|
||||||
|
|
||||||
|
* [Aster referral (30% fee discount)](https://www.asterdex.com/en/referral/4665f3)
|
||||||
|
* [Binance referral link](https://www.binance.com/join?ref=KNKCA9XC)
|
||||||
|
* [GRVT referral link](https://grvt.io/exchange/sign-up?ref=sea)
|
||||||
|
* [Backpack referral link](https://backpack.exchange/join/ritmex)
|
||||||
|
* [edgex referral link](https://pro.edgex.exchange/referral/BULL)
|
||||||
|
* [Paradex referral link](https://paradex.io/ref/xingxingjun)
|
||||||
|
* [Apex referral link](https://join.omni.apex.exchange/RITHMEX)
|
||||||
|
|
||||||
|
## Documentation Map
|
||||||
|
- [中文 README](README.md)
|
||||||
|
- [Beginner-friendly Quick Start](simple-readme.md)
|
||||||
|
|
||||||
|
## Highlights
|
||||||
|
- **Live data & risk sync** via websockets with REST fallbacks and full reconciliation on restart.
|
||||||
|
- **Trend strategy** featuring SMA30 entries, fixed stop loss, trailing stop, Bollinger bandwidth gate, and profit-lock stepping.
|
||||||
|
- **Guardian strategy** that never opens trades but mirrors your live exposure, ensuring every position has a synced stop loss and trailing stop.
|
||||||
|
- **Market-making loop** with dual-sided quote chasing, loss caps, and automatic order healing.
|
||||||
|
- **Modular architecture** decoupling engines, exchange adapters, and the Ink CLI for easy venue or strategy extensions.
|
||||||
|
|
||||||
|
## Supported Exchanges
|
||||||
|
| Exchange | Contract Type | Required Environment Variables | Notes |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Aster | USDT perpetuals | `ASTER_API_KEY`, `ASTER_API_SECRET` | Default venue; works with the bootstrap script |
|
||||||
|
| GRVT | USDT perpetuals | `GRVT_API_KEY`, `GRVT_API_SECRET`, `GRVT_SUB_ACCOUNT_ID` | Switch `GRVT_ENV` between `prod` and `testnet` |
|
||||||
|
| Lighter | zkLighter perpetuals | `LIGHTER_ACCOUNT_INDEX`, `LIGHTER_API_PRIVATE_KEY` | Defaults to `LIGHTER_ENV=testnet` |
|
||||||
|
| Backpack | USDC perpetuals | `BACKPACK_API_KEY`, `BACKPACK_API_SECRET`, `BACKPACK_PASSWORD` | Set `BACKPACK_SANDBOX=true` for the sandbox |
|
||||||
|
| Paradex | StarkEx perpetuals | `PARADEX_PRIVATE_KEY`, `PARADEX_WALLET_ADDRESS` | Toggle `PARADEX_SANDBOX=true` for the testnet |
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
- Bun ≥ 1.2 (both `bun` and `bunx` on PATH)
|
||||||
|
- macOS, Linux, or Windows via WSL (native Windows works but WSL is recommended)
|
||||||
|
- Node.js is optional unless your tooling requires it
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
### One-line bootstrap (macOS / Linux / WSL)
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://github.com/discountry/ritmex-bot/raw/refs/heads/main/setup.sh | bash
|
||||||
|
```
|
||||||
|
The script installs Bun, project dependencies, collects Aster API credentials, generates `.env`, and launches the CLI. Prepare the relevant exchange API keys before running it.
|
||||||
|
|
||||||
|
### Manual installation
|
||||||
|
1. **Clone the repository**
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/discountry/ritmex-bot.git
|
||||||
|
cd ritmex-bot
|
||||||
|
```
|
||||||
|
Alternatively, download the ZIP from GitHub and extract it manually.
|
||||||
|
2. **Install Bun**
|
||||||
|
- macOS / Linux: `curl -fsSL https://bun.sh/install | bash`
|
||||||
|
- Windows PowerShell: `powershell -c "irm bun.sh/install.ps1 | iex"`
|
||||||
|
Re-open the terminal and verify `bun -v` prints a version.
|
||||||
|
3. **Install dependencies**
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
4. **Create your environment file**
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
Edit `.env` with the exchange credentials and overrides you plan to use.
|
||||||
|
5. **Launch the CLI**
|
||||||
|
```bash
|
||||||
|
bun run index.ts
|
||||||
|
```
|
||||||
|
Use the arrow keys to pick a strategy, `Enter` to start, `Esc` to go back, and `Ctrl+C` to exit.
|
||||||
|
|
||||||
|
## Shared Configuration
|
||||||
|
`.env.example` captures all defaults; the most common settings are summarised below.
|
||||||
|
|
||||||
|
| Variable | Purpose |
|
||||||
|
| --- | --- |
|
||||||
|
| `EXCHANGE` | Choose the venue (`aster` / `grvt` / `lighter` / `backpack` / `paradex`) |
|
||||||
|
| `TRADE_SYMBOL` | Contract symbol (defaults to `BTCUSDT`) |
|
||||||
|
| `TRADE_AMOUNT` | Order size in base asset units |
|
||||||
|
| `LOSS_LIMIT` | Max per-trade loss in USDT before forced close |
|
||||||
|
| `TRAILING_PROFIT` / `TRAILING_CALLBACK_RATE` | Trailing stop trigger (USDT) and pullback percentage |
|
||||||
|
| `PROFIT_LOCK_TRIGGER_USD` / `PROFIT_LOCK_OFFSET_USD` | Profit lock trigger and offset thresholds |
|
||||||
|
| `BOLLINGER_*` | Bollinger bandwidth filters for the trend engine |
|
||||||
|
| `PRICE_TICK` / `QTY_STEP` | Exchange precision filters for price and quantity |
|
||||||
|
| `POLL_INTERVAL_MS` | Trend engine polling cadence in milliseconds |
|
||||||
|
| `MAX_CLOSE_SLIPPAGE_PCT` | Allowed deviation vs mark price when closing |
|
||||||
|
| `MAKER_*` | Maker-specific knobs (quote offsets, refresh cadence, slippage guard, etc.) |
|
||||||
|
|
||||||
|
> CLI flags override environment variables at runtime:
|
||||||
|
> ```bash
|
||||||
|
> bun run index.ts --exchange grvt --strategy maker
|
||||||
|
> bun run index.ts -e lighter -s offset-maker --silent
|
||||||
|
> ```
|
||||||
|
|
||||||
|
## Exchange Setup Guides
|
||||||
|
### Aster
|
||||||
|
1. Keep `EXCHANGE=aster` (default value).
|
||||||
|
2. Supply `ASTER_API_KEY` and `ASTER_API_SECRET`.
|
||||||
|
3. Adjust `TRADE_SYMBOL`, `PRICE_TICK`, and `QTY_STEP` to match the requested market.
|
||||||
|
4. The bootstrap script auto-populates these variables; manual installs must maintain them.
|
||||||
|
|
||||||
|
### GRVT
|
||||||
|
1. Set `EXCHANGE=grvt` inside `.env`.
|
||||||
|
2. Fill `GRVT_API_KEY`, `GRVT_API_SECRET`, and `GRVT_SUB_ACCOUNT_ID`.
|
||||||
|
3. Use `GRVT_ENV=testnet` when targeting the test environment, and align `GRVT_INSTRUMENT` / `GRVT_SYMBOL`.
|
||||||
|
4. Optional: provide `GRVT_COOKIE` or a custom `GRVT_SIGNER_PATH` when reusing an existing session.
|
||||||
|
|
||||||
|
### Lighter
|
||||||
|
1. Set `EXCHANGE=lighter`.
|
||||||
|
2. Provide `LIGHTER_ACCOUNT_INDEX` and `LIGHTER_API_PRIVATE_KEY` (40-byte hex private key).
|
||||||
|
3. Switch `LIGHTER_ENV` to `mainnet`, `staging`, or `dev` when necessary; override `LIGHTER_BASE_URL` if endpoints differ.
|
||||||
|
4. `LIGHTER_SYMBOL` defaults to `BTCUSDT`; override price/size decimals when markets differ.
|
||||||
|
|
||||||
|
### Backpack
|
||||||
|
1. Set `EXCHANGE=backpack`.
|
||||||
|
2. Populate `BACKPACK_API_KEY`, `BACKPACK_API_SECRET`, and `BACKPACK_PASSWORD`; add `BACKPACK_SUBACCOUNT` if you trade from a subaccount.
|
||||||
|
3. Toggle `BACKPACK_SANDBOX=true` for the sandbox environment and verify `BACKPACK_SYMBOL` matches the contract (defaults to `BTC_USD_PERP`).
|
||||||
|
4. Enable `BACKPACK_DEBUG=true` for verbose adapter logging.
|
||||||
|
|
||||||
|
### Paradex
|
||||||
|
1. Set `EXCHANGE=paradex`.
|
||||||
|
2. Provide `PARADEX_PRIVATE_KEY` (EVM private key) and `PARADEX_WALLET_ADDRESS`.
|
||||||
|
3. The adapter connects to mainnet by default; enable `PARADEX_SANDBOX=true` and adjust `PARADEX_SYMBOL` for testnet usage.
|
||||||
|
4. Advanced tuning: use `PARADEX_USE_PRO`, `PARADEX_RECONNECT_DELAY_MS`, or debug flags as needed.
|
||||||
|
|
||||||
|
## Command Cheatsheet
|
||||||
|
```bash
|
||||||
|
bun run index.ts # Launch the CLI (default entrypoint)
|
||||||
|
bun run start # Alias for bun run index.ts
|
||||||
|
bun run dev # Development entrypoint
|
||||||
|
bun x vitest run # Execute the full Vitest suite
|
||||||
|
```
|
||||||
|
|
||||||
|
## Silent & Background Execution
|
||||||
|
### Direct silent launch
|
||||||
|
Skip the Ink menu and start a strategy directly:
|
||||||
|
```bash
|
||||||
|
bun run index.ts --strategy trend --silent
|
||||||
|
bun run index.ts --strategy maker --silent
|
||||||
|
bun run index.ts --strategy offset-maker --silent
|
||||||
|
```
|
||||||
|
Combine with `--exchange/-e` to pin the venue for that run.
|
||||||
|
|
||||||
|
### Package scripts
|
||||||
|
Convenience aliases exposed via `package.json`:
|
||||||
|
```bash
|
||||||
|
bun run start:trend:silent
|
||||||
|
bun run start:maker:silent
|
||||||
|
bun run start:offset:silent
|
||||||
|
```
|
||||||
|
|
||||||
|
### Daemonising with pm2
|
||||||
|
Install `pm2` locally (e.g. `bun add -d pm2`) and launch the process:
|
||||||
|
```bash
|
||||||
|
bunx pm2 start bun --name ritmex-trend --cwd . --restart-delay 5000 -- run index.ts --strategy trend --silent
|
||||||
|
```
|
||||||
|
You can also call the bundled scripts:
|
||||||
|
```bash
|
||||||
|
bun run pm2:start:trend
|
||||||
|
bun run pm2:start:maker
|
||||||
|
bun run pm2:start:offset
|
||||||
|
```
|
||||||
|
Run `pm2 save` afterwards if you want the process list to survive reboots.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
Powered by Vitest:
|
||||||
|
```bash
|
||||||
|
bun run test
|
||||||
|
bun x vitest --watch
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
- Keep at least 50–100 USDT in the account before deploying a live strategy.
|
||||||
|
- Configure leverage on the exchange manually (~50x is recommended); the bot will not change it.
|
||||||
|
- Ensure your server or workstation clock is in sync to avoid signature errors.
|
||||||
|
- Accounts must run in one-way position mode.
|
||||||
|
- **Env not loading**: make sure `.env` lives in the repo root and variable names are spelled correctly.
|
||||||
|
- **Permission rejected**: confirm the API key has perpetual trading scopes enabled.
|
||||||
|
- **Precision errors**: align `PRICE_TICK`, `QTY_STEP`, and `TRADE_SYMBOL` with the exchange filters.
|
||||||
|
See [simple-readme.md](simple-readme.md) for more detailed walkthroughs.
|
||||||
|
|
||||||
|
## Community & Support
|
||||||
|
- Telegram: [https://t.me/+4fdo0quY87o4Mjhh](https://t.me/+4fdo0quY87o4Mjhh)
|
||||||
|
- Issues and PRs are welcome for bug reports and feature requests
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
Algorithmic trading carries risk. Validate strategies with paper trading or small capital first, safeguard your API keys, and only grant the minimum required permissions.
|
||||||
@@ -5,11 +5,17 @@
|
|||||||
"name": "ritmex-bot",
|
"name": "ritmex-bot",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@grvt/client": "^1.6.4",
|
"@grvt/client": "^1.6.4",
|
||||||
|
"@noble/ed25519": "^3.0.0",
|
||||||
|
"@x10xchange/stark-crypto-wrapper-wasm": "^0.2.0",
|
||||||
"axios": "^1.12.2",
|
"axios": "^1.12.2",
|
||||||
"ccxt": "^4.5.5",
|
"bignumber.js": "^9.3.1",
|
||||||
|
"ccxt": "^4.5.12",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
"dotenv": "^17.2.2",
|
"dotenv": "^17.2.2",
|
||||||
|
"ethereum-cryptography": "^2.1.3",
|
||||||
"ink": "^6.3.1",
|
"ink": "^6.3.1",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
|
"starknet": "^8.5.4",
|
||||||
"ws": "^8.18.3",
|
"ws": "^8.18.3",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -80,6 +86,12 @@
|
|||||||
|
|
||||||
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||||
|
|
||||||
|
"@noble/curves": ["@noble/curves@1.4.2", "", { "dependencies": { "@noble/hashes": "1.4.0" } }, "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw=="],
|
||||||
|
|
||||||
|
"@noble/ed25519": ["@noble/ed25519@3.0.0", "", {}, "sha512-QyteqMNm0GLqfa5SoYbSC3+Pvykwpn95Zgth4MFVSMKBB75ELl9tX1LAVsN4c3HXOrakHsF2gL4zWDAYCcsnzg=="],
|
||||||
|
|
||||||
|
"@noble/hashes": ["@noble/hashes@1.4.0", "", {}, "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg=="],
|
||||||
|
|
||||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.0", "", { "os": "android", "cpu": "arm" }, "sha512-VxDYCDqOaR7NXzAtvRx7G1u54d2kEHopb28YH/pKzY6y0qmogP3gG7CSiWsq9WvDFxOQMpNEyjVAHZFXfH3o/A=="],
|
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.0", "", { "os": "android", "cpu": "arm" }, "sha512-VxDYCDqOaR7NXzAtvRx7G1u54d2kEHopb28YH/pKzY6y0qmogP3gG7CSiWsq9WvDFxOQMpNEyjVAHZFXfH3o/A=="],
|
||||||
|
|
||||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.0", "", { "os": "android", "cpu": "arm64" }, "sha512-pqDirm8koABIKvzL59YI9W9DWbRlTX7RWhN+auR8HXJxo89m4mjqbah7nJZjeKNTNYopqL+yGg+0mhCpf3xZtQ=="],
|
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.0", "", { "os": "android", "cpu": "arm64" }, "sha512-pqDirm8koABIKvzL59YI9W9DWbRlTX7RWhN+auR8HXJxo89m4mjqbah7nJZjeKNTNYopqL+yGg+0mhCpf3xZtQ=="],
|
||||||
@@ -124,6 +136,18 @@
|
|||||||
|
|
||||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.0", "", { "os": "win32", "cpu": "x64" }, "sha512-6iKDCVSIUQ8jPMoIV0OytRKniaYyy5EbY/RRydmLW8ZR3cEBhxbWl5ro0rkUNe0ef6sScvhbY79HrjRm8i3vDQ=="],
|
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.0", "", { "os": "win32", "cpu": "x64" }, "sha512-6iKDCVSIUQ8jPMoIV0OytRKniaYyy5EbY/RRydmLW8ZR3cEBhxbWl5ro0rkUNe0ef6sScvhbY79HrjRm8i3vDQ=="],
|
||||||
|
|
||||||
|
"@scure/base": ["@scure/base@1.2.6", "", {}, "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg=="],
|
||||||
|
|
||||||
|
"@scure/bip32": ["@scure/bip32@1.4.0", "", { "dependencies": { "@noble/curves": "~1.4.0", "@noble/hashes": "~1.4.0", "@scure/base": "~1.1.6" } }, "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg=="],
|
||||||
|
|
||||||
|
"@scure/bip39": ["@scure/bip39@1.3.0", "", { "dependencies": { "@noble/hashes": "~1.4.0", "@scure/base": "~1.1.6" } }, "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ=="],
|
||||||
|
|
||||||
|
"@scure/starknet": ["@scure/starknet@1.1.0", "", { "dependencies": { "@noble/curves": "~1.7.0", "@noble/hashes": "~1.6.0" } }, "sha512-83g3M6Ix2qRsPN4wqLDqiRZ2GBNbjVWfboJE/9UjfG+MHr6oDSu/CWgy8hsBSJejr09DkkL+l0Ze4KVrlCIdtQ=="],
|
||||||
|
|
||||||
|
"@starknet-io/starknet-types-08": ["@starknet-io/types-js@0.8.4", "", {}, "sha512-0RZ3TZHcLsUTQaq1JhDSCM8chnzO4/XNsSCozwDET64JK5bjFDIf2ZUkta+tl5Nlbf4usoU7uZiDI/Q57kt2SQ=="],
|
||||||
|
|
||||||
|
"@starknet-io/starknet-types-09": ["@starknet-io/types-js@0.9.2", "", {}, "sha512-vWOc0FVSn+RmabozIEWcEny1I73nDGTvOrLYJsR1x7LGA3AZmqt4i/aW69o/3i2NN5CVP8Ok6G1ayRQJKye3Wg=="],
|
||||||
|
|
||||||
"@types/bun": ["@types/bun@1.2.22", "", { "dependencies": { "bun-types": "1.2.22" } }, "sha512-5A/KrKos2ZcN0c6ljRSOa1fYIyCKhZfIVYeuyb4snnvomnpFqC0tTsEkdqNxbAgExV384OETQ//WAjl3XbYqQA=="],
|
"@types/bun": ["@types/bun@1.2.22", "", { "dependencies": { "bun-types": "1.2.22" } }, "sha512-5A/KrKos2ZcN0c6ljRSOa1fYIyCKhZfIVYeuyb4snnvomnpFqC0tTsEkdqNxbAgExV384OETQ//WAjl3XbYqQA=="],
|
||||||
|
|
||||||
"@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="],
|
"@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="],
|
||||||
@@ -154,12 +178,18 @@
|
|||||||
|
|
||||||
"@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="],
|
"@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="],
|
||||||
|
|
||||||
|
"@x10xchange/stark-crypto-wrapper-wasm": ["@x10xchange/stark-crypto-wrapper-wasm@0.2.0", "", {}, "sha512-pi7cHvDgPf9Ri9q0cvrUnCnucafB+F3ch7ia0oEJhKMY+bWvxWRYA2vve9s6UMMNV6PnmTyFZPOYptVGGdLTWA=="],
|
||||||
|
|
||||||
|
"abi-wan-kanabi": ["abi-wan-kanabi@2.2.4", "", { "dependencies": { "ansicolors": "^0.3.2", "cardinal": "^2.1.1", "fs-extra": "^10.0.0", "yargs": "^17.7.2" }, "bin": { "generate": "dist/generate.js" } }, "sha512-0aA81FScmJCPX+8UvkXLki3X1+yPQuWxEkqXBVKltgPAK79J+NB+Lp5DouMXa7L6f+zcRlIA/6XO7BN/q9fnvg=="],
|
||||||
|
|
||||||
"ansi-escapes": ["ansi-escapes@7.1.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-YdhtCd19sKRKfAAUsrcC1wzm4JuzJoiX4pOJqIoW2qmKj5WzG/dL8uUJ0361zaXtHqK7gEhOwtAtz7t3Yq3X5g=="],
|
"ansi-escapes": ["ansi-escapes@7.1.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-YdhtCd19sKRKfAAUsrcC1wzm4JuzJoiX4pOJqIoW2qmKj5WzG/dL8uUJ0361zaXtHqK7gEhOwtAtz7t3Yq3X5g=="],
|
||||||
|
|
||||||
"ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
"ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||||
|
|
||||||
"ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
"ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||||
|
|
||||||
|
"ansicolors": ["ansicolors@0.3.2", "", {}, "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg=="],
|
||||||
|
|
||||||
"assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="],
|
"assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="],
|
||||||
|
|
||||||
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||||
@@ -168,13 +198,17 @@
|
|||||||
|
|
||||||
"axios": ["axios@1.12.2", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw=="],
|
"axios": ["axios@1.12.2", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw=="],
|
||||||
|
|
||||||
|
"bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.2.22", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-hwaAu8tct/Zn6Zft4U9BsZcXkYomzpHJX28ofvx7k0Zz2HNz54n1n+tDgxoWFGB4PcFvJXJQloPhaV2eP3Q6EA=="],
|
"bun-types": ["bun-types@1.2.22", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-hwaAu8tct/Zn6Zft4U9BsZcXkYomzpHJX28ofvx7k0Zz2HNz54n1n+tDgxoWFGB4PcFvJXJQloPhaV2eP3Q6EA=="],
|
||||||
|
|
||||||
"cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
|
"cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
|
||||||
|
|
||||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||||
|
|
||||||
"ccxt": ["ccxt@4.5.5", "", { "dependencies": { "ws": "^8.8.1" } }, "sha512-AyhwTFLkx4sO985ImIOfumEBox7AHD/iqk5tPGICObUSZG6wTXg0aRzU8Hjz974aCMG4msFwLk3A/iXPKAU4wA=="],
|
"cardinal": ["cardinal@2.1.1", "", { "dependencies": { "ansicolors": "~0.3.2", "redeyed": "~2.1.0" }, "bin": { "cdl": "./bin/cdl.js" } }, "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw=="],
|
||||||
|
|
||||||
|
"ccxt": ["ccxt@4.5.12", "", { "dependencies": { "ws": "^8.8.1" } }, "sha512-2lfL2TKjq4vBkQUQWJfDqFywhvYCZmk9r0SWC8GqA4AHZ6qozKVUJowxQTvdRsLX9jBwYSE0nc7JVurBrQ6SHg=="],
|
||||||
|
|
||||||
"chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="],
|
"chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="],
|
||||||
|
|
||||||
@@ -188,14 +222,22 @@
|
|||||||
|
|
||||||
"cli-truncate": ["cli-truncate@4.0.0", "", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^7.0.0" } }, "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA=="],
|
"cli-truncate": ["cli-truncate@4.0.0", "", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^7.0.0" } }, "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA=="],
|
||||||
|
|
||||||
|
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
|
||||||
|
|
||||||
"code-excerpt": ["code-excerpt@4.0.0", "", { "dependencies": { "convert-to-spaces": "^2.0.1" } }, "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA=="],
|
"code-excerpt": ["code-excerpt@4.0.0", "", { "dependencies": { "convert-to-spaces": "^2.0.1" } }, "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA=="],
|
||||||
|
|
||||||
|
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||||
|
|
||||||
|
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||||
|
|
||||||
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
||||||
|
|
||||||
"convert-to-spaces": ["convert-to-spaces@2.0.1", "", {}, "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ=="],
|
"convert-to-spaces": ["convert-to-spaces@2.0.1", "", {}, "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ=="],
|
||||||
|
|
||||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||||
|
|
||||||
|
"date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="],
|
||||||
|
|
||||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||||
|
|
||||||
"deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
|
"deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
|
||||||
@@ -224,10 +266,16 @@
|
|||||||
|
|
||||||
"esbuild": ["esbuild@0.25.10", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.10", "@esbuild/android-arm": "0.25.10", "@esbuild/android-arm64": "0.25.10", "@esbuild/android-x64": "0.25.10", "@esbuild/darwin-arm64": "0.25.10", "@esbuild/darwin-x64": "0.25.10", "@esbuild/freebsd-arm64": "0.25.10", "@esbuild/freebsd-x64": "0.25.10", "@esbuild/linux-arm": "0.25.10", "@esbuild/linux-arm64": "0.25.10", "@esbuild/linux-ia32": "0.25.10", "@esbuild/linux-loong64": "0.25.10", "@esbuild/linux-mips64el": "0.25.10", "@esbuild/linux-ppc64": "0.25.10", "@esbuild/linux-riscv64": "0.25.10", "@esbuild/linux-s390x": "0.25.10", "@esbuild/linux-x64": "0.25.10", "@esbuild/netbsd-arm64": "0.25.10", "@esbuild/netbsd-x64": "0.25.10", "@esbuild/openbsd-arm64": "0.25.10", "@esbuild/openbsd-x64": "0.25.10", "@esbuild/openharmony-arm64": "0.25.10", "@esbuild/sunos-x64": "0.25.10", "@esbuild/win32-arm64": "0.25.10", "@esbuild/win32-ia32": "0.25.10", "@esbuild/win32-x64": "0.25.10" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ=="],
|
"esbuild": ["esbuild@0.25.10", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.10", "@esbuild/android-arm": "0.25.10", "@esbuild/android-arm64": "0.25.10", "@esbuild/android-x64": "0.25.10", "@esbuild/darwin-arm64": "0.25.10", "@esbuild/darwin-x64": "0.25.10", "@esbuild/freebsd-arm64": "0.25.10", "@esbuild/freebsd-x64": "0.25.10", "@esbuild/linux-arm": "0.25.10", "@esbuild/linux-arm64": "0.25.10", "@esbuild/linux-ia32": "0.25.10", "@esbuild/linux-loong64": "0.25.10", "@esbuild/linux-mips64el": "0.25.10", "@esbuild/linux-ppc64": "0.25.10", "@esbuild/linux-riscv64": "0.25.10", "@esbuild/linux-s390x": "0.25.10", "@esbuild/linux-x64": "0.25.10", "@esbuild/netbsd-arm64": "0.25.10", "@esbuild/netbsd-x64": "0.25.10", "@esbuild/openbsd-arm64": "0.25.10", "@esbuild/openbsd-x64": "0.25.10", "@esbuild/openharmony-arm64": "0.25.10", "@esbuild/sunos-x64": "0.25.10", "@esbuild/win32-arm64": "0.25.10", "@esbuild/win32-ia32": "0.25.10", "@esbuild/win32-x64": "0.25.10" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ=="],
|
||||||
|
|
||||||
|
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||||
|
|
||||||
"escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="],
|
"escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="],
|
||||||
|
|
||||||
|
"esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
|
||||||
|
|
||||||
"estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
|
"estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
|
||||||
|
|
||||||
|
"ethereum-cryptography": ["ethereum-cryptography@2.2.1", "", { "dependencies": { "@noble/curves": "1.4.2", "@noble/hashes": "1.4.0", "@scure/bip32": "1.4.0", "@scure/bip39": "1.3.0" } }, "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg=="],
|
||||||
|
|
||||||
"expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="],
|
"expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="],
|
||||||
|
|
||||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||||
@@ -236,10 +284,14 @@
|
|||||||
|
|
||||||
"form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="],
|
"form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="],
|
||||||
|
|
||||||
|
"fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="],
|
||||||
|
|
||||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||||
|
|
||||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||||
|
|
||||||
|
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
|
||||||
|
|
||||||
"get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
"get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
||||||
|
|
||||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||||
@@ -248,6 +300,8 @@
|
|||||||
|
|
||||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||||
|
|
||||||
|
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||||
|
|
||||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||||
|
|
||||||
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
||||||
@@ -264,6 +318,10 @@
|
|||||||
|
|
||||||
"js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],
|
"js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],
|
||||||
|
|
||||||
|
"jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="],
|
||||||
|
|
||||||
|
"lossless-json": ["lossless-json@4.3.0", "", {}, "sha512-ToxOC+SsduRmdSuoLZLYAr5zy1Qu7l5XhmPWM3zefCZ5IcrzW/h108qbJUKfOlDlhvhjUK84+8PSVX0kxnit0g=="],
|
||||||
|
|
||||||
"loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="],
|
"loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="],
|
||||||
|
|
||||||
"magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="],
|
"magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="],
|
||||||
@@ -282,6 +340,8 @@
|
|||||||
|
|
||||||
"onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
|
"onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
|
||||||
|
|
||||||
|
"pako": ["pako@2.1.0", "", {}, "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="],
|
||||||
|
|
||||||
"patch-console": ["patch-console@2.0.0", "", {}, "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA=="],
|
"patch-console": ["patch-console@2.0.0", "", {}, "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA=="],
|
||||||
|
|
||||||
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
@@ -300,6 +360,10 @@
|
|||||||
|
|
||||||
"react-reconciler": ["react-reconciler@0.32.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ=="],
|
"react-reconciler": ["react-reconciler@0.32.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ=="],
|
||||||
|
|
||||||
|
"redeyed": ["redeyed@2.1.1", "", { "dependencies": { "esprima": "~4.0.0" } }, "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ=="],
|
||||||
|
|
||||||
|
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
|
||||||
|
|
||||||
"restore-cursor": ["restore-cursor@4.0.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="],
|
"restore-cursor": ["restore-cursor@4.0.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="],
|
||||||
|
|
||||||
"rollup": ["rollup@4.52.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.0", "@rollup/rollup-android-arm64": "4.52.0", "@rollup/rollup-darwin-arm64": "4.52.0", "@rollup/rollup-darwin-x64": "4.52.0", "@rollup/rollup-freebsd-arm64": "4.52.0", "@rollup/rollup-freebsd-x64": "4.52.0", "@rollup/rollup-linux-arm-gnueabihf": "4.52.0", "@rollup/rollup-linux-arm-musleabihf": "4.52.0", "@rollup/rollup-linux-arm64-gnu": "4.52.0", "@rollup/rollup-linux-arm64-musl": "4.52.0", "@rollup/rollup-linux-loong64-gnu": "4.52.0", "@rollup/rollup-linux-ppc64-gnu": "4.52.0", "@rollup/rollup-linux-riscv64-gnu": "4.52.0", "@rollup/rollup-linux-riscv64-musl": "4.52.0", "@rollup/rollup-linux-s390x-gnu": "4.52.0", "@rollup/rollup-linux-x64-gnu": "4.52.0", "@rollup/rollup-linux-x64-musl": "4.52.0", "@rollup/rollup-openharmony-arm64": "4.52.0", "@rollup/rollup-win32-arm64-msvc": "4.52.0", "@rollup/rollup-win32-ia32-msvc": "4.52.0", "@rollup/rollup-win32-x64-gnu": "4.52.0", "@rollup/rollup-win32-x64-msvc": "4.52.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-+IuescNkTJQgX7AkIDtITipZdIGcWF0pnVvZTWStiazUmcGA2ag8dfg0urest2XlXUi9kuhfQ+qmdc5Stc3z7g=="],
|
"rollup": ["rollup@4.52.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.0", "@rollup/rollup-android-arm64": "4.52.0", "@rollup/rollup-darwin-arm64": "4.52.0", "@rollup/rollup-darwin-x64": "4.52.0", "@rollup/rollup-freebsd-arm64": "4.52.0", "@rollup/rollup-freebsd-x64": "4.52.0", "@rollup/rollup-linux-arm-gnueabihf": "4.52.0", "@rollup/rollup-linux-arm-musleabihf": "4.52.0", "@rollup/rollup-linux-arm64-gnu": "4.52.0", "@rollup/rollup-linux-arm64-musl": "4.52.0", "@rollup/rollup-linux-loong64-gnu": "4.52.0", "@rollup/rollup-linux-ppc64-gnu": "4.52.0", "@rollup/rollup-linux-riscv64-gnu": "4.52.0", "@rollup/rollup-linux-riscv64-musl": "4.52.0", "@rollup/rollup-linux-s390x-gnu": "4.52.0", "@rollup/rollup-linux-x64-gnu": "4.52.0", "@rollup/rollup-linux-x64-musl": "4.52.0", "@rollup/rollup-openharmony-arm64": "4.52.0", "@rollup/rollup-win32-arm64-msvc": "4.52.0", "@rollup/rollup-win32-ia32-msvc": "4.52.0", "@rollup/rollup-win32-x64-gnu": "4.52.0", "@rollup/rollup-win32-x64-msvc": "4.52.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-+IuescNkTJQgX7AkIDtITipZdIGcWF0pnVvZTWStiazUmcGA2ag8dfg0urest2XlXUi9kuhfQ+qmdc5Stc3z7g=="],
|
||||||
@@ -318,6 +382,8 @@
|
|||||||
|
|
||||||
"stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
|
"stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
|
||||||
|
|
||||||
|
"starknet": ["starknet@8.9.0", "", { "dependencies": { "@noble/curves": "~1.7.0", "@noble/hashes": "~1.6.0", "@scure/base": "~1.2.1", "@scure/starknet": "1.1.0", "@starknet-io/starknet-types-08": "npm:@starknet-io/types-js@~0.8.4", "@starknet-io/starknet-types-09": "npm:@starknet-io/types-js@~0.9.1", "abi-wan-kanabi": "2.2.4", "lossless-json": "^4.2.0", "pako": "^2.0.4", "ts-mixer": "^6.0.3" } }, "sha512-uhXSCKW0SMBGckd4yVlscVz4DMkQqZ+/RTpfxZ5J46e6AuCUHOqGKv61tqgUP9GhdvgHV/XyN+kdmlyZn3rXjA=="],
|
||||||
|
|
||||||
"std-env": ["std-env@3.9.0", "", {}, "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw=="],
|
"std-env": ["std-env@3.9.0", "", {}, "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw=="],
|
||||||
|
|
||||||
"string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
"string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
||||||
@@ -338,12 +404,16 @@
|
|||||||
|
|
||||||
"tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="],
|
"tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="],
|
||||||
|
|
||||||
|
"ts-mixer": ["ts-mixer@6.0.4", "", {}, "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="],
|
||||||
|
|
||||||
"type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
|
"type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
|
||||||
|
|
||||||
"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
|
"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
|
||||||
|
|
||||||
"undici-types": ["undici-types@7.12.0", "", {}, "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ=="],
|
"undici-types": ["undici-types@7.12.0", "", {}, "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ=="],
|
||||||
|
|
||||||
|
"universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
|
||||||
|
|
||||||
"vite": ["vite@7.1.7", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA=="],
|
"vite": ["vite@7.1.7", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA=="],
|
||||||
|
|
||||||
"vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="],
|
"vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="],
|
||||||
@@ -358,10 +428,56 @@
|
|||||||
|
|
||||||
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
||||||
|
|
||||||
|
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
||||||
|
|
||||||
|
"yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
|
||||||
|
|
||||||
|
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
|
||||||
|
|
||||||
"yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="],
|
"yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="],
|
||||||
|
|
||||||
|
"@scure/bip32/@scure/base": ["@scure/base@1.1.9", "", {}, "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg=="],
|
||||||
|
|
||||||
|
"@scure/bip39/@scure/base": ["@scure/base@1.1.9", "", {}, "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg=="],
|
||||||
|
|
||||||
|
"@scure/starknet/@noble/curves": ["@noble/curves@1.7.0", "", { "dependencies": { "@noble/hashes": "1.6.0" } }, "sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw=="],
|
||||||
|
|
||||||
|
"@scure/starknet/@noble/hashes": ["@noble/hashes@1.6.1", "", {}, "sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w=="],
|
||||||
|
|
||||||
"cli-truncate/slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="],
|
"cli-truncate/slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="],
|
||||||
|
|
||||||
|
"cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||||
|
|
||||||
|
"cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||||
|
|
||||||
|
"cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||||
|
|
||||||
|
"starknet/@noble/curves": ["@noble/curves@1.7.0", "", { "dependencies": { "@noble/hashes": "1.6.0" } }, "sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw=="],
|
||||||
|
|
||||||
|
"starknet/@noble/hashes": ["@noble/hashes@1.6.1", "", {}, "sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w=="],
|
||||||
|
|
||||||
|
"yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||||
|
|
||||||
|
"@scure/starknet/@noble/curves/@noble/hashes": ["@noble/hashes@1.6.0", "", {}, "sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ=="],
|
||||||
|
|
||||||
"cli-truncate/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="],
|
"cli-truncate/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="],
|
||||||
|
|
||||||
|
"cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||||
|
|
||||||
|
"cliui/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||||
|
|
||||||
|
"cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
|
|
||||||
|
"cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||||
|
|
||||||
|
"starknet/@noble/curves/@noble/hashes": ["@noble/hashes@1.6.0", "", {}, "sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ=="],
|
||||||
|
|
||||||
|
"yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||||
|
|
||||||
|
"yargs/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||||
|
|
||||||
|
"yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||||
|
|
||||||
|
"yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,51 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CCXT Basic example for the browser</title>
|
||||||
|
<script type="text/javascript" src="https://unpkg.com/ccxt"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
document.addEventListener ("DOMContentLoaded", function () {
|
||||||
|
|
||||||
|
const pollTickerContinuously = async function (exchange, symbol) {
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const ticker = await exchange.watchTicker (symbol)
|
||||||
|
|
||||||
|
const text = [
|
||||||
|
exchange.id,
|
||||||
|
symbol,
|
||||||
|
JSON.stringify (ticker, undefined, '\n\t')
|
||||||
|
]
|
||||||
|
|
||||||
|
document.getElementById ('content').innerHTML = text.join (' ');
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
|
||||||
|
const text = [
|
||||||
|
e.constructor.name,
|
||||||
|
e.message,
|
||||||
|
]
|
||||||
|
|
||||||
|
document.getElementById ('content').innerHTML = text.join (' ');
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const exchange = new ccxt.pro.binance ({ enableRateLimit: true })
|
||||||
|
const symbol = 'ETH/BTC'
|
||||||
|
|
||||||
|
pollTickerContinuously (exchange, symbol)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Hello, CCXT!</h1>
|
||||||
|
<p>This example uses websockets to watch changes in price of ETH/BTC</p>
|
||||||
|
<pre id="content"></pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import ccxt from '../../../js/ccxt.js';
|
||||||
|
|
||||||
|
// your version must be 0.7+
|
||||||
|
console.log ('CCXT Version:', ccxt.version)
|
||||||
|
|
||||||
|
function handle (exchange, symbol, ticker) {
|
||||||
|
console.log (new Date (), exchange.id, symbol, ticker)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loop (exchange, symbol) {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const ticker = await exchange.watchMyTrades ()
|
||||||
|
handle (exchange, symbol, ticker)
|
||||||
|
sleep( 10000 )
|
||||||
|
} catch (e) {
|
||||||
|
console.log (symbol, e)
|
||||||
|
// do nothing and retry on next loop iteration
|
||||||
|
// throw e // uncomment to break all loops in case of an error in any one of them
|
||||||
|
// break // you can also break just this one loop if it fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
|
||||||
|
const exchange = new ccxt.pro.apex ({
|
||||||
|
'apiKey': 'your api Key',
|
||||||
|
'secret': 'your api secret',
|
||||||
|
'walletAddress': 'your eth address',
|
||||||
|
'options': {
|
||||||
|
'accountId': 'your account id',
|
||||||
|
'passphrase': 'your api passphrase',
|
||||||
|
'seeds': 'your zklink omni seed',
|
||||||
|
'brokerId': '',
|
||||||
|
},
|
||||||
|
}) // usd(s)-margined contracts
|
||||||
|
exchange.setSandboxMode(true)
|
||||||
|
//
|
||||||
|
// or
|
||||||
|
//
|
||||||
|
// const exchange = new ccxt.pro.binance () // spot markets
|
||||||
|
//
|
||||||
|
// WARNING: when using the spot markets mind subscription limits!
|
||||||
|
// don't attempt to subscribe to all of them
|
||||||
|
// the exchanges will not allow that in general
|
||||||
|
// instead, specify a shorter list of symbols to subscribe to
|
||||||
|
//
|
||||||
|
// or
|
||||||
|
//
|
||||||
|
// const exchange = new ccxt.pro.binancecoinm () // coin-margined contracts
|
||||||
|
|
||||||
|
if (exchange.has['watchTicker']) {
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
// many symbols
|
||||||
|
//await Promise.all (exchange.symbols.map (symbol => loop (exchange, symbol)))
|
||||||
|
//
|
||||||
|
// or
|
||||||
|
//
|
||||||
|
// const symbols = [ 'BTC/USDT', 'ETH/USDT' ] // specific symbols
|
||||||
|
// await Promise.all (symbols.map (symbol => loop (exchange, symbol)))
|
||||||
|
//
|
||||||
|
// or
|
||||||
|
//
|
||||||
|
await loop (exchange, ['BTC-USDT','ETH-USDT']) // one symbol
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log (exchange.id, 'does not support watchTicker yet')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main ()
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('../../../ccxt')
|
||||||
|
|
||||||
|
console.log ('CCXT Version:', ccxt.version)
|
||||||
|
|
||||||
|
// This example will run silent and will return your balance only when the balance is updated.
|
||||||
|
//
|
||||||
|
// 1. launch the example with your keys and keep it running
|
||||||
|
// 2. go to the trading section on the website
|
||||||
|
// 3. place a order on a spot market
|
||||||
|
// 4. see your balance updated in the example
|
||||||
|
//
|
||||||
|
// Warning! This example might produce a lot of output to your screen
|
||||||
|
|
||||||
|
|
||||||
|
async function watchBalance (exchange) {
|
||||||
|
let balance = await exchange.fetchBalance ()
|
||||||
|
console.log ('---------------------------------------------------------')
|
||||||
|
console.log (exchange.iso8601 (exchange.milliseconds ()))
|
||||||
|
console.log (balance, '\n')
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const update = await exchange.watchBalance ()
|
||||||
|
balance = exchange.deep_extend (balance, update)
|
||||||
|
// it will print the balance update when the balance changes
|
||||||
|
// if the balance remains unchanged the exchange will not send it
|
||||||
|
console.log ('---------------------------------------------------------')
|
||||||
|
console.log (exchange.iso8601 (exchange.milliseconds ()))
|
||||||
|
console.log (balance, '\n')
|
||||||
|
} catch (e) {
|
||||||
|
console.log (e.constructor.name, e.message)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
|
||||||
|
const exchange = new ccxt.pro.binance ({
|
||||||
|
'apiKey': 'YOUR_API_KEY',
|
||||||
|
'secret': 'YOUR_SECRET',
|
||||||
|
})
|
||||||
|
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
|
||||||
|
// exchange.verbose = true // uncomment for debugging purposes if necessary
|
||||||
|
|
||||||
|
await watchBalance (exchange)
|
||||||
|
|
||||||
|
await exchange.close ()
|
||||||
|
}
|
||||||
|
|
||||||
|
main ()
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('../../../ccxt')
|
||||||
|
|
||||||
|
console.log ('CCXT Version:', ccxt.version)
|
||||||
|
|
||||||
|
let HttpsProxyAgent = undefined
|
||||||
|
|
||||||
|
try {
|
||||||
|
HttpsProxyAgent = require ('https-proxy-agent')
|
||||||
|
} catch (e) {
|
||||||
|
console.log (e.constructor.name, e.message)
|
||||||
|
console.log ("\nCould not load the HTTPS proxy agent")
|
||||||
|
console.log ("\nPlease, run this command to make sure it's properly installed:")
|
||||||
|
console.log ("\nnpm install https-proxy-agent\n")
|
||||||
|
process.exit ()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
|
||||||
|
console.log ('Using proxy server', httpsProxyUrl);
|
||||||
|
|
||||||
|
// adjust for your HTTPS proxy URL
|
||||||
|
const httpsProxyUrl = process.env.https_proxy || 'https://username:password@your-proxy.com'
|
||||||
|
, httpsAgent = new HttpsProxyAgent (httpsProxyUrl)
|
||||||
|
, exchange = new ccxt.binance ({
|
||||||
|
httpsAgent: httpsAgent, // ←--------------------- httpsAgent here
|
||||||
|
options: {
|
||||||
|
'ws': {
|
||||||
|
'options': { agent: httpsAgent }, // ←--- httpsAgent here
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const symbol = 'BTC/USDT'
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
console.log ('Markets loaded')
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const orderbook = await exchange.watchOrderBook (symbol)
|
||||||
|
console.log (exchange.iso8601 (exchange.milliseconds()), symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
} catch (e) {
|
||||||
|
console.log (e.constructor.name, e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main ()
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const ccxt = require ('ccxt')
|
||||||
|
|
||||||
|
const ohlcvsBySymbol = {}
|
||||||
|
|
||||||
|
function handleAllOHLCVs (exchange, ohlcvs, symbol, timeframe) {
|
||||||
|
const now = exchange.iso8601 (exchange.milliseconds ())
|
||||||
|
const lastCandle = exchange.safeValue (ohlcvs, ohlcvs.length - 1)
|
||||||
|
const datetime = exchange.iso8601 (lastCandle[0])
|
||||||
|
console.log (now, datetime, symbol, timeframe, lastCandle.slice (1))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pollOHLCV (exchange, symbol, timeframe) {
|
||||||
|
await exchange.throttle (1000) // 1000ms delay between subscriptions
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const response = await exchange.watchOHLCV (symbol, timeframe)
|
||||||
|
ohlcvsBySymbol[symbol] = response
|
||||||
|
handleAllOHLCVs(exchange, response, symbol, timeframe)
|
||||||
|
} catch (e) {
|
||||||
|
console.log (e.constructor.name, e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
|
||||||
|
const exchange = new ccxt.pro.binance()
|
||||||
|
const markets = await exchange.loadMarkets ()
|
||||||
|
const timeframe = '5m'
|
||||||
|
|
||||||
|
const firstOneHundredSymbols = exchange.symbols.slice (0, 100)
|
||||||
|
|
||||||
|
await Promise.all (firstOneHundredSymbols.map (symbol => pollOHLCV (exchange, symbol, timeframe)))
|
||||||
|
}
|
||||||
|
|
||||||
|
main ()
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('ccxt');
|
||||||
|
|
||||||
|
// your version must be 0.7+
|
||||||
|
console.log ('CCXT Version:', ccxt.version)
|
||||||
|
|
||||||
|
function handle (exchange, symbol, timeframe, candles) {
|
||||||
|
const lastCandle = candles[candles.length - 1]
|
||||||
|
const lastClosingPrice = lastCandle[4]
|
||||||
|
console.log (new Date (), exchange.id, timeframe, symbol, '\t', lastClosingPrice)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loop (exchange, symbol, timeframe) {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const candles = await exchange.watchOHLCV (symbol, timeframe)
|
||||||
|
handle (exchange, symbol, timeframe, candles)
|
||||||
|
} catch (e) {
|
||||||
|
console.log (symbol, e)
|
||||||
|
// do nothing and retry on next loop iteration
|
||||||
|
// throw e // uncomment to break all loops in case of an error in any one of them
|
||||||
|
// break // you can also break just this one loop if it fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
|
||||||
|
const exchange = new ccxt.pro.binanceusdm () // usd(s)-margined contracts
|
||||||
|
//
|
||||||
|
// or
|
||||||
|
//
|
||||||
|
// const exchange = new ccxt.pro.binance () // spot markets
|
||||||
|
//
|
||||||
|
// WARNING: when using the spot markets mind subscription limits!
|
||||||
|
// don't attempt to subscribe to all of them
|
||||||
|
// the exchanges will not allow that in general
|
||||||
|
// instead, specify a shorter list of symbols to subscribe to
|
||||||
|
//
|
||||||
|
// or
|
||||||
|
//
|
||||||
|
// const exchange = new ccxt.pro.binancecoinm () // coin-margined contracts
|
||||||
|
|
||||||
|
if (exchange.has['watchOHLCV']) {
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
const timeframe = '15m'
|
||||||
|
// many symbols
|
||||||
|
await Promise.all (exchange.symbols.map (symbol => loop (exchange, symbol, timeframe)))
|
||||||
|
//
|
||||||
|
// or
|
||||||
|
//
|
||||||
|
// const symbols = [ 'BTC/USDT', 'ETH/USDT' ] // specific symbols
|
||||||
|
// await Promise.all (symbols.map (symbol => loop (exchange, symbol, timeframe)))
|
||||||
|
//
|
||||||
|
// or
|
||||||
|
//
|
||||||
|
// await loop (exchange, 'BTC/USDT', timeframe) // one symbol
|
||||||
|
} else {
|
||||||
|
console.log (exchange.id, 'does not support watchOHLCV yet')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main ()
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('ccxt');
|
||||||
|
|
||||||
|
// your version must be 0.7+
|
||||||
|
console.log ('CCXT Version:', ccxt.version)
|
||||||
|
|
||||||
|
function handle (exchange, symbol, ticker) {
|
||||||
|
console.log (new Date (), exchange.id, symbol, ticker['last'])
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loop (exchange, symbol) {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const ticker = await exchange.watchTicker (symbol)
|
||||||
|
handle (exchange, symbol, ticker)
|
||||||
|
} catch (e) {
|
||||||
|
console.log (symbol, e)
|
||||||
|
// do nothing and retry on next loop iteration
|
||||||
|
// throw e // uncomment to break all loops in case of an error in any one of them
|
||||||
|
// break // you can also break just this one loop if it fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
|
||||||
|
const exchange = new ccxt.pro.binanceusdm () // usd(s)-margined contracts
|
||||||
|
//
|
||||||
|
// or
|
||||||
|
//
|
||||||
|
// const exchange = new ccxt.pro.binance () // spot markets
|
||||||
|
//
|
||||||
|
// WARNING: when using the spot markets mind subscription limits!
|
||||||
|
// don't attempt to subscribe to all of them
|
||||||
|
// the exchanges will not allow that in general
|
||||||
|
// instead, specify a shorter list of symbols to subscribe to
|
||||||
|
//
|
||||||
|
// or
|
||||||
|
//
|
||||||
|
// const exchange = new ccxt.pro.binancecoinm () // coin-margined contracts
|
||||||
|
|
||||||
|
if (exchange.has['watchTicker']) {
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
// many symbols
|
||||||
|
await Promise.all (exchange.symbols.map (symbol => loop (exchange, symbol)))
|
||||||
|
//
|
||||||
|
// or
|
||||||
|
//
|
||||||
|
// const symbols = [ 'BTC/USDT', 'ETH/USDT' ] // specific symbols
|
||||||
|
// await Promise.all (symbols.map (symbol => loop (exchange, symbol)))
|
||||||
|
//
|
||||||
|
// or
|
||||||
|
//
|
||||||
|
// await loop (exchange, 'BTC/USDT') // one symbol
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log (exchange.id, 'does not support watchTicker yet')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main ()
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('ccxt');
|
||||||
|
const asTable = require ('as-table').configure ({ delimiter: ' | ' })
|
||||||
|
|
||||||
|
console.log ('CCXT Version:', ccxt.version)
|
||||||
|
|
||||||
|
|
||||||
|
async function loop (exchange, symbol, timeframe, completeCandlesOnly = false) {
|
||||||
|
const durationInSeconds = exchange.parseTimeframe (timeframe)
|
||||||
|
const durationInMs = durationInSeconds * 1000
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const trades = await exchange.watchTrades (symbol)
|
||||||
|
if (trades.length > 0) {
|
||||||
|
const currentMinute = parseInt (exchange.milliseconds () / durationInMs)
|
||||||
|
let ohlcvc = exchange.buildOHLCVC (trades, timeframe)
|
||||||
|
if (completeCandlesOnly) {
|
||||||
|
ohlcvc = ohlcvc.filter (candle => parseInt (candle[0] / durationInMs) < currentMinute)
|
||||||
|
}
|
||||||
|
if (ohlcvc.length > 0) {
|
||||||
|
console.log("Symbol:", symbol, "timeframe:", timeframe);
|
||||||
|
console.log ('-----------------------------------------------------------')
|
||||||
|
console.log (asTable (ohlcvc))
|
||||||
|
console.log ('-----------------------------------------------------------')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log (symbol, e)
|
||||||
|
// do nothing and retry on next loop iteration
|
||||||
|
// throw e // uncomment to break all loops in case of an error in any one of them
|
||||||
|
// break // you can also break just this one loop if it fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
// select the exchange
|
||||||
|
const exchange = new ccxt.pro.ftx ()
|
||||||
|
|
||||||
|
if (exchange.has['watchTrades']) {
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
// Change this value accordingly
|
||||||
|
const timeframe = '1m'
|
||||||
|
|
||||||
|
const allSymbols = exchange.symbols;
|
||||||
|
|
||||||
|
// arbitrary n symbols
|
||||||
|
const limit = 5;
|
||||||
|
// const selectedSymbols = allSymbols.slice(0, limit);
|
||||||
|
// you can also specify the symbols manually
|
||||||
|
// example:
|
||||||
|
// const selectedSymbols = ['BTC/USDT', 'LTC/USDT']
|
||||||
|
|
||||||
|
console.log(selectedSymbols);
|
||||||
|
|
||||||
|
// Use this variable to choose if only complete candles
|
||||||
|
// should be considered
|
||||||
|
const completeCandlesOnly = true
|
||||||
|
|
||||||
|
await Promise.all (selectedSymbols.map (symbol => loop (exchange, symbol, timeframe, completeCandlesOnly)))
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log (exchange.id, 'does not support watchTrades yet')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main ()
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('../../../ccxt');
|
||||||
|
|
||||||
|
console.log ('CCXT Version:', ccxt.version)
|
||||||
|
|
||||||
|
let ohlcvs = {}
|
||||||
|
|
||||||
|
async function log (exchange, symbol, timeframe) {
|
||||||
|
const market = exchange.market (symbol)
|
||||||
|
const duration = exchange.parseTimeframe (timeframe) * 1000
|
||||||
|
console.log (exchange.iso8601 (exchange.milliseconds ()), 'Warming up, waiting for at least one trade...')
|
||||||
|
while (!Object.keys (ohlcvs).length) {
|
||||||
|
await exchange.throttle (1)
|
||||||
|
}
|
||||||
|
let now = exchange.milliseconds ()
|
||||||
|
const start = (parseInt (now / duration) + 1) * duration
|
||||||
|
console.log (exchange.iso8601 (exchange.milliseconds ()), 'Trades started arriving, waiting till', exchange.iso8601 (start))
|
||||||
|
await exchange.sleep (start - now)
|
||||||
|
console.log (exchange.iso8601 (exchange.milliseconds ()), 'Done warming up')
|
||||||
|
for (let i = 0;; i++) {
|
||||||
|
now = exchange.milliseconds ()
|
||||||
|
let candle = Object.values (ohlcvs)[0]
|
||||||
|
console.log ('')
|
||||||
|
console.log (exchange.iso8601 (now), '------------------------------------------------------')
|
||||||
|
console.log ('Datetime ', 'Timestamp ', ... [ 'Open', 'High', 'Low', 'Close', market['base'], market['quote'] ].map (x => x.toString ().padEnd (10, ' ')))
|
||||||
|
for (let j = start; j < now; j += duration) {
|
||||||
|
if (!(j in ohlcvs)) {
|
||||||
|
ohlcvs[j] = [ j, candle[4], candle[4], candle[4], candle[4], 0, 0 ]
|
||||||
|
}
|
||||||
|
candle = exchange.safeValue (ohlcvs, j);
|
||||||
|
console.log (exchange.iso8601 (j), ... candle.map (x => x.toString ().padEnd (10, ' ')))
|
||||||
|
}
|
||||||
|
ohlcvs = exchange.indexBy (Object.values (ohlcvs).slice (-1000), 0)
|
||||||
|
await exchange.sleep(1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function watch (exchange, symbol, timeframe) {
|
||||||
|
console.log ('Starting', exchange.id, symbol)
|
||||||
|
const duration = exchange.parseTimeframe (timeframe) * 1000
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const trades = await exchange.watchTrades(symbol)
|
||||||
|
for (const trade of trades) {
|
||||||
|
const timestamp = parseInt(trade['timestamp'] / duration) * duration
|
||||||
|
let candle = exchange.safe_value(ohlcvs, timestamp)
|
||||||
|
if (candle) {
|
||||||
|
candle[2] = Math.max(trade['price'], candle[2])
|
||||||
|
candle[3] = Math.min(trade['price'], candle[3])
|
||||||
|
candle[4] = trade['price']
|
||||||
|
candle[5] = exchange.parseNumber(exchange.amountToPrecision(symbol, trade['amount'] + candle[5]))
|
||||||
|
candle[6] = exchange.parseNumber(exchange.costToPrecision(symbol, trade['cost'] + candle[6]))
|
||||||
|
} else {
|
||||||
|
candle = [
|
||||||
|
timestamp,
|
||||||
|
trade['price'],
|
||||||
|
trade['price'],
|
||||||
|
trade['price'],
|
||||||
|
trade['price'],
|
||||||
|
exchange.parseNumber(exchange.amountToPrecision(symbol, trade['amount'])),
|
||||||
|
exchange.parseNumber(exchange.costToPrecision(symbol, trade['cost'])),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
ohlcvs[timestamp] = candle
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log (e.constructor.name, e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const symbol = 'BTC/USDT'
|
||||||
|
const exchange = new ccxt.pro.binance ({ 'newUpdates': true })
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
const timeframe = '1m'
|
||||||
|
await Promise.all ([ watch (exchange, symbol, timeframe), log (exchange, symbol, timeframe) ])
|
||||||
|
await exchange.close ()
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('../../../ccxt');
|
||||||
|
|
||||||
|
console.log ('CCXT Version:', ccxt.version)
|
||||||
|
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
let ohlcvs = {}
|
||||||
|
const symbol = 'BTC/USDT'
|
||||||
|
const exchange = new ccxt.pro.binance ({ 'newUpdates': true })
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
const market = exchange.market (symbol)
|
||||||
|
const timeframe = '1m'
|
||||||
|
const duration = exchange.parseTimeframe (timeframe) * 1000
|
||||||
|
console.log ('Starting', exchange.id, symbol)
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const trades = await exchange.watchTrades(symbol)
|
||||||
|
for (const trade of trades) {
|
||||||
|
const timestamp = parseInt(trade['timestamp'] / duration) * duration
|
||||||
|
let candle = exchange.safe_value(ohlcvs, timestamp)
|
||||||
|
if (candle) {
|
||||||
|
candle[2] = Math.max(trade['price'], candle[2])
|
||||||
|
candle[3] = Math.min(trade['price'], candle[3])
|
||||||
|
candle[4] = trade['price']
|
||||||
|
candle[5] = exchange.parseNumber(exchange.amountToPrecision(symbol, trade['amount'] + candle[5]))
|
||||||
|
candle[6] = exchange.parseNumber(exchange.costToPrecision(symbol, trade['cost'] + candle[6]))
|
||||||
|
} else {
|
||||||
|
candle = [
|
||||||
|
timestamp,
|
||||||
|
trade['price'],
|
||||||
|
trade['price'],
|
||||||
|
trade['price'],
|
||||||
|
trade['price'],
|
||||||
|
exchange.parseNumber(exchange.amountToPrecision(symbol, trade['amount'])),
|
||||||
|
exchange.parseNumber(exchange.costToPrecision(symbol, trade['cost'])),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
ohlcvs[timestamp] = candle
|
||||||
|
}
|
||||||
|
console.log ('')
|
||||||
|
console.log (exchange.iso8601 (exchange.milliseconds ()), '------------------------------------------------------')
|
||||||
|
const values = Object.values (ohlcvs).slice (-1000)
|
||||||
|
ohlcvs = exchange.indexBy (values, 0)
|
||||||
|
console.log ('Datetime ', 'Timestamp ', ... [ 'Open', 'High', 'Low', 'Close', market['base'], market['quote'] ].map (x => x.toString ().padEnd (10, ' ')))
|
||||||
|
for (let i = 0; i < values.length; i++) {
|
||||||
|
const candle = values[i]
|
||||||
|
console.log (exchange.iso8601 (candle[0]), ... candle.map (x => x.toString ().padEnd (10, ' ')))
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.log (e.constructor.name, e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await exchange.close ()
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
const ccxt = require ('../../../ccxt.js').pro
|
||||||
|
, asTable = require ('as-table') // .configure ({ print: require ('string.ify').noPretty })
|
||||||
|
, log = require ('ololog').noLocate
|
||||||
|
, ansi = require ('ansicolor').nice
|
||||||
|
|
||||||
|
;(async function test () {
|
||||||
|
|
||||||
|
let total = 0
|
||||||
|
let missing = 0
|
||||||
|
let implemented = 0
|
||||||
|
let emulated = 0
|
||||||
|
|
||||||
|
const exchanges = ccxt.exchanges
|
||||||
|
.map (id => new ccxt[id]())
|
||||||
|
.filter (exchange => exchange.has.ws)
|
||||||
|
|
||||||
|
log (
|
||||||
|
asTable (
|
||||||
|
exchanges
|
||||||
|
.map (exchange => {
|
||||||
|
|
||||||
|
let result = {};
|
||||||
|
|
||||||
|
[
|
||||||
|
'ws',
|
||||||
|
'watchOrderBook',
|
||||||
|
'watchTicker',
|
||||||
|
'watchTrades',
|
||||||
|
'watchOHLCV',
|
||||||
|
'watchBalance',
|
||||||
|
'watchOrders',
|
||||||
|
'watchMyTrades',
|
||||||
|
|
||||||
|
].forEach (key => {
|
||||||
|
|
||||||
|
total += 1
|
||||||
|
|
||||||
|
let capability = (key in exchange.has) ?
|
||||||
|
exchange.has[key].toString () :
|
||||||
|
'undefined'
|
||||||
|
|
||||||
|
if (!exchange.has[key]) {
|
||||||
|
capability = exchange.id.red.dim
|
||||||
|
missing += 1
|
||||||
|
} else if (exchange.has[key] === 'emulated') {
|
||||||
|
capability = exchange.id.yellow
|
||||||
|
emulated += 1
|
||||||
|
} else {
|
||||||
|
capability = exchange.id.green
|
||||||
|
implemented += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
result[key] = capability
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
log ('Summary:',
|
||||||
|
exchanges.length.toString ().green, 'exchanges,',
|
||||||
|
implemented.toString ().green, 'methods implemented,',
|
||||||
|
emulated.toString ().yellow, 'emulated,',
|
||||||
|
missing.toString ().red, 'missing,',
|
||||||
|
total.toString (), 'total')
|
||||||
|
|
||||||
|
}) ()
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('../../../ccxt')
|
||||||
|
|
||||||
|
console.log ("CCXT Pro Version:", ccxt.version)
|
||||||
|
|
||||||
|
const orderbooks = {}
|
||||||
|
|
||||||
|
let run = true
|
||||||
|
|
||||||
|
async function watchOrderBook (exchange, symbol) {
|
||||||
|
while (run) {
|
||||||
|
try {
|
||||||
|
const orderbook = await exchange.watchOrderBook (symbol)
|
||||||
|
orderbooks[symbol] = orderbook
|
||||||
|
console.log (exchange.iso8601 (exchange.milliseconds ()), orderbook['datetime'], orderbook['nonce'], symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
} catch (e) {
|
||||||
|
console.log (e.constructor.name, e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function stop (exchange) {
|
||||||
|
await exchange.sleep (10000)
|
||||||
|
run = false
|
||||||
|
await exchange.close ()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
const exchange = new ccxt.pro.binance ()
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
exchange.verbose = true
|
||||||
|
const symbols = [
|
||||||
|
'BTC/USDT',
|
||||||
|
'ETH/USDT',
|
||||||
|
]
|
||||||
|
stop (exchange).then (() => {})
|
||||||
|
await Promise.all (symbols.map (symbol => watchOrderBook (exchange, symbol)))
|
||||||
|
console.log ('Sleeping for a moment...')
|
||||||
|
await exchange.sleep (10000)
|
||||||
|
console.log ('Done')
|
||||||
|
}
|
||||||
|
|
||||||
|
main ()
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('ccxt')
|
||||||
|
|
||||||
|
console.log ("CCXT Pro Version:", ccxt.version)
|
||||||
|
|
||||||
|
async function loop (exchange, method, symbol) {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const orderbook = await exchange[method] (symbol)
|
||||||
|
console.log (exchange.iso8601 (exchange.milliseconds ()), orderbook['datetime'], orderbook['nonce'], symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
} catch (e) {
|
||||||
|
console.log (e.constructor.name, e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
const exchange = new ccxt.pro.gateio ({
|
||||||
|
'options': {'defaultType':'swap'}
|
||||||
|
})
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
// exchange.verbose = true // uncomment for debugging purposes if necessary
|
||||||
|
const symbols = [
|
||||||
|
'ANC/USDT:USDT',
|
||||||
|
]
|
||||||
|
await Promise.all (symbols.map (symbol => loop (exchange, 'fetchOrderBook', symbol)))
|
||||||
|
}
|
||||||
|
|
||||||
|
main ()
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
const ccxt = require ('../../../ccxt')
|
||||||
|
|
||||||
|
console.log ('CCXT Pro version:', ccxt.version)
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
const exchange = new ccxt.pro.gateio ({
|
||||||
|
'apiKey': 'YOUR_API_KEY',
|
||||||
|
'secret': 'YOUR_SECRET',
|
||||||
|
})
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
exchange.verbose = true
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const response = await exchange.watchBalance ()
|
||||||
|
console.log (new Date (), response)
|
||||||
|
} catch (e) {
|
||||||
|
console.log (e.constructor.name, e.message)
|
||||||
|
await exchange.sleep (1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main ()
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('ccxt')
|
||||||
|
|
||||||
|
console.log ("CCXT Pro Version:", ccxt.version)
|
||||||
|
|
||||||
|
const orderbooks = {}
|
||||||
|
|
||||||
|
async function watchAllSymbols (exchange, symbols) {
|
||||||
|
while (true) {
|
||||||
|
const keys = Object.keys (orderbooks);
|
||||||
|
if (symbols.length === keys.length) {
|
||||||
|
console.log ('\n\n\n\n\n')
|
||||||
|
console.log ('----------------------------------------------------')
|
||||||
|
console.log ('All orderbooks received at least one update:');
|
||||||
|
for (let i = 0; i < symbols.length; i++) {
|
||||||
|
const symbol = symbols[i]
|
||||||
|
const orderbook = orderbooks[symbol]
|
||||||
|
console.log (exchange.iso8601 (exchange.milliseconds ()), orderbook['datetime'], orderbook['nonce'], symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
}
|
||||||
|
console.log ('----------------------------------------------------')
|
||||||
|
console.log ('\n\n\n\n\n')
|
||||||
|
// process.exit () // stop here if you want
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
await exchange.sleep (1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function watchOrderBook (exchange, symbol) {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const orderbook = await exchange.watchOrderBook (symbol)
|
||||||
|
orderbooks[symbol] = orderbook
|
||||||
|
console.log (exchange.iso8601 (exchange.milliseconds ()), orderbook['datetime'], orderbook['nonce'], symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
} catch (e) {
|
||||||
|
console.log (e.constructor.name, e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
const exchange = new ccxt.pro.gateio ({
|
||||||
|
'options': {
|
||||||
|
'defaultType': 'swap',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
// exchange.verbose = true // uncomment for debugging purposes if necessary
|
||||||
|
const symbols = [
|
||||||
|
// 'SOS/USDT:USDT',
|
||||||
|
// 'JASMY/USDT:USDT',
|
||||||
|
// 'SLP/USDT:USDT',
|
||||||
|
'ACH/USDT:USDT',
|
||||||
|
'MKISHU/USDT:USDT',
|
||||||
|
// 'GMT/USDT:USDT',
|
||||||
|
// 'ASTR/USDT:USDT',
|
||||||
|
'RAMP/USDT:USDT',
|
||||||
|
'RSR/USDT:USDT',
|
||||||
|
// 'RACA/USDT:USDT',
|
||||||
|
// 'ROOK/USDT:USDT',
|
||||||
|
// 'ROSE/USDT:USDT',
|
||||||
|
]
|
||||||
|
await Promise.all ([
|
||||||
|
watchAllSymbols (exchange, symbols),
|
||||||
|
... symbols.map (symbol => watchOrderBook (exchange, symbol))
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
main ()
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('../../../ccxt');
|
||||||
|
|
||||||
|
let stop = false
|
||||||
|
|
||||||
|
async function shutdown (milliseconds) {
|
||||||
|
await ccxt.sleep (10000)
|
||||||
|
stop = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function watchOrderBook (exchangeId, symbol) {
|
||||||
|
|
||||||
|
const exchange = new ccxt.pro[exchangeId] ()
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
// exchange.verbose = true
|
||||||
|
while (!stop) {
|
||||||
|
try {
|
||||||
|
const orderbook = await exchange.watchOrderBook (symbol)
|
||||||
|
console.log (new Date (), exchange.id, symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
} catch (e) {
|
||||||
|
console.log (symbol, e)
|
||||||
|
stop = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await exchange.close ()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
const streams = {
|
||||||
|
'binance': 'BTC/USDT',
|
||||||
|
'ftx': 'BTC/USDT',
|
||||||
|
};
|
||||||
|
|
||||||
|
await Promise.all ([
|
||||||
|
shutdown (10000),
|
||||||
|
... Object.entries (streams).map (([ exchangeId, symbol ]) => watchOrderBook (exchangeId, symbol))
|
||||||
|
])
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('../../../ccxt');
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
|
||||||
|
const streams = {
|
||||||
|
'binance': 'BTC/USDT',
|
||||||
|
'bittrex': 'BTC/USDT',
|
||||||
|
'poloniex': 'BTC/USDT',
|
||||||
|
'bitfinex': 'BTC/USDT',
|
||||||
|
'hitbtc': 'BTC/USDT',
|
||||||
|
'upbit': 'BTC/USDT',
|
||||||
|
'coinbasepro': 'BTC/USD',
|
||||||
|
'ftx': 'BTC/USDT',
|
||||||
|
'okex': 'BTC/USDT',
|
||||||
|
'gateio': 'BTC/USDT',
|
||||||
|
};
|
||||||
|
|
||||||
|
await Promise.all (Object.keys (streams).map (exchangeId =>
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
|
||||||
|
const exchange = new ccxt.pro[exchangeId] ({ enableRateLimit: true })
|
||||||
|
const symbol = streams[exchangeId]
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const orderbook = await exchange.watchOrderBook (symbol)
|
||||||
|
console.log (new Date (), exchange.id, symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
} catch (e) {
|
||||||
|
console.log (symbol, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}) ())
|
||||||
|
)
|
||||||
|
}) ()
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('ccxt')
|
||||||
|
, exchange = new ccxt.okex ({
|
||||||
|
apiKey: 'YOUR_API_KEY',
|
||||||
|
secret: 'YOUR_API_SECRET',
|
||||||
|
password: 'YOUR_API_PASSWORD',
|
||||||
|
enableRateLimit: true,
|
||||||
|
options: { defaultType: 'futures' },
|
||||||
|
})
|
||||||
|
, symbol = 'BTC/USDT:USDT-201225'
|
||||||
|
, amount = 1 // how may contracts
|
||||||
|
, price = undefined // or your limit price
|
||||||
|
, side = 'buy' // or 'sell'
|
||||||
|
, type = '1' // 1 open long, 2 open short, 3 close long, 4 close short for futures
|
||||||
|
, order_type = '4' // 0 = limit order, 4 = market order
|
||||||
|
|
||||||
|
console.log ('CCXT Pro Version: ', ccxt.version)
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
|
||||||
|
// exchange.verbose = true // uncomment for debugging
|
||||||
|
// open long market price order
|
||||||
|
const order = await exchange.createOrder(symbol, 'market', side, amount, price, { type });
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
// open long market price order
|
||||||
|
// const order = await exchange.createOrder(symbol, type, side, amount, price, { order_type });
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
// close short market price order
|
||||||
|
// const order = await exchange.createOrder(symbol, 'market', side, amount, price, { type, order_type });
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
// close short market price order
|
||||||
|
// const order = await exchange.createOrder(symbol, '4', side, amount, price, { order_type });
|
||||||
|
// ...
|
||||||
|
|
||||||
|
console.log (order)
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
|
||||||
|
console.log (e.constructor.name, e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main ()
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
const ccxt = require ('ccxt')
|
||||||
|
|
||||||
|
console.log ('Node.js:', process.version)
|
||||||
|
console.log ('CCXT Pro v' + ccxt.version)
|
||||||
|
|
||||||
|
const exchange = new ccxt.pro.okex ({
|
||||||
|
'apiKey': 'YOUR_API_KEY',
|
||||||
|
'secret': 'YOUR_API_SECRET',
|
||||||
|
'password': 'YOUR_API_PASWORD'
|
||||||
|
})
|
||||||
|
|
||||||
|
async function watchBalance (code) {
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const balance = await exchange.watchBalance ({
|
||||||
|
'code': code,
|
||||||
|
})
|
||||||
|
console.log ('New', code, 'balance is: ', balance[code])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createOrder (symbol, type, side, amount, price = undefined, params = {}) {
|
||||||
|
|
||||||
|
console.log ('Creating', symbol, type, 'order')
|
||||||
|
const order = await exchange.createOrder (symbol, type, side, amount, price, params);
|
||||||
|
console.log (symbol, type, side, 'order created')
|
||||||
|
console.log (order)
|
||||||
|
}
|
||||||
|
|
||||||
|
;(async () => {
|
||||||
|
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
console.log (exchange.id, 'markets loaded')
|
||||||
|
|
||||||
|
const symbol = 'ETH/USDT'
|
||||||
|
const market = exchange.market (symbol)
|
||||||
|
const quote = market['quote']
|
||||||
|
|
||||||
|
// run in parallel without await
|
||||||
|
console.log ('Watching', quote, 'balance')
|
||||||
|
watchBalance (quote)
|
||||||
|
|
||||||
|
// wait a bit to allow it to subscribe
|
||||||
|
await exchange.sleep (3000)
|
||||||
|
|
||||||
|
const ticker = await exchange.watchTicker (symbol)
|
||||||
|
const type = 'market'
|
||||||
|
const side = 'buy'
|
||||||
|
const amount = 0.1
|
||||||
|
const price = ticker['bid']
|
||||||
|
|
||||||
|
await createOrder (symbol, type, side, amount, price)
|
||||||
|
|
||||||
|
}) ()
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import ccxt from '../../../js/ccxt.js';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const exchange = new ccxt.pro.okx()
|
||||||
|
await exchange.loadMarkets()
|
||||||
|
// exchange.verbose = true
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
// don't do this, specify a list of symbols to watch for watchTickers
|
||||||
|
// or a very large subscription message will crash your WS connection
|
||||||
|
// const tickers = await exchange.watchTickers ()
|
||||||
|
|
||||||
|
// do this instead
|
||||||
|
const symbols = [
|
||||||
|
'ETH/BTC',
|
||||||
|
'BTC/USDT',
|
||||||
|
'ETH/USDT',
|
||||||
|
// ...
|
||||||
|
]
|
||||||
|
const tickers = await exchange.watchTickers (symbols)
|
||||||
|
symbols = Object.keys(tickers)
|
||||||
|
console.log (new Date(), 'received', symbols.length, 'symbols', ... symbols.slice(0,5).join(', '), '...')
|
||||||
|
} catch (e) {
|
||||||
|
console.log (new Date(), e.constructor.name, e.message)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('ccxt')
|
||||||
|
|
||||||
|
console.log ('CCXT Version:', ccxt.version)
|
||||||
|
|
||||||
|
async function watchOrders(exchange) {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const orders = await exchange.watchOrders() // await here
|
||||||
|
console.log(orders)
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function watchBalance(exchange) {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const balance = await exchange.watchBalance() // await here
|
||||||
|
console.log(balance)
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const exchange = new ccxt.pro.binance({
|
||||||
|
'apiKey': 'YOUR_API_KEY',
|
||||||
|
'secret': 'YOUR_SECRET',
|
||||||
|
'password': 'IF NECESSARY',
|
||||||
|
// etc...
|
||||||
|
})
|
||||||
|
await exchange.loadMarkets () // await here
|
||||||
|
|
||||||
|
// exchange.verbose = true // uncomment for debugging purposes if necessary
|
||||||
|
|
||||||
|
watchOrders(exchange) // no await
|
||||||
|
watchBalance(exchange) // no await
|
||||||
|
|
||||||
|
await exchange.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('ccxt');
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
|
||||||
|
const exchange = new ccxt.pro.binance ({ enableRateLimit: true })
|
||||||
|
const symbols = [ 'BTC/USDT', 'ETH/BTC', 'ETH/USDT' ]
|
||||||
|
|
||||||
|
const loop = async (symbol) => {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const orderbook = await exchange.watchOrderBook (symbol)
|
||||||
|
console.log (new Date (), symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
} catch (e) {
|
||||||
|
console.log (symbol, e)
|
||||||
|
// do nothing and retry on next loop iteration
|
||||||
|
// throw e // uncomment to break all loops in case of an error in any one of them
|
||||||
|
// break // you can also break just this one loop if it fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all (symbols.map (symbol => loop (symbol)))
|
||||||
|
|
||||||
|
}) ()
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('ccxt');
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
|
||||||
|
const exchange = new ccxt.pro.binance ({ enableRateLimit: true })
|
||||||
|
const symbols = [ 'BTC/USDT', 'ETH/BTC', 'ETH/USDT' ]
|
||||||
|
|
||||||
|
await Promise.all (symbols.map (symbol =>
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const orderbook = await exchange.watchOrderBook (symbol)
|
||||||
|
console.log (new Date (), symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
} catch (e) {
|
||||||
|
console.log (symbol, e)
|
||||||
|
// do nothing and retry on next loop iteration
|
||||||
|
// throw e // uncomment to break all loops in case of an error in any one of them
|
||||||
|
// break // you can also break just this one loop if it fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}) ())
|
||||||
|
)
|
||||||
|
}) ()
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('ccxt')
|
||||||
|
, SocksProxyAgent = require ('socks-proxy-agent')
|
||||||
|
, socks = 'socks://127.0.0.1:7000'
|
||||||
|
, socksAgent = new SocksProxyAgent (socks)
|
||||||
|
, exchange = new ccxt.binance ({
|
||||||
|
enableRatLimit: true,
|
||||||
|
httpsAgent: socksAgent, // ←--------------------- socksAgent here
|
||||||
|
options: {
|
||||||
|
'ws': {
|
||||||
|
'options': { agent: socksAgent }, // ←--- socksAgent here
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
;(async () => {
|
||||||
|
console.log (socks)
|
||||||
|
const symbol = 'BTC/USDT'
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
console.log ('Markets loaded')
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const orderbook = await exchange.watchOrderBook (symbol)
|
||||||
|
console.log (exchange.iso8601 (exchange.milliseconds()), symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
} catch (e) {
|
||||||
|
console.log (e.constructor.name, e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) ()
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('../../../ccxt');
|
||||||
|
|
||||||
|
async function watchOrderBook (exchange, symbol) {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const method = exchange.has.watchOrderBook ? 'watchOrderBook' : 'fetchOrderBook'
|
||||||
|
const orderbook = await exchange[method](symbol)
|
||||||
|
console.log (new Date (), exchange.id, symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
} catch (e) {
|
||||||
|
console.log (symbol, e)
|
||||||
|
process.exit ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function watchExchange (exchangeId, symbols) {
|
||||||
|
const exchange = new ccxt.pro[exchangeId] ()
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
await Promise.all (symbols.map (symbol => watchOrderBook (exchange, symbol)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
const streams = {
|
||||||
|
'ftx': [
|
||||||
|
'BTC/USDT',
|
||||||
|
'ETH/BTC',
|
||||||
|
],
|
||||||
|
'coinex': [
|
||||||
|
'BTC/USDT',
|
||||||
|
'ETH/BTC',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const entries = Object.entries (streams)
|
||||||
|
await Promise.all (entries.map (([ exchangeId, symbols ]) => watchExchange (exchangeId, symbols)))
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('../../../ccxt');
|
||||||
|
|
||||||
|
async function watchOrderBook (exchange, symbol) {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const orderbook = await exchange.watchOrderBook (symbol)
|
||||||
|
console.log (new Date (), exchange.id, symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
} catch (e) {
|
||||||
|
console.log (symbol, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function watchExchange (exchangeId, symbols) {
|
||||||
|
const exchange = new ccxt.pro[exchangeId] ()
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
await Promise.all (symbols.map (symbol => watchOrderBook (exchange, symbol)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
const streams = {
|
||||||
|
'binance': [
|
||||||
|
'BTC/USDT',
|
||||||
|
'ETH/BTC',
|
||||||
|
],
|
||||||
|
'ftx': [
|
||||||
|
'BTC/USDT',
|
||||||
|
'ETH/BTC',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const entries = Object.entries (streams)
|
||||||
|
await Promise.all (entries.map (([ exchangeId, symbols ]) => watchExchange (exchangeId, symbols)))
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('../../../ccxt');
|
||||||
|
|
||||||
|
console.log ('CCXT Version:', ccxt.version)
|
||||||
|
|
||||||
|
|
||||||
|
async function watchTickerLoop (exchange, symbol) {
|
||||||
|
// exchange.verbose = true // uncomment for debugging purposes if necessary
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const ticker = await exchange.watchTicker (symbol)
|
||||||
|
console.log (new Date (), exchange.id, symbol, ticker['last'])
|
||||||
|
} catch (e) {
|
||||||
|
console.log (symbol, e)
|
||||||
|
// do nothing and retry on next loop iteration
|
||||||
|
// throw e // uncomment to break all loops in case of an error in any one of them
|
||||||
|
// break // you can also break just this one loop if it fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function exchangeLoop (exchangeId, symbols) {
|
||||||
|
const exchange = new ccxt.pro[exchangeId]()
|
||||||
|
await exchange.loadMarkets ()
|
||||||
|
const loops = symbols.map (symbol => watchTickerLoop (exchange, symbol))
|
||||||
|
await Promise.all (loops)
|
||||||
|
await exchange.close ()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
const exchanges = {
|
||||||
|
'binance': [ 'BTC/USDT', 'ETH/USDT' ],
|
||||||
|
'ftx': [ 'BTC/USD', 'ETH/USD' ],
|
||||||
|
}
|
||||||
|
const loops = Object.entries (exchanges).map (([ exchangeId, symbols ]) => exchangeLoop (exchangeId, symbols))
|
||||||
|
await Promise.all (loops)
|
||||||
|
}
|
||||||
|
|
||||||
|
main ()
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('ccxt');
|
||||||
|
|
||||||
|
async function watchOrderBook (exchangeId, symbol) {
|
||||||
|
|
||||||
|
const exchange = new ccxt.pro[exchangeId] ()
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const orderbook = await exchange.watchOrderBook (symbol)
|
||||||
|
console.log (new Date (), exchange.id, symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
} catch (e) {
|
||||||
|
console.log (symbol, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
const streams = {
|
||||||
|
'binance': 'BTC/USDT',
|
||||||
|
'ftx': 'BTC/USDT',
|
||||||
|
};
|
||||||
|
|
||||||
|
await Promise.all (Object.entries (streams).map (([ exchangeId, symbol ]) => watchOrderBook (exchangeId, symbol)))
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('../../../ccxt');
|
||||||
|
|
||||||
|
console.log ('CCXT Version:', ccxt.version);
|
||||||
|
|
||||||
|
async function watchExchange (exchangeId, symbol) {
|
||||||
|
|
||||||
|
const exchange = new ccxt.pro[exchangeId] ({
|
||||||
|
newUpdates: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
await exchange.loadMarkets ();
|
||||||
|
|
||||||
|
// exchange.verbose = true // uncomment for debugging purposes if necessary
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const trades = await exchange.watchTrades (symbol)
|
||||||
|
for (let i = 0; i < trades.length; i++) {
|
||||||
|
const trade = trades[i]
|
||||||
|
console.log (exchange.iso8601 (exchange.milliseconds ()), exchange.id, trade['symbol'], trade['id'], trade['datetime'], trade['price'], trade['amount'])
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log (symbol, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
|
||||||
|
const streams = {
|
||||||
|
'binance': 'BTC/USDT',
|
||||||
|
'okex': 'BTC/USDT',
|
||||||
|
'kraken': 'BTC/USD',
|
||||||
|
};
|
||||||
|
|
||||||
|
const values = Object.entries (streams)
|
||||||
|
const promises = values.map (([ exchangeId, symbol ]) => watchExchange (exchangeId, symbol))
|
||||||
|
await Promise.all (promises)
|
||||||
|
}
|
||||||
|
|
||||||
|
main ()
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('../../../ccxt');
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
|
||||||
|
const streams = {
|
||||||
|
'binance': 'BTC/USDT',
|
||||||
|
'okex': 'BTC/USDT'
|
||||||
|
};
|
||||||
|
|
||||||
|
await Promise.all (Object.keys (streams).map (exchangeId =>
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
|
||||||
|
const exchange = new ccxt.pro[exchangeId] ({
|
||||||
|
enableRateLimit: true,
|
||||||
|
options: {
|
||||||
|
tradesLimit: 100, // lower = better, 1000 by default
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const symbol = streams[exchangeId]
|
||||||
|
let lastId = ''
|
||||||
|
while (true) {
|
||||||
|
console.log ('---')
|
||||||
|
try {
|
||||||
|
const trades = await exchange.watchTrades (symbol)
|
||||||
|
for (let i = 0; i < trades.length; i++) {
|
||||||
|
const trade = trades[i]
|
||||||
|
if (trade['id'] > lastId) {
|
||||||
|
console.log (exchange.iso8601 (exchange.milliseconds ()), exchange.id, trade['symbol'], trade['id'], trade['datetime'], trade['price'], trade['amount'])
|
||||||
|
lastId = trade['id']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log (symbol, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}) ())
|
||||||
|
)
|
||||||
|
}) ()
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ccxt = require ('ccxt');
|
||||||
|
|
||||||
|
console.log ('CCXT Version:', ccxt.version)
|
||||||
|
|
||||||
|
async function watchTrades (exchange, symbol) {
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const trades = await exchange.watchTrades (symbol)
|
||||||
|
console.log (new Date (), exchange.id, symbol, trades.length, 'trades')
|
||||||
|
} catch (e) {
|
||||||
|
console.log (symbol, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
const symbols = [ 'USDT/THB', 'BTC/THB', 'ETH/THB' ]
|
||||||
|
const exchange = new ccxt.pro.zipmex({
|
||||||
|
'newUpdates': true
|
||||||
|
})
|
||||||
|
const markets = await exchange.loadMarkets ()
|
||||||
|
exchange.verbose = true
|
||||||
|
await Promise.all (symbols.map ((symbol) => watchTrades (exchange, symbol)))
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// see this issue for details
|
||||||
|
// https://github.com/ccxt/ccxt/issues/6659
|
||||||
|
|
||||||
|
const ccxt = require ('ccxt')
|
||||||
|
|
||||||
|
const exchange = new ccxt.pro.kraken ()
|
||||||
|
|
||||||
|
function yellow (s) {
|
||||||
|
return '\x1b[33m' + s + '\x1b[0m'
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runWs () {
|
||||||
|
while (1) {
|
||||||
|
const book = await exchange.watchOrderBook ('ETH/BTC')
|
||||||
|
console.log (new Date (), 'WS ', book['datetime'], book['bids'][0][0], book['asks'][0][0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runRest () {
|
||||||
|
while (1) {
|
||||||
|
const book = await exchange.fetchOrderBook ('ETH/BTC')
|
||||||
|
const timestamp = new Date (exchange.last_response_headers['Date']).getTime ()
|
||||||
|
const datetime = exchange.iso8601 (timestamp)
|
||||||
|
console.log (new Date (), 'REST', yellow (datetime), book['bids'][0][0], book['asks'][0][0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runWs ()
|
||||||
|
runRest ()
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
date_default_timezone_set('UTC');
|
||||||
|
|
||||||
|
$root = dirname(dirname(dirname(dirname(__FILE__))));
|
||||||
|
include $root . '/ccxt.php';
|
||||||
|
|
||||||
|
function create_exchange($exchange_id, $config) {
|
||||||
|
|
||||||
|
$keys = array(
|
||||||
|
'ids',
|
||||||
|
'markets',
|
||||||
|
'markets_by_id',
|
||||||
|
'currencies',
|
||||||
|
'currencies_by_id',
|
||||||
|
'base_currencies',
|
||||||
|
'quote_currencies',
|
||||||
|
'symbols',
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
$exchange_class = '\\ccxt\\pro\\' . $exchange_id;
|
||||||
|
$exchange = new $exchange_class($config);
|
||||||
|
$markets_on_disk = "./{$exchange_id}.markets.json";
|
||||||
|
|
||||||
|
$exchange->verbose = true; // this is a debug output to demonstrate which networking calls are being issued
|
||||||
|
|
||||||
|
if (file_exists($markets_on_disk)) {
|
||||||
|
|
||||||
|
$cache = json_decode(file_get_contents($markets_on_disk), true);
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
$exchange->{$key} = $cache[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$markets = yield $exchange->load_markets();
|
||||||
|
$cache = array();
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
$cache[$key] = $exchange->{$key};
|
||||||
|
}
|
||||||
|
file_put_contents($markets_on_disk, json_encode($cache));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $exchange;
|
||||||
|
}
|
||||||
|
|
||||||
|
$exchanges = array(
|
||||||
|
array('binance', array(
|
||||||
|
'id' => 'binance1',
|
||||||
|
'apiKey' => 'YOUR_API_KEY_HERE',
|
||||||
|
'secret' => 'YOUR_SECRET_HERE',
|
||||||
|
)),
|
||||||
|
array('binance', array(
|
||||||
|
'id' => 'binance2',
|
||||||
|
'apiKey' => 'YOUR_API_KEY_HERE',
|
||||||
|
'secret' => 'YOUR_SECRET_HERE',
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
|
$loop = function($exchange_id, $config) {
|
||||||
|
$exchange = yield create_exchange($exchange_id, $config);
|
||||||
|
$exchange->verbose = true;
|
||||||
|
while (true) {
|
||||||
|
$response = yield $exchange->watch_balance();
|
||||||
|
print('--------------------------------------------------------------');
|
||||||
|
print($exchange->id);
|
||||||
|
print($response);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
foreach ($exchanges as $exchange) {
|
||||||
|
\React\Async\coroutine($loop, $exchange[0], $exchange[1]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$root = dirname(dirname(dirname(dirname(__FILE__))));
|
||||||
|
include $root . '/ccxt.php';
|
||||||
|
|
||||||
|
|
||||||
|
$config = array('enableRateLimit' => true);
|
||||||
|
$binance = new \ccxt\pro\binance($config);
|
||||||
|
$bittrex = new \ccxt\pro\bittrex($config);
|
||||||
|
$symbol = "BTC/USDT";
|
||||||
|
|
||||||
|
$loop = function($exchange, $symbol) {
|
||||||
|
echo 'got inside' . PHP_EOL;
|
||||||
|
for ($i = 0; $i < 5; $i++) {
|
||||||
|
$ticker = yield $exchange->watch_ticker($symbol);
|
||||||
|
print_ticker($ticker, $exchange->id, $symbol);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function print_ticker($ticker, $exchange_name, $symbol) {
|
||||||
|
$bid = $ticker['bid'];
|
||||||
|
$ask = $ticker['ask'];
|
||||||
|
echo "$exchange_name $symbol - bid: $bid <> ask: $ask" . PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
\React\Async\coroutine($loop, $bittrex, $symbol);
|
||||||
|
\React\Async\coroutine($loop, $binance, $symbol);
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$root = dirname(dirname(dirname(dirname(__FILE__))));
|
||||||
|
include $root . '/ccxt.php';
|
||||||
|
|
||||||
|
$id = 'aax';
|
||||||
|
$exchange_class = '\\ccxt\\pro\\' . $id;
|
||||||
|
$exchange = new $exchange_class(array(
|
||||||
|
'enableRateLimit' => true,
|
||||||
|
));
|
||||||
|
|
||||||
|
$symbols = array('BTC/USDT', 'ETH/USDT', 'ETH/BTC');
|
||||||
|
|
||||||
|
function print_orderbook($orderbook, $symbol) {
|
||||||
|
$id = isset($orderbook['nonce']) ? $orderbook['nonce'] : $orderbook['datetime'];
|
||||||
|
echo $id, ' ', $symbol, ' ',
|
||||||
|
count($orderbook['asks']), ' asks ', json_encode($orderbook['asks'][0]), ' ',
|
||||||
|
count($orderbook['bids']), ' bids ', json_encode($orderbook['bids'][0]), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$loop = function($exchange, $symbol) {
|
||||||
|
while (true) {
|
||||||
|
$orderbook = yield $exchange->watch_order_book($symbol);
|
||||||
|
print_orderbook($orderbook, $symbol);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach ($symbols as $symbol) {
|
||||||
|
\React\Async\coroutine($loop, $exchange, $symbol);
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
date_default_timezone_set('UTC');
|
||||||
|
include dirname(dirname(dirname(dirname(__FILE__)))). '/ccxt.php';
|
||||||
|
|
||||||
|
$binance_id = '\\ccxt\\pro\\binance';
|
||||||
|
$binance_exchange = new $binance_id( /*[ 'apiKey' => _YOUR_APIKEY_HERE_, 'secret' => _YOUR_SECRET_HERE_ ]*/ );
|
||||||
|
|
||||||
|
$ftx_id = '\\ccxt\\pro\\ftx';
|
||||||
|
$ftx_exchange = new $ftx_id( /*['apiKey' => _YOUR_APIKEY_HERE_, 'secret' => _YOUR_SECRET_HERE_]*/ );
|
||||||
|
|
||||||
|
$wrapper_func = function($exchange, $symbol, $method_name) {
|
||||||
|
if ($exchange->has[$method_name]) {
|
||||||
|
print ("Starting $method_name for $exchange->id -> $symbol\n");
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
$orderbook = yield $exchange->$method_name($symbol);
|
||||||
|
print("$exchange->id -> $method_name -> $symbol : " . substr(json_encode($orderbook), 0 , 70) . "...\n");
|
||||||
|
} catch (\Exception $ex) {
|
||||||
|
print($ex->getMessage());
|
||||||
|
sleep(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print ($exchange->id . " API yet doesnt support $method_name");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function runAsync (...$args) {
|
||||||
|
\React\Async\coroutine(...$args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// *** uncomment whichever methods you want to test ***
|
||||||
|
// runAsync($wrapper_func, ...[$exchange, 'ETH/USDT', 'watchOrderBook']);
|
||||||
|
// runAsync($wrapper_func, ...[$exchange, 'ETH/USDT', 'watchTicker']);
|
||||||
|
// runAsync($wrapper_func, ...[$exchange, 'ETH/USDT', 'watchOrders']);
|
||||||
|
// runAsync($wrapper_func, ...[$exchange, 'ETH/USDT', 'watchMyTrades']);
|
||||||
|
|
||||||
|
runAsync($wrapper_func, ...[$binance_exchange, 'SOL/USDT', 'watchTrades']);
|
||||||
|
runAsync($wrapper_func, ...[$ftx_exchange, 'ETH/USDT', 'watchTrades']);
|
||||||
|
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import ccxt.pro
|
||||||
|
from pprint import pprint
|
||||||
|
from asyncio import run
|
||||||
|
|
||||||
|
|
||||||
|
print('CCXT Version:', ccxt.__version__)
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.binance({
|
||||||
|
'apiKey': 'YOUR_API_KEY',
|
||||||
|
'secret': 'YOUR_SECRET',
|
||||||
|
})
|
||||||
|
|
||||||
|
markets = await exchange.load_markets()
|
||||||
|
|
||||||
|
# exchange.verbose = True # uncomment for debugging purposes if necessary
|
||||||
|
|
||||||
|
symbol = 'ETH/BTC'
|
||||||
|
type = 'limit' # or 'market'
|
||||||
|
side = 'sell' # or 'buy'
|
||||||
|
amount = 1.0
|
||||||
|
price = 0.060154 # or None
|
||||||
|
|
||||||
|
order = await exchange.create_order(symbol, type, side, amount, price)
|
||||||
|
canceled = await exchange.cancel_order(order['id'], order['symbol'])
|
||||||
|
|
||||||
|
pprint(canceled)
|
||||||
|
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
|
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import ccxt.pro
|
||||||
|
from asyncio import run
|
||||||
|
|
||||||
|
|
||||||
|
print('CCXT Version:', ccxt.__version__)
|
||||||
|
|
||||||
|
|
||||||
|
# This example will run silent and will return your balance only when the balance is updated.
|
||||||
|
#
|
||||||
|
# 1. launch the example with your keys and keep it running
|
||||||
|
# 2. go to the trading section on the website
|
||||||
|
# 3. place a order on a spot market
|
||||||
|
# 4. see your balance updated in the example
|
||||||
|
#
|
||||||
|
# Warning! This example might produce a lot of output to your screen
|
||||||
|
|
||||||
|
|
||||||
|
async def watch_balance(exchange):
|
||||||
|
await exchange.load_markets()
|
||||||
|
# exchange.verbose = True # uncomment for debugging purposes if necessary
|
||||||
|
balance = await exchange.fetch_balance()
|
||||||
|
print('---------------------------------------------------------')
|
||||||
|
print(exchange.iso8601(exchange.milliseconds()))
|
||||||
|
print(balance)
|
||||||
|
print('')
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
update = await exchange.watch_balance()
|
||||||
|
balance = exchange.deep_extend(balance, update)
|
||||||
|
# it will print the balance update when the balance changes
|
||||||
|
# if the balance remains unchanged the exchange will not send it
|
||||||
|
print('---------------------------------------------------------')
|
||||||
|
print(exchange.iso8601(exchange.milliseconds()))
|
||||||
|
print(balance)
|
||||||
|
print('')
|
||||||
|
except Exception as e:
|
||||||
|
print('watch_balance() failed')
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.binance({
|
||||||
|
'apiKey': 'YOUR_API_KEY',
|
||||||
|
'secret': 'YOUR_SECRET',
|
||||||
|
})
|
||||||
|
await watch_balance(exchange)
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import ccxt
|
||||||
|
import ccxt.pro
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
|
||||||
|
|
||||||
|
async def print_balance(exchange, market_type):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
balance = await exchange.watch_balance({'type': market_type})
|
||||||
|
pprint(balance)
|
||||||
|
print('balance of ' + market_type, balance)
|
||||||
|
print(exchange.options[market_type])
|
||||||
|
except ccxt.BaseError as e:
|
||||||
|
print(type(e), e)
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e), e)
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.binance({
|
||||||
|
"apiKey": "",
|
||||||
|
"secret": "",
|
||||||
|
'enableRateLimit': True,
|
||||||
|
'newUpdates': True,
|
||||||
|
})
|
||||||
|
# you must make an order a transfer first to the websocket to send updates
|
||||||
|
asyncio.ensure_future(print_balance(exchange, 'future'))
|
||||||
|
asyncio.ensure_future(print_balance(exchange, 'delivery')) # inverse futures settled in BTC
|
||||||
|
asyncio.ensure_future(print_balance(exchange, 'spot'))
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
|
asyncio.ensure_future(main())
|
||||||
|
loop.run_forever()
|
||||||
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import ccxt.pro
|
||||||
|
from asyncio import run
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.binance({
|
||||||
|
'options': {
|
||||||
|
'defaultType': 'future',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
symbol = 'BTC/USDT'
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
orderbook = await exchange.watch_order_book(symbol)
|
||||||
|
print(orderbook['bids'][0], orderbook['asks'][0])
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import ccxt.pro as ccxt
|
||||||
|
from asyncio import run
|
||||||
|
|
||||||
|
print('CCXT Version:', ccxt.__version__)
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.binance({
|
||||||
|
'options': {
|
||||||
|
'defaultType': 'future', # spot, margin, future, delivery
|
||||||
|
},
|
||||||
|
})
|
||||||
|
# or
|
||||||
|
# exchange = ccxt.pro.binanceusdm()
|
||||||
|
# or
|
||||||
|
# exchange = ccxt.pro.binancecoinm()
|
||||||
|
symbol = 'BTC/USDT'
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
orderbook = await exchange.watch_order_book(symbol)
|
||||||
|
print(exchange.iso8601(exchange.milliseconds()), exchange.id, symbol, 'ask:', orderbook['asks'][0], 'bid:', orderbook['bids'][0])
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
break
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import ccxt.pro
|
||||||
|
from asyncio import run, gather
|
||||||
|
|
||||||
|
|
||||||
|
print('CCXT Pro version', ccxt.pro.__version__)
|
||||||
|
|
||||||
|
|
||||||
|
async def watch_order_book(exchange, symbol):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
orderbook = await exchange.watch_order_book(symbol)
|
||||||
|
datetime = exchange.iso8601(exchange.milliseconds())
|
||||||
|
print(datetime, orderbook['nonce'], symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
async def reload_markets(exchange, delay):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
await exchange.sleep(delay)
|
||||||
|
markets = await exchange.load_markets(True)
|
||||||
|
datetime = exchange.iso8601(exchange.milliseconds())
|
||||||
|
print(datetime, 'Markets reloaded')
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.binance()
|
||||||
|
await exchange.load_markets()
|
||||||
|
# exchange.verbose = True
|
||||||
|
symbol = 'BTC/USDT'
|
||||||
|
delay = 60000 # every minute = 60 seconds = 60000 milliseconds
|
||||||
|
loops = [watch_order_book(exchange, symbol), reload_markets(exchange, delay)]
|
||||||
|
await gather(*loops)
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import ccxt.pro
|
||||||
|
from asyncio import gather, run
|
||||||
|
|
||||||
|
|
||||||
|
orderbooks = {}
|
||||||
|
|
||||||
|
|
||||||
|
def handle_all_orderbooks(orderbooks):
|
||||||
|
print('We have the following orderbooks:')
|
||||||
|
for id, orderbooks_by_symbol in orderbooks.items():
|
||||||
|
for symbol in orderbooks_by_symbol.keys():
|
||||||
|
orderbook = orderbooks_by_symbol[symbol]
|
||||||
|
print(ccxt.pro.Exchange.iso8601(orderbook['timestamp']), id, symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
|
||||||
|
|
||||||
|
async def symbol_loop(exchange, id, symbol):
|
||||||
|
print('Starting', id, symbol)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
orderbook = await exchange.watch_order_book(symbol)
|
||||||
|
orderbooks[id] = orderbooks.get(id, {})
|
||||||
|
orderbooks[id][symbol] = orderbook
|
||||||
|
print('===========================================================')
|
||||||
|
#
|
||||||
|
# here you can do what you want
|
||||||
|
# with the most recent versions of each orderbook you have so far
|
||||||
|
#
|
||||||
|
# you can also wait until all of them are available
|
||||||
|
# by just looking into all the orderbooks and counting them
|
||||||
|
#
|
||||||
|
# we just print them here to keep this example simple
|
||||||
|
#
|
||||||
|
handle_all_orderbooks(orderbooks)
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
# raise e # uncomment to break all loops in case of an error in any one of them
|
||||||
|
break # you can break just this one loop if it fails
|
||||||
|
|
||||||
|
|
||||||
|
async def exchange_loop(id, config):
|
||||||
|
print('Starting', id)
|
||||||
|
exchange = getattr(ccxt.pro, config['id'])({
|
||||||
|
'options': config['options'],
|
||||||
|
})
|
||||||
|
loops = [symbol_loop(exchange, id, symbol) for symbol in config['symbols']]
|
||||||
|
await gather(*loops)
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
configs = {
|
||||||
|
'Exchange A (Binance spot)': {
|
||||||
|
'id': 'binance',
|
||||||
|
'symbols': ['BTC/USDT', 'ETH/BTC','ETH/USDT'],
|
||||||
|
'options': {
|
||||||
|
'defaultType': 'spot',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'Exchange B (Binance futures)': {
|
||||||
|
'id': 'binance',
|
||||||
|
'symbols': ['BTC/USDT', 'ETH/USDT'],
|
||||||
|
'options': {
|
||||||
|
'defaultType': 'future',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
loops = [exchange_loop(id, config) for id, config in configs.items()]
|
||||||
|
await gather(*loops)
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import ccxt.pro as ccxt
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
orderbooks = {}
|
||||||
|
|
||||||
|
def when_orderbook_changed(exchange_spot, symbol, orderbook):
|
||||||
|
# this is a common handler function
|
||||||
|
# it is called when any of the orderbook is updated
|
||||||
|
# it has access to both the orderbook that was updated
|
||||||
|
# as well as the rest of the orderbooks
|
||||||
|
# ...................................................................
|
||||||
|
print('-------------------------------------------------------------')
|
||||||
|
print('Last updated:', exchange_spot.iso8601(exchange_spot.milliseconds()))
|
||||||
|
# ...................................................................
|
||||||
|
# print just one orderbook here
|
||||||
|
# print(orderbook['datetime'], symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
# ...................................................................
|
||||||
|
# or print all orderbooks that have been already subscribed-to
|
||||||
|
for symbol, orderbook in orderbooks.items():
|
||||||
|
print(orderbook['datetime'], symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
|
||||||
|
|
||||||
|
async def watch_one_orderbook(exchange_spot, symbol):
|
||||||
|
# a call cost of 1 in the queue of subscriptions
|
||||||
|
# means one subscription per exchange.rateLimit milliseconds
|
||||||
|
your_delay = 1
|
||||||
|
await exchange_spot.throttle(your_delay)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
orderbook = await exchange_spot.watch_order_book(symbol)
|
||||||
|
orderbooks[symbol] = orderbook
|
||||||
|
when_orderbook_changed(exchange_spot, symbol, orderbook)
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
|
||||||
|
|
||||||
|
async def watch_some_orderbooks(exchange_spot, symbol_list):
|
||||||
|
loops = [watch_one_orderbook(exchange_spot, symbol) for symbol in symbol_list]
|
||||||
|
# let them run, don't for all tasks cause they execute asynchronously
|
||||||
|
# don't print here
|
||||||
|
await asyncio.gather(*loops)
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange_spot = ccxt.binance()
|
||||||
|
await exchange_spot.load_markets()
|
||||||
|
await watch_some_orderbooks(exchange_spot, ['ZEN/USDT', 'RUNE/USDT', 'AAVE/USDT', 'SNX/USDT'])
|
||||||
|
await exchange_spot.close()
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from asyncio import run
|
||||||
|
import ccxt.pro as ccxt
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
|
||||||
|
# This example will run silent and will return your balance only when the balance is updated.
|
||||||
|
|
||||||
|
# 1. launch the example with your keys and keep it running
|
||||||
|
# 2. go to the margin trading on the website
|
||||||
|
# 3. place a margin order on a spot market
|
||||||
|
# 4. see your balance updated in the example
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.binance({
|
||||||
|
'apiKey': 'YOUR_API_KEY',
|
||||||
|
'secret': 'YOUR_SECRET',
|
||||||
|
'options': {
|
||||||
|
'defaultType': 'margin',
|
||||||
|
},
|
||||||
|
# comment it out if you don't want debug output
|
||||||
|
# this is for the demo purpose only (to show the communication)
|
||||||
|
'verbose': True,
|
||||||
|
})
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
balance = await exchange.watch_balance()
|
||||||
|
# it will print the balance update when the balance changes
|
||||||
|
# if the balance remains unchanged the exchange will not send it
|
||||||
|
pprint(balance)
|
||||||
|
except Exception as e:
|
||||||
|
print('watch_balance() failed')
|
||||||
|
print(e)
|
||||||
|
break
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import ccxt.pro
|
||||||
|
from asyncio import run
|
||||||
|
|
||||||
|
print('CCXT Pro version', ccxt.pro.__version__)
|
||||||
|
|
||||||
|
|
||||||
|
def table(values):
|
||||||
|
first = values[0]
|
||||||
|
keys = list(first.keys()) if isinstance(first, dict) else range(0, len(first))
|
||||||
|
widths = [max([len(str(v[k])) for v in values]) for k in keys]
|
||||||
|
string = ' | '.join(['{:<' + str(w) + '}' for w in widths])
|
||||||
|
return "\n".join([string.format(*[str(v[k]) for k in keys]) for v in values])
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.binance({
|
||||||
|
# 'options': {
|
||||||
|
# 'OHLCVLimit': 1000, # how many candles to store in memory by default
|
||||||
|
# },
|
||||||
|
})
|
||||||
|
symbol = 'ETH/USDT' # or BNB/USDT, etc...
|
||||||
|
timeframe = '1m' # 5m, 1h, 1d
|
||||||
|
limit = 10 # how many candles to return max
|
||||||
|
method = 'watchOHLCV'
|
||||||
|
if (method in exchange.has) and exchange.has[method]:
|
||||||
|
max_iterations = 100000 # how many times to repeat the loop before exiting
|
||||||
|
for i in range(0, max_iterations):
|
||||||
|
try:
|
||||||
|
ohlcvs = await exchange.watch_ohlcv(symbol, timeframe, None, limit)
|
||||||
|
now = exchange.milliseconds()
|
||||||
|
print('\n===============================================================================')
|
||||||
|
print('Loop iteration:', i, 'current time:', exchange.iso8601(now), symbol, timeframe)
|
||||||
|
print('-------------------------------------------------------------------------------')
|
||||||
|
print(table([[exchange.iso8601(o[0])] + o[1:] for o in ohlcvs]))
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
break
|
||||||
|
await exchange.close()
|
||||||
|
else:
|
||||||
|
print(exchange.id, method, 'is not supported or not implemented yet')
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from asyncio import run
|
||||||
|
import ccxt.pro as ccxt
|
||||||
|
|
||||||
|
|
||||||
|
class MyBinance(ccxt.binance):
|
||||||
|
def handle_order_book_message(self, client, message, orderbook):
|
||||||
|
asks = self.safe_value(message, 'a', [])
|
||||||
|
bids = self.safe_value(message, 'b', [])
|
||||||
|
# printing high-frequency updates is a resource-heavy task
|
||||||
|
# this print statement is here just to demonstrate the work of it
|
||||||
|
# replace it with you logic for processing individual updates
|
||||||
|
print('Updates:', {
|
||||||
|
'asks': asks,
|
||||||
|
'bids': bids,
|
||||||
|
})
|
||||||
|
return super(MyBinance, self).handle_order_book_message(client, message, orderbook);
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = MyBinance()
|
||||||
|
symbol = 'BTC/USDT'
|
||||||
|
print('Watching', exchange.id, symbol)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
orderbook = await exchange.watch_order_book(symbol)
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
# raise e # uncomment to break all loops in case of an error in any one of them
|
||||||
|
# break # you can also break just this one loop if it fails
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import ccxt.pro
|
||||||
|
from asyncio import run, gather
|
||||||
|
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'orderbook': None,
|
||||||
|
'balance': None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def common_handler(exchange, symbol):
|
||||||
|
market = exchange.market(symbol)
|
||||||
|
base = market['base']
|
||||||
|
quote = market['quote']
|
||||||
|
balance = data['balance']
|
||||||
|
orderbook = data['orderbook']
|
||||||
|
if balance and orderbook:
|
||||||
|
total = balance['total']
|
||||||
|
tip = [ orderbook['asks'][0], orderbook['bids'][0] ]
|
||||||
|
print(exchange.iso8601(exchange.milliseconds()), symbol, 'orderbook:', tip, 'balance:', total)
|
||||||
|
|
||||||
|
|
||||||
|
async def watch_order_book(exchange, symbol):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
data['orderbook'] = await exchange.watch_order_book(symbol)
|
||||||
|
common_handler(exchange, symbol)
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
break # break this loop
|
||||||
|
|
||||||
|
|
||||||
|
async def watch_balance(exchange, symbol):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
data['balance'] = await exchange.watch_balance()
|
||||||
|
common_handler(exchange, symbol)
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
break # break this loop
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.binance({
|
||||||
|
'apiKey': 'YOUR_API_KEY',
|
||||||
|
'secret': 'YOUR_SECRET',
|
||||||
|
})
|
||||||
|
await exchange.load_markets()
|
||||||
|
symbol = 'BTC/USDT'
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
loops = [
|
||||||
|
watch_order_book(exchange, symbol),
|
||||||
|
watch_balance(exchange, symbol)
|
||||||
|
]
|
||||||
|
await gather(*loops)
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
break
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Python
|
||||||
|
import ccxt.pro
|
||||||
|
from asyncio import run, gather
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
|
||||||
|
async def place_delayed_order(exchange, symbol, amount, price):
|
||||||
|
try:
|
||||||
|
await exchange.sleep(5000) # wait a bit
|
||||||
|
order = await exchange.create_limit_buy_order(symbol, amount, price)
|
||||||
|
print(exchange.iso8601(exchange.milliseconds()), 'place_delayed_order')
|
||||||
|
pprint(order)
|
||||||
|
print('---------------------------------------------------------------')
|
||||||
|
except Exception as e:
|
||||||
|
# break
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
|
||||||
|
async def watch_orders_loop(exchange, symbol):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
orders = await exchange.watch_orders(symbol)
|
||||||
|
print(exchange.iso8601(exchange.milliseconds()), 'watch_orders_loop', len(orders), ' last orders cached')
|
||||||
|
print('---------------------------------------------------------------')
|
||||||
|
except Exception as e:
|
||||||
|
# break
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
|
||||||
|
async def watch_balance_loop(exchange):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
balance = await exchange.watch_balance()
|
||||||
|
print(exchange.iso8601(exchange.milliseconds()), 'watch_balance_loop')
|
||||||
|
pprint(balance)
|
||||||
|
print('---------------------------------------------------------------')
|
||||||
|
except Exception as e:
|
||||||
|
# break
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.binanceusdm({
|
||||||
|
'apiKey': 'YOUR_API_KEY',
|
||||||
|
'secret': 'YOUR_SECRET',
|
||||||
|
})
|
||||||
|
symbol = 'BTC/USDT'
|
||||||
|
amount = 0.001
|
||||||
|
price = 11111
|
||||||
|
loops = [
|
||||||
|
watch_orders_loop(exchange, symbol),
|
||||||
|
watch_balance_loop(exchange),
|
||||||
|
place_delayed_order(exchange, symbol, amount, price)
|
||||||
|
]
|
||||||
|
await gather(*loops)
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from asyncio import run, gather
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
sys.path.append(root + '/python')
|
||||||
|
|
||||||
|
import ccxt.pro # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
print('CCXT Version:', ccxt.__version__)
|
||||||
|
|
||||||
|
|
||||||
|
async def print_balance_continuously(exchange):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
print('-----------------------------------------------------------')
|
||||||
|
await exchange.load_markets()
|
||||||
|
balance = await exchange.watch_balance()
|
||||||
|
print(exchange.iso8601(exchange.milliseconds()), exchange.id)
|
||||||
|
for currency, value in balance['total'].items():
|
||||||
|
print(value, currency)
|
||||||
|
except Exception as e:
|
||||||
|
print('-----------------------------------------------------------')
|
||||||
|
print(exchange.iso8601(exchange.milliseconds()), exchange.id, type(e), e)
|
||||||
|
await exchange.sleep(300000) # sleep 5 minutes and retry
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
config = {
|
||||||
|
'apiKey': 'YOUR_API_KEY',
|
||||||
|
'secret': 'YOUR_SECRET',
|
||||||
|
}
|
||||||
|
exchange_ids = [
|
||||||
|
'binance',
|
||||||
|
'binanceusdm',
|
||||||
|
'binancecoinm',
|
||||||
|
]
|
||||||
|
exchanges = [getattr(ccxt.pro, exchange_id)(config) for exchange_id in exchange_ids]
|
||||||
|
printing_loops = [print_balance_continuously(exchange) for exchange in exchanges]
|
||||||
|
await gather(*printing_loops)
|
||||||
|
closing_tasks = [exchange.close() for exchange in exchanges]
|
||||||
|
await gather(*closing_tasks)
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import ccxt.pro
|
||||||
|
from asyncio import run
|
||||||
|
|
||||||
|
|
||||||
|
def table(values):
|
||||||
|
first = values[0]
|
||||||
|
keys = list(first.keys()) if isinstance(first, dict) else range(0, len(first))
|
||||||
|
widths = [max([len(str(v[k])) for v in values]) for k in keys]
|
||||||
|
string = ' | '.join(['{:<' + str(w) + '}' for w in widths])
|
||||||
|
return "\n".join([string.format(*[str(v[k]) for k in keys]) for v in values])
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.bitmex({
|
||||||
|
# 'options': {
|
||||||
|
# 'OHLCVLimit': 1000, # how many candles to store in memory by default
|
||||||
|
# },
|
||||||
|
})
|
||||||
|
symbol = 'BTC/USD'
|
||||||
|
timeframe = '1m' # 5m, 1h, 1d
|
||||||
|
limit = 10 # how many candles to return max
|
||||||
|
method = 'watchOHLCV'
|
||||||
|
if (method in exchange.has) and exchange.has[method]:
|
||||||
|
max_iterations = 100 # how many times to repeat the loop before exiting
|
||||||
|
for i in range(0, max_iterations):
|
||||||
|
try:
|
||||||
|
ohlcvs = await exchange.watch_ohlcv(symbol, timeframe, None, limit)
|
||||||
|
now = exchange.milliseconds()
|
||||||
|
print('\n===============================================================================')
|
||||||
|
print('Loop iteration:', i, 'current time:', exchange.iso8601(now), symbol, timeframe)
|
||||||
|
print('-------------------------------------------------------------------------------')
|
||||||
|
print(table([[exchange.iso8601(o[0])] + o[1:] for o in ohlcvs]))
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
break
|
||||||
|
await exchange.close()
|
||||||
|
else:
|
||||||
|
print(exchange.id, method, 'is not supported or not implemented yet')
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import ccxt.pro
|
||||||
|
from asyncio import run, gather
|
||||||
|
|
||||||
|
|
||||||
|
def table(values):
|
||||||
|
first = values[0]
|
||||||
|
keys = list(first.keys()) if isinstance(first, dict) else range(0, len(first))
|
||||||
|
widths = [max([len(str(v[k])) for v in values]) for k in keys]
|
||||||
|
string = ' | '.join(['{:<' + str(w) + '}' for w in widths])
|
||||||
|
return "\n".join([string.format(*[str(v[k]) for k in keys]) for v in values])
|
||||||
|
|
||||||
|
|
||||||
|
async def watch_ticker(color, duration, exchange, symbol):
|
||||||
|
method = 'watchTicker'
|
||||||
|
if (method in exchange.has) and exchange.has[method]:
|
||||||
|
start = exchange.milliseconds()
|
||||||
|
i = 1
|
||||||
|
while True:
|
||||||
|
ticker = await exchange.watch_ticker(symbol)
|
||||||
|
now = exchange.milliseconds()
|
||||||
|
# start color code
|
||||||
|
print(color, 'Ticker ========================================================================')
|
||||||
|
print(exchange.iso8601(now), symbol, 'iteration:', i, 'last price:', ticker['last'])
|
||||||
|
print('-------------------------------------------------------------------------------')
|
||||||
|
# pprint.pprint(ticker) # uncomment for a lengthy complete printout
|
||||||
|
print('\x1b[0m') # stop color code
|
||||||
|
i += 1
|
||||||
|
if (start + duration) < now:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise Exception(exchange.id + ' ' + method + ' is not supported or not implemented yet')
|
||||||
|
|
||||||
|
|
||||||
|
async def watch_ohlcv(color, duration, exchange, symbol, timeframe, limit):
|
||||||
|
method = 'watchOHLCV'
|
||||||
|
if (method in exchange.has) and exchange.has[method]:
|
||||||
|
start = exchange.milliseconds()
|
||||||
|
i = 1
|
||||||
|
while True:
|
||||||
|
ohlcvs = await exchange.watch_ohlcv(symbol, timeframe, None, limit)
|
||||||
|
now = exchange.milliseconds()
|
||||||
|
# start color code
|
||||||
|
print(color, 'OHLCV =========================================================================')
|
||||||
|
print(exchange.iso8601(now), symbol, timeframe, 'iteration:', i)
|
||||||
|
print('-------------------------------------------------------------------------------')
|
||||||
|
print(table([[exchange.iso8601(o[0])] + o[1:] for o in ohlcvs]))
|
||||||
|
print('\x1b[0m') # stop color code
|
||||||
|
i += 1
|
||||||
|
if (start + duration) < now:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise Exception(exchange.id + ' ' + method + ' is not supported or not implemented yet')
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.bitmex()
|
||||||
|
await exchange.load_markets()
|
||||||
|
duration = 1200000 # run 20 minutes = 1200000 milliseconds
|
||||||
|
symbol = 'BTC/USD'
|
||||||
|
limit = 10
|
||||||
|
loops = [
|
||||||
|
watch_ticker('\033[35m', duration, exchange, symbol), # magenta
|
||||||
|
watch_ohlcv('\x1b[33m', duration, exchange, symbol, '1m', limit), # yellow
|
||||||
|
watch_ohlcv('\x1b[32m', duration, exchange, symbol, '5m', limit), # green
|
||||||
|
]
|
||||||
|
await gather(*loops)
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import ccxt.pro
|
||||||
|
from asyncio import run
|
||||||
|
|
||||||
|
|
||||||
|
print('CCXT Pro version', ccxt.pro.__version__)
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.bitvavo()
|
||||||
|
await exchange.load_markets()
|
||||||
|
exchange.verbose = True
|
||||||
|
symbol = 'BTC/EUR'
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
orderbook = await exchange.watch_order_book(symbol)
|
||||||
|
print(orderbook['nonce'], symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
break
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import asyncio
|
||||||
|
import ccxt.pro
|
||||||
|
|
||||||
|
|
||||||
|
print('CCXT Version:', ccxt.__version__)
|
||||||
|
|
||||||
|
|
||||||
|
async def loop(exchange, symbol, timeframe, complete_candles_only=False):
|
||||||
|
duration_in_seconds = exchange.parse_timeframe(timeframe)
|
||||||
|
duration_in_ms = duration_in_seconds * 1000
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
trades = await exchange.watch_trades(symbol)
|
||||||
|
if len(trades) > 0:
|
||||||
|
current_minute = int(exchange.milliseconds() / duration_in_ms)
|
||||||
|
ohlcvc = exchange.build_ohlcvc(trades, timeframe)
|
||||||
|
if complete_candles_only:
|
||||||
|
ohlcvc = [candle for candle in ohlcvc if int(candle[0] / duration_in_ms) < current_minute]
|
||||||
|
if len(ohlcvc) > 0:
|
||||||
|
print('-----------------------------------------------------------')
|
||||||
|
print("Symbol:", symbol, "timeframe:", timeframe)
|
||||||
|
print(ohlcvc)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{type(e).__name__}: {(str(e))}")
|
||||||
|
# raise type(e)(str(e)) # uncomment to break all loops in case of an error in any one of them
|
||||||
|
# break # you can also break just this one loop if it fails
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
# select the exchange
|
||||||
|
exchange = ccxt.pro.binance()
|
||||||
|
if exchange.has['watchTrades']:
|
||||||
|
markets = await exchange.load_markets()
|
||||||
|
# Change this value accordingly
|
||||||
|
timeframe = '1m'
|
||||||
|
limit = 5
|
||||||
|
selected_symbols = list(markets.values())[:limit]
|
||||||
|
# you can also specify the symbols manually
|
||||||
|
# selected_symbols = ['BTC/USDT', 'ETH/USDT']
|
||||||
|
|
||||||
|
# Use this variable to choose if only complete candles
|
||||||
|
# should be considered
|
||||||
|
complete_candles_only = True
|
||||||
|
await asyncio.gather(*[loop(exchange, symbol['symbol'], timeframe, complete_candles_only)
|
||||||
|
for symbol in selected_symbols])
|
||||||
|
await exchange.close()
|
||||||
|
else:
|
||||||
|
print(exchange.id, 'does not support watchTrades yet')
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import ccxt.pro
|
||||||
|
from asyncio import run
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.coinbase()
|
||||||
|
method = 'watchTrades'
|
||||||
|
print('CCXT Pro version', ccxt.pro.__version__)
|
||||||
|
if exchange.has[method]:
|
||||||
|
last_id = ''
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
trades = await exchange.watch_trades('BTC/USD')
|
||||||
|
for trade in trades:
|
||||||
|
if trade['id'] > last_id:
|
||||||
|
print(exchange.iso8601(exchange.milliseconds()), trade['symbol'], trade['datetime'], trade['price'], trade['amount'])
|
||||||
|
last_id = trade['id']
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# stop
|
||||||
|
await exchange.close()
|
||||||
|
raise e
|
||||||
|
# or retry
|
||||||
|
# pass
|
||||||
|
else:
|
||||||
|
raise Exception(exchange.id + ' ' + method + ' is not supported or not implemented yet')
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import ccxt.pro
|
||||||
|
from asyncio import run
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.coinbase()
|
||||||
|
method = 'watchTrades'
|
||||||
|
print('CCXT Pro version', ccxt.pro.__version__)
|
||||||
|
if exchange.has[method]:
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
trades = await exchange.watch_trades('BTC/USD')
|
||||||
|
num_trades = len(trades)
|
||||||
|
trade = trades[-1]
|
||||||
|
print(exchange.iso8601(exchange.milliseconds()), trade['symbol'], trade['datetime'], trade['price'], trade['amount'], 'stored', num_trades, 'trades in cache')
|
||||||
|
except Exception as e:
|
||||||
|
# stop
|
||||||
|
await exchange.close()
|
||||||
|
raise e
|
||||||
|
# or retry
|
||||||
|
# pass
|
||||||
|
else:
|
||||||
|
raise Exception(exchange.id + ' ' + method + ' is not supported or not implemented yet')
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import ccxt.pro
|
||||||
|
from asyncio import run
|
||||||
|
|
||||||
|
|
||||||
|
async def consume_all_trades(exchange, symbol):
|
||||||
|
await exchange.load_markets()
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
trades = await exchange.watch_trades(symbol)
|
||||||
|
print('----------------------------------------------------------------------')
|
||||||
|
print(exchange.iso8601(exchange.milliseconds()), 'received', len(trades), 'new', symbol, 'trades:')
|
||||||
|
for trade in trades:
|
||||||
|
print(exchange.id, symbol, trade['id'], trade['datetime'], trade['amount'], trade['price'])
|
||||||
|
exchange.trades[symbol].clear()
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
exchange = ccxt.pro.bitmex()
|
||||||
|
symbol = 'BTC/USD'
|
||||||
|
run(consume_all_trades(exchange, symbol))
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import ccxt.pro
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
async def loop(exchange, symbol):
|
||||||
|
since = datetime.utcnow()
|
||||||
|
timestamp = int(since.timestamp() * 1000)
|
||||||
|
while True:
|
||||||
|
trades = await exchange.watch_trades(symbol, since=timestamp)
|
||||||
|
print('--------------------------------------------------------------')
|
||||||
|
print('Received', len(trades), 'after', exchange.iso8601 (timestamp))
|
||||||
|
print('waiting for next update...')
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.gateio()
|
||||||
|
await loop(exchange, 'BTC/USDT')
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
asyncio.run(main())
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from asyncio import run
|
||||||
|
import ccxt.pro
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
|
||||||
|
class MyBinance(ccxt.pro.binance):
|
||||||
|
def handle_ohlcv(self, client, message):
|
||||||
|
# add your handling of the original message here
|
||||||
|
print('intercepted', message)
|
||||||
|
return super(MyBinance, self).handle_ohlcv(client, message)
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = MyBinance()
|
||||||
|
symbol = 'BTC/USDT'
|
||||||
|
print('Watching', exchange.id, symbol)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
ohlcv = await exchange.watch_ohlcv(symbol, '1m')
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
# raise e # uncomment to break all loops in case of an error in any one of them
|
||||||
|
# break # you can also break just this one loop if it fails
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print('CCXT Version:', ccxt.__version__)
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import ccxt.pro
|
||||||
|
from asyncio import gather, run
|
||||||
|
|
||||||
|
|
||||||
|
async def symbol_loop(exchange, symbol):
|
||||||
|
print('Starting the', exchange.id, 'symbol loop with', symbol)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
orderbook = await exchange.watch_order_book(symbol)
|
||||||
|
now = exchange.milliseconds()
|
||||||
|
print(exchange.iso8601(now), exchange.id, symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
# raise e # uncomment to break all loops in case of an error in any one of them
|
||||||
|
break # you can break just this one loop if it fails
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.kucoin({
|
||||||
|
"apiKey": "YOUR_API_KEY",
|
||||||
|
"secret": "YOUR_API_SECRET",
|
||||||
|
"password": "YOUR_API_PASSWORD",
|
||||||
|
})
|
||||||
|
symbols = ['KDA/USDT', 'KDA/BTC', 'BTC/USDT']
|
||||||
|
loops = [symbol_loop(exchange, symbol) for symbol in symbols]
|
||||||
|
await gather(*loops)
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import ccxt.pro
|
||||||
|
from asyncio import gather, run
|
||||||
|
|
||||||
|
|
||||||
|
async def symbol_loop(exchange, method, symbol):
|
||||||
|
print('Starting', exchange.id, method, symbol)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
response = await getattr(exchange, method)(symbol)
|
||||||
|
now = exchange.milliseconds()
|
||||||
|
iso8601 = exchange.iso8601(now)
|
||||||
|
if method == 'watchOrderBook':
|
||||||
|
print(iso8601, exchange.id, method, symbol, response['asks'][0], response['bids'][0])
|
||||||
|
elif method == 'watchTicker':
|
||||||
|
print(iso8601, exchange.id, method, symbol, response['high'], response['low'], response['bid'], response['ask'])
|
||||||
|
elif method == 'watchTrades':
|
||||||
|
print(iso8601, exchange.id, method, symbol, len(response), 'trades')
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
# raise e # uncomment to break all loops in case of an error in any one of them
|
||||||
|
break # you can break just this one loop if it fails
|
||||||
|
|
||||||
|
|
||||||
|
async def method_loop(exchange, method, symbols):
|
||||||
|
print('Starting', exchange.id, method, symbols)
|
||||||
|
loops = [symbol_loop(exchange, method, symbol) for symbol in symbols]
|
||||||
|
await gather(*loops)
|
||||||
|
|
||||||
|
|
||||||
|
async def exchange_loop(exchange_id, methods):
|
||||||
|
print('Starting', exchange_id, methods)
|
||||||
|
exchange = getattr(ccxt.pro, exchange_id)()
|
||||||
|
loops = [method_loop(exchange, method, symbols) for method, symbols in methods.items()]
|
||||||
|
await gather(*loops)
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchanges = {
|
||||||
|
'okex': {
|
||||||
|
'watchOrderBook': ['BTC/USDT', 'ETH/BTC', 'ETH/USDT'],
|
||||||
|
'watchTicker': ['BTC/USDT'],
|
||||||
|
},
|
||||||
|
'binance': {
|
||||||
|
'watchOrderBook': ['BTC/USDT', 'ETH/BTC'],
|
||||||
|
'watchTrades': [ 'ETH/BTC' ],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
loops = [exchange_loop(exchange_id, methods) for exchange_id, methods in exchanges.items()]
|
||||||
|
await gather(*loops)
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import ccxt.pro
|
||||||
|
|
||||||
|
|
||||||
|
orderbooks = {}
|
||||||
|
|
||||||
|
|
||||||
|
def handle_all_orderbooks(orderbooks):
|
||||||
|
print('We have the following orderbooks:')
|
||||||
|
for exchange_id, orderbooks_by_symbol in orderbooks.items():
|
||||||
|
for symbol in orderbooks_by_symbol.keys():
|
||||||
|
orderbook = orderbooks_by_symbol[symbol]
|
||||||
|
print(ccxt.pro.Exchange.iso8601(orderbook['timestamp']), exchange_id, symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
|
||||||
|
|
||||||
|
async def symbol_loop(exchange, symbol):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
orderbook = await exchange.watch_order_book(symbol)
|
||||||
|
orderbooks[exchange.id] = orderbooks.get(exchange.id, {})
|
||||||
|
orderbooks[exchange.id][symbol] = orderbook
|
||||||
|
print('===========================================================')
|
||||||
|
#
|
||||||
|
# here you can do what you want
|
||||||
|
# with the most recent versions of each orderbook you have so far
|
||||||
|
#
|
||||||
|
# you can also wait until all of them are available
|
||||||
|
# by just looking into all the orderbooks and counting them
|
||||||
|
#
|
||||||
|
# we just print them here to keep this example simple
|
||||||
|
#
|
||||||
|
handle_all_orderbooks(orderbooks)
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
# raise e # uncomment to break all loops in case of an error in any one of them
|
||||||
|
break # you can break just this one loop if it fails
|
||||||
|
|
||||||
|
|
||||||
|
async def exchange_loop(exchange_id, symbols):
|
||||||
|
exchange = getattr(ccxt.pro, exchange_id)()
|
||||||
|
loops = [symbol_loop(exchange, symbol) for symbol in symbols]
|
||||||
|
await asyncio.gather(*loops)
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
symbols = ['BTC/USDT', 'ETH/BTC']
|
||||||
|
# symbols = []
|
||||||
|
exchanges = {
|
||||||
|
'okex': symbols + ['ETH/USDT'],
|
||||||
|
'binance': symbols,
|
||||||
|
}
|
||||||
|
loops = [exchange_loop(exchange_id, symbols) for exchange_id, symbols in exchanges.items()]
|
||||||
|
await asyncio.gather(*loops)
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import ccxt.pro
|
||||||
|
from asyncio import run, gather, sleep
|
||||||
|
|
||||||
|
|
||||||
|
orderbooks = {}
|
||||||
|
|
||||||
|
|
||||||
|
def handle_all_orderbooks(orderbooks):
|
||||||
|
print('We have the following orderbooks:')
|
||||||
|
for exchange_id, orderbooks_by_symbol in orderbooks.items():
|
||||||
|
for symbol in orderbooks_by_symbol.keys():
|
||||||
|
orderbook = orderbooks_by_symbol[symbol]
|
||||||
|
print(ccxt.pro.Exchange.iso8601(orderbook['timestamp']), exchange_id, symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
|
||||||
|
|
||||||
|
async def handling_loop(orderbooks):
|
||||||
|
delay = 5
|
||||||
|
while True:
|
||||||
|
await sleep(delay)
|
||||||
|
handle_all_orderbooks(orderbooks)
|
||||||
|
|
||||||
|
|
||||||
|
async def symbol_loop(exchange, symbol):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
orderbook = await exchange.watch_order_book(symbol)
|
||||||
|
orderbooks[exchange.id] = orderbooks.get(exchange.id, {})
|
||||||
|
orderbooks[exchange.id][symbol] = orderbook
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
# raise e # uncomment to break all loops in case of an error in any one of them
|
||||||
|
break # you can break just this one loop if it fails
|
||||||
|
|
||||||
|
|
||||||
|
async def exchange_loop(exchange_id, symbols):
|
||||||
|
exchange = getattr(ccxt.pro, exchange_id)()
|
||||||
|
loops = [symbol_loop(exchange, symbol) for symbol in symbols]
|
||||||
|
await gather(*loops)
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
symbols = ['BTC/USDT', 'ETH/BTC']
|
||||||
|
# symbols = []
|
||||||
|
exchanges = {
|
||||||
|
'okex': symbols + ['ETH/USDT'],
|
||||||
|
'binance': symbols,
|
||||||
|
}
|
||||||
|
loops = [exchange_loop(exchange_id, symbols) for exchange_id, symbols in exchanges.items()]
|
||||||
|
loops += [handling_loop(orderbooks)]
|
||||||
|
await gather(*loops)
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import ccxt.pro
|
||||||
|
from asyncio import gather, run
|
||||||
|
|
||||||
|
|
||||||
|
async def symbol_loop(exchange, method, symbol):
|
||||||
|
print('Starting', exchange.id, method, symbol)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
response = await getattr(exchange, method)(symbol)
|
||||||
|
now = exchange.milliseconds()
|
||||||
|
iso8601 = exchange.iso8601(now)
|
||||||
|
if method == 'watchOrderBook':
|
||||||
|
print(iso8601, exchange.id, method, symbol, response['asks'][0], response['bids'][0])
|
||||||
|
elif method == 'watchTicker':
|
||||||
|
print(iso8601, exchange.id, method, symbol, response['high'], response['low'], response['bid'], response['ask'])
|
||||||
|
elif method == 'watchTrades':
|
||||||
|
print(iso8601, exchange.id, method, symbol, len(response), 'trades')
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
# raise e # uncomment to break all loops in case of an error in any one of them
|
||||||
|
break # you can break just this one loop if it fails
|
||||||
|
|
||||||
|
|
||||||
|
async def symbols_method_loop(exchange, method, symbols):
|
||||||
|
print('Starting', exchange.id, method, symbols)
|
||||||
|
loops = [symbol_loop(exchange, method, symbol) for symbol in symbols]
|
||||||
|
await gather(*loops)
|
||||||
|
|
||||||
|
|
||||||
|
async def method_loop(exchange, method):
|
||||||
|
print('Starting', exchange.id, method)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
response = await getattr(exchange, method)()
|
||||||
|
now = exchange.milliseconds()
|
||||||
|
iso8601 = exchange.iso8601(now)
|
||||||
|
print(iso8601, exchange.id, method, response)
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
# raise e # uncomment to break all loops in case of an error in any one of them
|
||||||
|
break # you can break just this one loop if it fails
|
||||||
|
|
||||||
|
|
||||||
|
async def exchange_loop(exchange_id, methods, config={}):
|
||||||
|
print('Starting', exchange_id, methods)
|
||||||
|
exchange = getattr(ccxt.pro, exchange_id)()
|
||||||
|
for attr, value in config.items():
|
||||||
|
setattr(exchange, attr, value)
|
||||||
|
loops = [symbols_method_loop(exchange, method, symbols) if len(symbols) else method_loop(exchange, method) for method, symbols in methods.items()]
|
||||||
|
await gather(*loops)
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
keys = {
|
||||||
|
'okex': {
|
||||||
|
'apiKey': 'YOUR_API_KEY',
|
||||||
|
'secret': 'YOUR_SECRET',
|
||||||
|
},
|
||||||
|
'binance': {
|
||||||
|
'apiKey': 'YOUR_API_KEY',
|
||||||
|
'secret': 'YOUR_SECRET',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
exchanges = {
|
||||||
|
'okex': {
|
||||||
|
'watchOrderBook': ['BTC/USDT', 'ETH/BTC', 'ETH/USDT'],
|
||||||
|
'watchTicker': ['BTC/USDT'],
|
||||||
|
'watchBalance': [],
|
||||||
|
},
|
||||||
|
'binance': {
|
||||||
|
'watchOrderBook': ['BTC/USDT', 'ETH/BTC'],
|
||||||
|
'watchTrades': [ 'ETH/BTC' ],
|
||||||
|
'watchBalance': [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
loops = [exchange_loop(exchange_id, methods, keys.get(exchange_id, {})) for exchange_id, methods in exchanges.items()]
|
||||||
|
await gather(*loops)
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import ccxt.pro
|
||||||
|
from asyncio import gather, run
|
||||||
|
|
||||||
|
|
||||||
|
async def symbol_loop(exchange, symbol):
|
||||||
|
print('Starting the', exchange.id, 'symbol loop with', symbol)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
orderbook = await exchange.watch_order_book(symbol)
|
||||||
|
now = exchange.milliseconds()
|
||||||
|
print(exchange.iso8601(now), exchange.id, symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
# raise e # uncomment to break all loops in case of an error in any one of them
|
||||||
|
break # you can break just this one loop if it fails
|
||||||
|
|
||||||
|
async def exchange_loop(exchange_id, symbols):
|
||||||
|
print('Starting the', exchange_id, 'exchange loop with', symbols)
|
||||||
|
exchange = getattr(ccxt.pro, exchange_id)()
|
||||||
|
loops = [symbol_loop(exchange, symbol) for symbol in symbols]
|
||||||
|
await gather(*loops)
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchanges = {
|
||||||
|
'okex': ['BTC/USDT', 'ETH/BTC', 'ETH/USDT'],
|
||||||
|
'binance': ['BTC/USDT', 'ETH/BTC'],
|
||||||
|
}
|
||||||
|
loops = [exchange_loop(exchange_id, symbols) for exchange_id, symbols in exchanges.items()]
|
||||||
|
await gather(*loops)
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import ccxt.pro
|
||||||
|
from asyncio import gather, run
|
||||||
|
|
||||||
|
|
||||||
|
async def symbol_loop(exchange, symbol):
|
||||||
|
print('Starting the', exchange.id, 'symbol loop with', symbol)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
trades = await exchange.watch_trades(symbol)
|
||||||
|
now = exchange.milliseconds()
|
||||||
|
print(exchange.iso8601(now), exchange.id, symbol, len(trades), trades[-1]['price'])
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
# raise e # uncomment to break all loops in case of an error in any one of them
|
||||||
|
break # you can break just this one loop if it fails
|
||||||
|
|
||||||
|
|
||||||
|
async def exchange_loop(exchange_id, symbols):
|
||||||
|
print('Starting the', exchange_id, 'exchange loop with', symbols)
|
||||||
|
exchange = getattr(ccxt.pro, exchange_id)({
|
||||||
|
'newUpdates': True, # https://github.com/ccxt/ccxt/wiki/ccxt.pro.manual#incremental-data-structures
|
||||||
|
})
|
||||||
|
loops = [symbol_loop(exchange, symbol) for symbol in symbols]
|
||||||
|
await gather(*loops)
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchanges = {
|
||||||
|
'okex': ['BTC/USDT', 'ETH/BTC', 'ETH/USDT'],
|
||||||
|
'binance': ['BTC/USDT', 'ETH/BTC'],
|
||||||
|
}
|
||||||
|
loops = [exchange_loop(exchange_id, symbols) for exchange_id, symbols in exchanges.items()]
|
||||||
|
await gather(*loops)
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import ccxt.pro
|
||||||
|
|
||||||
|
|
||||||
|
async def loop(exchange_id, symbol):
|
||||||
|
exchange = getattr(ccxt.pro, exchange_id)()
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
orderbook = await exchange.watch_order_book(symbol)
|
||||||
|
now = exchange.milliseconds()
|
||||||
|
print(exchange.iso8601(now), exchange.id, symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
# raise e # uncomment to break all loops in case of an error in any one of them
|
||||||
|
break # you can break just this one loop if it fails
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
symbols = {
|
||||||
|
'kraken': 'BTC/USDT',
|
||||||
|
'binance': 'BTC/USDT',
|
||||||
|
'bitmex': 'XBT_USDT',
|
||||||
|
}
|
||||||
|
await asyncio.gather(*[loop(exchange_id, symbol) for exchange_id, symbol in symbols.items()])
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import ccxt.pro
|
||||||
|
import asyncio
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
async def watch_book(exchange, ticker):
|
||||||
|
last = None
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
orderbook = await exchange.watch_order_book(ticker)
|
||||||
|
# TODO add timeout within a minute
|
||||||
|
# TODO add last
|
||||||
|
top_bid = orderbook['bids'][0][0]
|
||||||
|
if last != top_bid:
|
||||||
|
print(f'{int(time.time() * 1000)} top bid for celo on {exchange.name} is {top_bid}')
|
||||||
|
last = top_bid
|
||||||
|
except Exception as e:
|
||||||
|
print(f'{exchange.name} failed {type(e)} {e}')
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange_ids = ['coinbasepro', 'okcoin', 'bittrex']
|
||||||
|
exchanges = [getattr(ccxt.pro, exchange_id)() for exchange_id in exchange_ids]
|
||||||
|
try:
|
||||||
|
done, pending = await asyncio.wait({watch_book(exchange, 'CELO/USD') for exchange in exchanges}, return_when=asyncio.FIRST_EXCEPTION)
|
||||||
|
for completed in done:
|
||||||
|
# trigger the exception here
|
||||||
|
completed.result()
|
||||||
|
except Exception as e:
|
||||||
|
print(f'closing all exchanges because of exception {type(e)} {e}')
|
||||||
|
await asyncio.gather(*[exchange.close() for exchange in exchanges])
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from asyncio import run
|
||||||
|
import ccxt.pro
|
||||||
|
|
||||||
|
|
||||||
|
print('CCXT Pro Version: ', ccxt.pro.__version__)
|
||||||
|
|
||||||
|
exchange = ccxt.pro.okex({
|
||||||
|
'apiKey': 'YOUR_API_KEY',
|
||||||
|
'secret': 'YOUR_API_SECRET',
|
||||||
|
'password': 'YOUR_API_PASSWORD',
|
||||||
|
'options': { 'defaultType': 'swap' },
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
await exchange.load_markets()
|
||||||
|
# exchange.verbose = True # uncomment for debugging
|
||||||
|
|
||||||
|
# https://github.com/ccxt/ccxt/wiki/Manual#overriding-unified-params
|
||||||
|
# https://www.okex.com/docs/en/#swap-swap---orders
|
||||||
|
|
||||||
|
symbol = 'BTC/USDT:USDT'
|
||||||
|
amount = 1 # how may contracts
|
||||||
|
price = None # or your limit price
|
||||||
|
side = 'buy' # or 'sell'
|
||||||
|
future_type = '1' # 1 open long, 2 open short, 3 close long, 4 close short for futures
|
||||||
|
order_type = '4' # 0 = limit order, 4 = market order
|
||||||
|
|
||||||
|
try:
|
||||||
|
# open long market price order
|
||||||
|
order = await exchange.create_order(symbol, 'market', side, amount, price, {'type': future_type})
|
||||||
|
# --------------------------------------------------------------------
|
||||||
|
# open long market price order
|
||||||
|
# const order = await exchange.create_order(symbol, type, side, amount, price, {'order_type': order_type})
|
||||||
|
# --------------------------------------------------------------------
|
||||||
|
# close short market price order
|
||||||
|
# const order = await exchange.create_order(symbol, 'market', side, amount, price, {'type': future_type, 'order_type': order_type})
|
||||||
|
# --------------------------------------------------------------------
|
||||||
|
# close short market price order
|
||||||
|
# const order = await exchange.create_order(symbol, '4', side, amount, price, {'order_type': order_type})
|
||||||
|
# ...
|
||||||
|
print(order)
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import ccxt.pro as ccxt
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.okex({
|
||||||
|
'apiKey': 'YOUR_API_KEY',
|
||||||
|
'secret': 'YOUR_SECRET',
|
||||||
|
# okex requires this: https://github.com/ccxt/ccxt/wiki/Manual#authentication
|
||||||
|
'password': 'YOUR_API_PASSWORD',
|
||||||
|
# comment it out if you don't want debug output
|
||||||
|
# this is for the demo purpose only (to show the communication)
|
||||||
|
'verbose': True,
|
||||||
|
})
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
balance = await exchange.watch_balance({
|
||||||
|
# okex watch_balance requires a symbol or an instrument_id
|
||||||
|
'symbol': 'BTC/USDT',
|
||||||
|
'type': 'margin',
|
||||||
|
})
|
||||||
|
# it will print the balance update when the balance changes
|
||||||
|
# if the balance remains unchanged the exchange will not send it
|
||||||
|
pprint(balance)
|
||||||
|
except Exception as e:
|
||||||
|
print('watch_balance() failed')
|
||||||
|
print(e)
|
||||||
|
break
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import ccxt.pro as ccxt
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.okex({
|
||||||
|
'apiKey': 'YOUR_API_KEY',
|
||||||
|
'secret': 'YOUR_SECRET',
|
||||||
|
# okex requires this: https://github.com/ccxt/ccxt/wiki/Manual#authentication
|
||||||
|
'password': 'YOUR_API_PASSWORD',
|
||||||
|
'options': {
|
||||||
|
'watchBalance': 'margin',
|
||||||
|
},
|
||||||
|
# comment it out if you don't want debug output
|
||||||
|
# this is for the demo purpose only (to show the communication)
|
||||||
|
'verbose': True,
|
||||||
|
})
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
balance = await exchange.watch_balance({
|
||||||
|
# okex watch_balance requires a symbol or an instrument_id
|
||||||
|
'symbol': 'BTC/USDT',
|
||||||
|
})
|
||||||
|
# it will print the balance update when the balance changes
|
||||||
|
# if the balance remains unchanged the exchange will not send it
|
||||||
|
pprint(balance)
|
||||||
|
except Exception as e:
|
||||||
|
print('watch_balance() failed')
|
||||||
|
print(e)
|
||||||
|
break
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import ccxt.pro
|
||||||
|
from asyncio import run
|
||||||
|
|
||||||
|
|
||||||
|
print('CCXT Pro version', ccxt.pro.__version__)
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.okx({
|
||||||
|
'options': {
|
||||||
|
'watchOrderBook': {
|
||||||
|
'depth': 'bbo-tbt', # tick-by-tick best bidask
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
markets = await exchange.load_markets()
|
||||||
|
# exchange.verbose = True # uncomment for debugging purposes if necessary
|
||||||
|
symbol = 'BTC/USDT'
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
# use this:
|
||||||
|
# orderbook = await exchange.watch_order_book(symbol)
|
||||||
|
# print(orderbook['datetime'], symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
# or this:
|
||||||
|
ticker = await exchange.watch_ticker(symbol)
|
||||||
|
print(ticker['datetime'], symbol, [ticker['ask'], ticker['askVolume']], [ticker['bid'], ticker['bidVolume']])
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
break
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import ccxt.pro
|
||||||
|
from asyncio import run, ensure_future
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
|
||||||
|
print('CCXT Version:', ccxt.__version__)
|
||||||
|
|
||||||
|
|
||||||
|
# on_connected() is called when a client connection is established
|
||||||
|
# note that the exchange will reuse the same client connection
|
||||||
|
# some exchanges might require two or more public/private connections
|
||||||
|
# therefore on_connected() may be called more than once
|
||||||
|
|
||||||
|
class MyBinance(ccxt.pro.binance):
|
||||||
|
def on_connected(self, client, message=None):
|
||||||
|
print('Connected to', client.url)
|
||||||
|
ensure_future(create_order(self))
|
||||||
|
|
||||||
|
|
||||||
|
async def create_order(exchange):
|
||||||
|
symbol = 'BTC/USDT'
|
||||||
|
type = 'limit'
|
||||||
|
side = 'buy'
|
||||||
|
amount = 123.45 # change for your values
|
||||||
|
price = 54.321 # change for your values
|
||||||
|
params = {}
|
||||||
|
try:
|
||||||
|
order = await exchange.create_order(symbol, type, side, amount, price, params)
|
||||||
|
print('--------------------------------------------------------------')
|
||||||
|
print('create_order():')
|
||||||
|
pprint(order)
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
|
||||||
|
|
||||||
|
async def watch_orders(exchange):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
orders = await exchange.watch_orders()
|
||||||
|
print('--------------------------------------------------------------')
|
||||||
|
print('watch_orders():')
|
||||||
|
pprint(orders)
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
break
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
exchange = MyBinance({
|
||||||
|
'apiKey': 'YOUR_API_KEY',
|
||||||
|
'secret': 'YOUR_SECRET',
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
run(watch_orders(exchange))
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import ccxt.pro
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
|
||||||
|
async def watch_order_book(exchange, symbol):
|
||||||
|
while True:
|
||||||
|
orderbook = await exchange.watch_order_book(symbol)
|
||||||
|
print(orderbook['datetime'], symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
|
||||||
|
|
||||||
|
async def watch_trades(exchange, symbol):
|
||||||
|
while True:
|
||||||
|
trades = await exchange.watch_trades(symbol)
|
||||||
|
last = trades[-1]
|
||||||
|
print(last['datetime'], last['price'], last['amount'])
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.bitstamp()
|
||||||
|
await exchange.load_markets()
|
||||||
|
symbol = 'BTC/USD'
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
loops = [
|
||||||
|
watch_order_book(exchange, symbol),
|
||||||
|
watch_trades(exchange, symbol)
|
||||||
|
]
|
||||||
|
await asyncio.gather(*loops)
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
break
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from asyncio import run
|
||||||
|
import ccxt.pro
|
||||||
|
|
||||||
|
async def loop(exchange, symbol):
|
||||||
|
await exchange.throttle(10)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
orderbook = await exchange.watch_order_book(symbol)
|
||||||
|
now = exchange.milliseconds()
|
||||||
|
print(exchange.iso8601(now), symbol, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
# raise e # uncomment to break all loops in case of an error in any one of them
|
||||||
|
# break # you can also break just this one loop if it fails
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.ftx()
|
||||||
|
symbols = ['BTC/USDT', 'ETH/USDT', 'ETH/BTC']
|
||||||
|
await asyncio.gather(*[loop(exchange, symbol) for symbol in symbols])
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import ccxt.pro
|
||||||
|
from asyncio import run
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
print('CCXT Version:', ccxt.__version__)
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.phemex({
|
||||||
|
'apiKey': 'YOUR_API_KEY',
|
||||||
|
'secret': 'YOUR_SECRET',
|
||||||
|
})
|
||||||
|
markets = await exchange.load_markets()
|
||||||
|
# exchange.verbose = True # uncomment for debugging purposes if necessary
|
||||||
|
try:
|
||||||
|
symbol = 'UNI/USDT'
|
||||||
|
response = await exchange.cancel_all_orders(symbol)
|
||||||
|
pprint(response)
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||||
|
sys.path.append(root + '/python')
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
import ccxt.pro
|
||||||
|
|
||||||
|
print('CCXT Version:', ccxt.pro.__version__)
|
||||||
|
|
||||||
|
orderbooks = {}
|
||||||
|
|
||||||
|
def handle_all_orderbooks(exchange, orderbooks, spot, future):
|
||||||
|
if spot in orderbooks and future in orderbooks:
|
||||||
|
spot_order_book = orderbooks[spot]
|
||||||
|
future_order_book = orderbooks[future]
|
||||||
|
timestamp = exchange.milliseconds()
|
||||||
|
spot_lag = abs(timestamp - spot_order_book['timestamp']) if spot_order_book['timestamp'] else 10000
|
||||||
|
future_lag = abs(timestamp - future_order_book['timestamp']) if future_order_book['timestamp'] else 10000
|
||||||
|
if spot_lag >= 10000 or future_lag >= 10000:
|
||||||
|
print('Lag > 10 seconds')
|
||||||
|
|
||||||
|
async def symbol_loop(exchange, symbol, spot, future):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
orderbook = await exchange.watch_order_book(symbol)
|
||||||
|
orderbooks[symbol] = orderbook
|
||||||
|
print(exchange.id, '{:13s}'.format(symbol), orderbook['datetime'], orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
#
|
||||||
|
# here you can do what you want
|
||||||
|
# with the most recent versions of each orderbook you have so far
|
||||||
|
#
|
||||||
|
# you can also wait until all of them are available
|
||||||
|
# by just looking into all the orderbooks and counting them
|
||||||
|
#
|
||||||
|
# we just print them here to keep this example simple
|
||||||
|
#
|
||||||
|
handle_all_orderbooks(exchange, orderbooks, spot, future)
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
# raise e # uncomment to break all loops in case of an error in any one of them
|
||||||
|
break # you can break just this one loop if it fails
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
spot = 'BTC/USDT'
|
||||||
|
future = 'BTC/USDT:USDT'
|
||||||
|
symbols = [ spot, future ]
|
||||||
|
exchange = ccxt.pro.bitmart()
|
||||||
|
loops = [
|
||||||
|
symbol_loop(exchange, spot, spot, future),
|
||||||
|
symbol_loop(exchange, future, spot, future),
|
||||||
|
]
|
||||||
|
await asyncio.gather(*loops)
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import ccxt.pro
|
||||||
|
|
||||||
|
|
||||||
|
async def loop(exchange, symbol, n):
|
||||||
|
i = 0
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
orderbook = await exchange.watch_order_book(symbol)
|
||||||
|
# print every 100th bidask to avoid wasting CPU cycles on printing
|
||||||
|
if not i % 100:
|
||||||
|
# i = how many updates there were in total
|
||||||
|
# n = the number of the pair to count subscriptions
|
||||||
|
now = exchange.milliseconds()
|
||||||
|
print(exchange.iso8601(now), n, symbol, i, orderbook['asks'][0], orderbook['bids'][0])
|
||||||
|
i += 1
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
# raise e # uncomment to break all loops in case of an error in any one of them
|
||||||
|
# break # you can also break just this one loop if it fails
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = ccxt.pro.kraken()
|
||||||
|
await exchange.load_markets()
|
||||||
|
markets = list(exchange.markets.values())
|
||||||
|
symbols = [market['symbol'] for market in markets if not market['darkpool']]
|
||||||
|
await asyncio.gather(*[loop(exchange, symbol, n) for n, symbol in enumerate(symbols)])
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import asyncio
|
||||||
|
import ccxt.pro
|
||||||
|
|
||||||
|
|
||||||
|
class MyBinance(ccxt.pro.binance):
|
||||||
|
|
||||||
|
def handle_mini_ticker(self, client, message):
|
||||||
|
market_id = self.safe_string_lower(message, 's')
|
||||||
|
message_hash = market_id + '@miniTicker'
|
||||||
|
client.resolve(message, message_hash)
|
||||||
|
|
||||||
|
def handle_message(self, client, message):
|
||||||
|
handlers = {
|
||||||
|
'24hrMiniTicker': self.handle_mini_ticker,
|
||||||
|
# add other custom handlers here
|
||||||
|
}
|
||||||
|
e = self.safe_string(message, 'e')
|
||||||
|
method = self.safe_value(handlers, e)
|
||||||
|
if method:
|
||||||
|
return method(client, message)
|
||||||
|
else:
|
||||||
|
return super(MyBinance, self).handle_message(client, message)
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchange = MyBinance({
|
||||||
|
'enableRateLimit': False,
|
||||||
|
'options': {
|
||||||
|
'defaultType': 'future'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
await exchange.load_markets()
|
||||||
|
# exchange.verbose = True # uncomment for debugging purposes
|
||||||
|
market = exchange.market('BTC/USDT')
|
||||||
|
message_hash = market['lowercaseId'] + '@miniTicker'
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
print(await exchange.watch_public(message_hash))
|
||||||
|
except Exception as e:
|
||||||
|
print(type(e).__name__, str(e))
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
asyncio.run(main())
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from asyncio import run, gather
|
||||||
|
import ccxt.pro
|
||||||
|
|
||||||
|
|
||||||
|
print('CCXT Version:', ccxt.__version__)
|
||||||
|
|
||||||
|
|
||||||
|
async def exchange_loop(exchange_id, symbols):
|
||||||
|
exchange = getattr(ccxt.pro, exchange_id)()
|
||||||
|
markets = await exchange.load_markets()
|
||||||
|
await gather(*[watch_ticker_loop(exchange, symbol) for symbol in symbols])
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
async def watch_ticker_loop(exchange, symbol):
|
||||||
|
# exchange.verbose = True # uncomment for debugging purposes if necessary
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
ticker = await exchange.watch_ticker(symbol)
|
||||||
|
now = exchange.milliseconds()
|
||||||
|
print(exchange.iso8601(now), exchange.id, symbol, 'bid:', ticker['bid'], 'ask:', ticker['ask'], 'last:', ticker['last'], 'on', ticker['datetime'])
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
# raise e # uncomment to break all loops in case of an error in any one of them
|
||||||
|
break # you can break just this one loop if it fails
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchanges = {
|
||||||
|
'binance': [ 'BTC/USDT', 'ETH/USDT' ],
|
||||||
|
'ftx': [ 'BTC/USD', 'ETH/USD' ],
|
||||||
|
}
|
||||||
|
loops = [exchange_loop(exchange_id, symbols) for exchange_id, symbols in exchanges.items()]
|
||||||
|
await gather(*loops)
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from asyncio import gather, run
|
||||||
|
import ccxt.pro
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
|
||||||
|
async def watch_ticker_continuously(exchange, symbol):
|
||||||
|
filename = exchange.id + '-' + symbol.replace('/', '-') + '.csv'
|
||||||
|
print('Watching', exchange.id, symbol, filename)
|
||||||
|
keys = ['index', 'exchange', 'symbol', 'timestamp', 'open', 'high', 'low', 'close', 'baseVolume']
|
||||||
|
with open(filename, 'w') as file:
|
||||||
|
file.write(','.join(keys) + "\n")
|
||||||
|
index = 0
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
ticker = await exchange.watch_ticker(symbol)
|
||||||
|
values = [str(index), exchange.id] + [str(ticker[key]) for key in keys[2:]]
|
||||||
|
print(*values)
|
||||||
|
with open(filename, 'a') as file:
|
||||||
|
file.write(','.join(values) + "\n")
|
||||||
|
index += 1
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
|
||||||
|
async def watch_tickers_continuously(exchange_id, overrides, symbols):
|
||||||
|
exchange_class = getattr(ccxt.pro, exchange_id)
|
||||||
|
exchange = exchange_class(overrides)
|
||||||
|
coroutines = [watch_ticker_continuously(exchange, symbol) for symbol in symbols]
|
||||||
|
await gather(*coroutines)
|
||||||
|
await exchange.close()
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
exchanges = {
|
||||||
|
'binance': {'options': {'defaultType': 'future'}},
|
||||||
|
'huobipro': {}
|
||||||
|
}
|
||||||
|
symbols = ['BTC/USDT', 'ETH/USDT', 'LTC/USDT', 'XRP/USDT', 'BCH/USDT']
|
||||||
|
coroutines = [watch_tickers_continuously(exchange_id, exchanges[exchange_id], symbols) for exchange_id in exchanges.keys()]
|
||||||
|
return await gather(*coroutines)
|
||||||
|
|
||||||
|
|
||||||
|
run(main())
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CCXT Basic example for the browser</title>
|
||||||
|
<script type="text/javascript" src="https://unpkg.com/ccxt"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
document.addEventListener ("DOMContentLoaded", function () {
|
||||||
|
|
||||||
|
alert ('ccxt version ' + ccxt.version);
|
||||||
|
|
||||||
|
const exchange = new ccxt.bittrex ({
|
||||||
|
'proxyUrl': 'https://cors-anywhere.herokuapp.com/'
|
||||||
|
})
|
||||||
|
|
||||||
|
const symbol = 'BTC/USDT'
|
||||||
|
|
||||||
|
exchange.fetchTicker (symbol).then (ticker => {
|
||||||
|
|
||||||
|
const text = [
|
||||||
|
exchange.id,
|
||||||
|
symbol,
|
||||||
|
JSON.stringify (ticker, undefined, '\n\t')
|
||||||
|
]
|
||||||
|
|
||||||
|
document.getElementById ('content').innerHTML = text.join (' ')
|
||||||
|
|
||||||
|
}).catch (e => {
|
||||||
|
|
||||||
|
const text = [
|
||||||
|
e.constructor.name,
|
||||||
|
e.message,
|
||||||
|
]
|
||||||
|
|
||||||
|
document.getElementById ('content').innerHTML = text.join (' ')
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Hello, CCXT!</h1>
|
||||||
|
<pre id="content"></pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CCXT Basic example for the browser</title>
|
||||||
|
<script type="text/javascript" src="https://unpkg.com/ccxt"></script>
|
||||||
|
<style type="text/css">
|
||||||
|
#contentA { background-color: #ccccff; }
|
||||||
|
#contentB { background-color: #ffcccc; }
|
||||||
|
</style>
|
||||||
|
<script>'use strict'
|
||||||
|
|
||||||
|
class MyExchange extends ccxt.coinbasepro {
|
||||||
|
|
||||||
|
async fetchTicker (symbol, params = {}) {
|
||||||
|
alert ("I'm about to call the parent method from the overrided class, woohooo!")
|
||||||
|
// just call the parent method and that's it
|
||||||
|
return super.fetchTicker (symbol, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener ("DOMContentLoaded", function () {
|
||||||
|
|
||||||
|
const exchange = new MyExchange ()
|
||||||
|
|
||||||
|
const symbol = 'ETH/BTC'
|
||||||
|
|
||||||
|
const showFetchedTicker = function (ticker, elementId) {
|
||||||
|
|
||||||
|
const text = [
|
||||||
|
exchange.id,
|
||||||
|
symbol,
|
||||||
|
JSON.stringify (ticker, undefined, '\n\t')
|
||||||
|
]
|
||||||
|
|
||||||
|
document.getElementById (elementId).innerHTML = text.join (' ')
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
exchange.fetchTicker (symbol).then (ticker => showFetchedTicker (ticker, 'content'))
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Hello, CCXT!</h1>
|
||||||
|
<pre id="content"></pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CCXT Basic example for the browser</title>
|
||||||
|
<script type="text/javascript" src="https://unpkg.com/ccxt"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
document.addEventListener ("DOMContentLoaded", function () {
|
||||||
|
|
||||||
|
alert ('ccxt version ' + ccxt.version + ' supporting ');
|
||||||
|
|
||||||
|
const pollTickerContinuously = async function (exchange, symbol) {
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const ticker = await exchange.fetchTicker (symbol)
|
||||||
|
|
||||||
|
const text = [
|
||||||
|
exchange.id,
|
||||||
|
symbol,
|
||||||
|
JSON.stringify (ticker, undefined, '\n\t')
|
||||||
|
]
|
||||||
|
|
||||||
|
document.getElementById ('content').innerHTML = text.join (' ');
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
|
||||||
|
const text = [
|
||||||
|
e.constructor.name,
|
||||||
|
e.message,
|
||||||
|
]
|
||||||
|
|
||||||
|
document.getElementById ('content').innerHTML = text.join (' ');
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const exchange = new ccxt.binance ({ enableRateLimit: true })
|
||||||
|
const symbol = 'ETH/BTC'
|
||||||
|
|
||||||
|
pollTickerContinuously (exchange, symbol)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Hello, CCXT!</h1>
|
||||||
|
<pre id="content"></pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CCXT Basic example for the browser</title>
|
||||||
|
<script type="text/javascript" src="https://unpkg.com/ccxt"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
document.addEventListener ("DOMContentLoaded", async function () {
|
||||||
|
|
||||||
|
alert ('ccxt version ' + ccxt.version);
|
||||||
|
|
||||||
|
const enableRateLimit = true
|
||||||
|
const exchange = new ccxt.poloniex ({ enableRateLimit })
|
||||||
|
const symbol = 'ETH/BTC'
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
let text = []
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const ticker = await exchange.fetchTicker (symbol)
|
||||||
|
|
||||||
|
text = [
|
||||||
|
exchange.id,
|
||||||
|
symbol,
|
||||||
|
JSON.stringify (exchange.omit (ticker, 'info'), undefined, '\t')
|
||||||
|
]
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
|
||||||
|
text = [
|
||||||
|
e.constructor.name,
|
||||||
|
e.message,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById ('content').innerHTML = text.join (' ')
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Hello, CCXT!</h1>
|
||||||
|
<pre id="content"></pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CCXT Basic example for the browser</title>
|
||||||
|
<script type="text/javascript" src="https://unpkg.com/ccxt"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
document.addEventListener ("DOMContentLoaded", function () {
|
||||||
|
|
||||||
|
alert ('CCXT' + (ccxt.isBrowser ? ' browser' : '') + ' version ' + ccxt.version);
|
||||||
|
|
||||||
|
const exchange = new ccxt.coinbasepro ({ enableRateLimit: true })
|
||||||
|
const symbol = 'ETH/BTC'
|
||||||
|
|
||||||
|
exchange.fetchTicker (symbol).then (ticker => {
|
||||||
|
|
||||||
|
const text = [
|
||||||
|
exchange.id,
|
||||||
|
symbol,
|
||||||
|
JSON.stringify (ticker, undefined, '\n\t')
|
||||||
|
]
|
||||||
|
|
||||||
|
document.getElementById ('content').innerHTML = text.join (' ')
|
||||||
|
|
||||||
|
}).catch (e => {
|
||||||
|
|
||||||
|
const text = [
|
||||||
|
e.constructor.name,
|
||||||
|
e.message,
|
||||||
|
]
|
||||||
|
|
||||||
|
document.getElementById ('content').innerHTML = text.join (' ')
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Hello, CCXT!</h1>
|
||||||
|
<pre id="content"></pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CCXT Basic example for the browser</title>
|
||||||
|
<script type="text/javascript" src="https://unpkg.com/ccxt"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
document.addEventListener ("DOMContentLoaded", function () {
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
const exchange = new ccxt.binance ({
|
||||||
|
'apiKey': 'YOUR_API_KEY',
|
||||||
|
'secret': 'YOUR_API_SECRET',
|
||||||
|
'proxyUrl': 'https://cors-anywhere.herokuapp.com/',
|
||||||
|
})
|
||||||
|
const response = await exchange.fetchBalance ()
|
||||||
|
console.log (response)
|
||||||
|
}
|
||||||
|
|
||||||
|
main ()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Hello, CCXT!</h1>
|
||||||
|
<pre id="content"></pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// JavaScript CORS Proxy
|
||||||
|
// Save this in a file like cors.js and run with `node cors [port]`
|
||||||
|
// It will listen for your requests on the port you pass in command line or port 8080 by default
|
||||||
|
const port = (process.argv.length > 2) ? parseInt (process.argv[2]) : 8080 // default
|
||||||
|
require ('cors-anywhere').createServer ({
|
||||||
|
setHeaders: { 'origin': 'https://www.bitmex.com' }
|
||||||
|
}).listen (port, '0.0.0.0')
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CCXT Basic example for the browser</title>
|
||||||
|
<script type="text/javascript" src="https://unpkg.com/ccxt"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
document.addEventListener ("DOMContentLoaded", function () {
|
||||||
|
|
||||||
|
alert ('ccxt version ' + ccxt.version);
|
||||||
|
|
||||||
|
const exchange = new ccxt.bitmex ({
|
||||||
|
'proxyUrl': 'http://127.0.0.1:8080/',
|
||||||
|
})
|
||||||
|
|
||||||
|
const symbol = 'BTC/USDT'
|
||||||
|
|
||||||
|
exchange.fetchTicker (symbol).then (ticker => {
|
||||||
|
|
||||||
|
const text = [
|
||||||
|
exchange.id,
|
||||||
|
symbol,
|
||||||
|
JSON.stringify (ticker, undefined, '\n\t')
|
||||||
|
]
|
||||||
|
|
||||||
|
document.getElementById ('content').innerHTML = text.join (' ')
|
||||||
|
|
||||||
|
}).catch (e => {
|
||||||
|
|
||||||
|
const text = [
|
||||||
|
e.constructor.name,
|
||||||
|
e.message,
|
||||||
|
]
|
||||||
|
|
||||||
|
document.getElementById ('content').innerHTML = text.join (' ')
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Hello, CCXT!</h1>
|
||||||
|
<pre id="content"></pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CCXT Basic example for the browser</title>
|
||||||
|
<script type="text/javascript" src="https://unpkg.com/ccxt"></script>
|
||||||
|
<script type="text/javascript" src="https://unpkg.com/lightweight-charts@3.4.0/dist/lightweight-charts.standalone.production.js"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
document.addEventListener ("DOMContentLoaded", async function () {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const exchange = new ccxt.coinbasepro ({ enableRateLimit: true });
|
||||||
|
const symbol = 'ETH/BTC';
|
||||||
|
const timeframe = '1d';
|
||||||
|
const since = undefined;
|
||||||
|
const limit = 600;
|
||||||
|
const config = {
|
||||||
|
width: 600,
|
||||||
|
height: 300,
|
||||||
|
priceScale: {
|
||||||
|
position: 'left',
|
||||||
|
mode: 1,
|
||||||
|
autoScale: true,
|
||||||
|
invertScale: false,
|
||||||
|
},
|
||||||
|
timeScale: {
|
||||||
|
fixLeftEdge: true,
|
||||||
|
lockVisibleTimeRangeOnResize: true,
|
||||||
|
rightBarStaysOnScroll: true,
|
||||||
|
visible: true,
|
||||||
|
timeVisible: true,
|
||||||
|
secondsVisible: true,
|
||||||
|
},
|
||||||
|
crosshair: {
|
||||||
|
mode: window.LightweightCharts.CrosshairMode.Normal,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const chart = window.LightweightCharts.createChart(document.body, config);
|
||||||
|
const candleSeries = chart.addCandlestickSeries();
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const response = await exchange.fetchOHLCV(symbol, timeframe, since, limit);
|
||||||
|
const last = response[response.length - 1]
|
||||||
|
const [ timestamp, open, high, low, close ] = last;
|
||||||
|
const data = response.map (([ timestamp, open, high, low, close ]) => ({ time: exchange.iso8601 (timestamp), open, high, low, close }));
|
||||||
|
candleSeries.setData(data);
|
||||||
|
} catch (e) {
|
||||||
|
console.log (e.constructor.name, e.message);
|
||||||
|
}
|
||||||
|
await exchange.sleep (1000);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert (e.constructor.name + ' ' + e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h2>CCXT running on a Webworker example</h2>
|
||||||
|
<p>This example uses a web worker to continuously call <b>fetchTicker</b> on a user-defined exchange, symbol and interval. Then, the web worker reaches back with the results.</p>
|
||||||
|
<h3>Parameters</h3>
|
||||||
|
<label for="exchange">Exchange:</label>
|
||||||
|
<input type="text" id="exchange" name="exchange" ><br><br>
|
||||||
|
<label for="symbol">Symbol: </label>
|
||||||
|
<input type="text" id="symbol" name="symbol"><br><br>
|
||||||
|
<label for="interval">Interval (ms): </label>
|
||||||
|
<input type="text" id="interval" name="interval"><br><br>
|
||||||
|
<button onclick="startWorker()">Start CCXT worker</button>
|
||||||
|
<button onclick="stopWorker()">Stop CCXT worker</button>
|
||||||
|
<h3> Received Results:</h3>
|
||||||
|
<output id="result"></output>
|
||||||
|
<table id="resultsTable" style="left:300px;width:400px; border:1px solid black;" >
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Symbol</th>
|
||||||
|
<th>Last Price</th>
|
||||||
|
<th>Base Volume</th>
|
||||||
|
<th>Exchange Ts</th>
|
||||||
|
<th>Local Ts</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="tbody">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
// fill in default values
|
||||||
|
document.addEventListener('DOMContentLoaded', function(event) {
|
||||||
|
document.getElementById('exchange').value = 'coinbasepro';
|
||||||
|
document.getElementById('symbol').value = 'BTC/USDT';
|
||||||
|
document.getElementById('interval').value = '1000';
|
||||||
|
});
|
||||||
|
|
||||||
|
var ccxtWorker;
|
||||||
|
function startWorker() {
|
||||||
|
|
||||||
|
if (typeof(Worker) !== "undefined") {
|
||||||
|
// if the worker does not exist yet, creates it
|
||||||
|
if (typeof(ccxtWorker) == "undefined") {
|
||||||
|
ccxtWorker = new Worker("worker.js");
|
||||||
|
}
|
||||||
|
|
||||||
|
// reads user-defined parameters
|
||||||
|
var exchange = document.getElementById('exchange').value
|
||||||
|
var symbol = document.getElementById('symbol').value
|
||||||
|
var interval = document.getElementById('interval').value
|
||||||
|
|
||||||
|
// send a message with those values
|
||||||
|
ccxtWorker.postMessage([exchange,symbol, interval]);
|
||||||
|
|
||||||
|
ccxtWorker.onmessage = function(event) {
|
||||||
|
// every time the ccxtworker posts a message
|
||||||
|
// this handler will be invoked
|
||||||
|
handleReceivedData(event.data)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
document.getElementById("result").innerHTML = "Sorry! No Web Worker support.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleReceivedData(data) {
|
||||||
|
var [symbol, last, baseVolume, timestamp, ourTimestamp] = data;
|
||||||
|
var tableRef = document.getElementById('resultsTable');
|
||||||
|
var tbody = document.getElementById('tbody');
|
||||||
|
row = tbody.insertRow(0);
|
||||||
|
|
||||||
|
cellSymbol = row.insertCell();
|
||||||
|
cellSymbol.innerHTML = symbol;
|
||||||
|
|
||||||
|
cellPrice = row.insertCell();
|
||||||
|
cellPrice.innerHTML = last;
|
||||||
|
|
||||||
|
cellPrice = row.insertCell();
|
||||||
|
cellPrice.innerHTML = baseVolume;
|
||||||
|
|
||||||
|
cellTimestamp = row.insertCell();
|
||||||
|
cellTimestamp.innerHTML = timestamp;
|
||||||
|
|
||||||
|
cellOurtimestamp = row.insertCell();
|
||||||
|
cellOurtimestamp.innerHTML = ourTimestamp;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopWorker() {
|
||||||
|
if (ccxtWorker !== undefined) {
|
||||||
|
ccxtWorker.terminate();
|
||||||
|
ccxtWorker = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
self.importScripts('https://unpkg.com/ccxt@1.79.2/dist/ccxt.browser.js');
|
||||||
|
|
||||||
|
console.log("Loaded ccxt version:", self.ccxt.version);
|
||||||
|
|
||||||
|
var exchangeInstance = undefined;
|
||||||
|
|
||||||
|
// handler of received messages
|
||||||
|
self.onmessage = async function handler(msg) {
|
||||||
|
await handleMessageFromMain(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get messages from the main script
|
||||||
|
async function handleMessageFromMain(msg) {
|
||||||
|
console.log(msg.data);
|
||||||
|
var [exchange, symbol, interval] = msg.data;
|
||||||
|
console.log('Worker received:', symbol,exchange, interval)
|
||||||
|
interval = parseInt(interval)
|
||||||
|
await processTicker(symbol, exchange)
|
||||||
|
// schedule process ticker execution
|
||||||
|
setInterval (async () => {
|
||||||
|
await processTicker(symbol, exchange)
|
||||||
|
}, interval)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function processTicker(symbol, exchangeId) {
|
||||||
|
if (exchangeInstance === undefined) {
|
||||||
|
exchangeInstance = new ccxt[exchangeId]
|
||||||
|
}
|
||||||
|
var result = await fetchTicker(symbol)
|
||||||
|
var symbol = result['symbol']
|
||||||
|
var last = result['last']
|
||||||
|
var timestamp = result['timestamp']
|
||||||
|
var baseVolume = result['baseVolume']
|
||||||
|
var ourTimestamp = Date.now()
|
||||||
|
// send the data back to the main script
|
||||||
|
postMessage([symbol, last, baseVolume, timestamp, ourTimestamp]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchTicker(symbol){
|
||||||
|
// use ccxt to fetch ticker info
|
||||||
|
var result = await exchangeInstance.fetchTicker(symbol)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user