mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-09 08:18:07 +00:00
Compare commits
46
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
732525b394 | ||
|
|
d829e451cd | ||
|
|
0d2aa84b84 | ||
|
|
d2867ba98c | ||
|
|
e28240e799 | ||
|
|
cfd5f4309d | ||
|
|
627ed36de4 | ||
|
|
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 |
+70
-7
@@ -1,5 +1,5 @@
|
||||
# Exchange selection
|
||||
EXCHANGE=aster # Pick aster (default) or grvt
|
||||
EXCHANGE=aster # Pick aster (default) or grvt/lighter/backpack/paradex
|
||||
|
||||
# Aster API credentials
|
||||
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)
|
||||
|
||||
# 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_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_OFFSET_USD=0.05 # Base stop offset from entry after trigger (USDT)
|
||||
PROFIT_LOCK_TRIGGER_USD=0.08 # Start moving base stop once unrealized PnL > this (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)
|
||||
PRICE_TICK=0.1 # Price tick size (e.g. BTCUSDT uses 0.1)
|
||||
@@ -27,11 +30,10 @@ 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%)
|
||||
|
||||
# Maker-only settings
|
||||
MAKER_LOSS_LIMIT=0.03 # Maker loss cap (USDT). Defaults to LOSS_LIMIT if unset
|
||||
MAKER_PRICE_CHASE=0.3 # Price chase threshold (USDT)
|
||||
MAKER_LOSS_LIMIT=0.05 # Maker loss cap (USDT). Defaults to LOSS_LIMIT if unset
|
||||
MAKER_BID_OFFSET=0 # Bid quote offset from top bid (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_PRICE_TICK=0.1 # Maker price tick size (defaults to PRICE_TICK)
|
||||
|
||||
@@ -47,3 +49,64 @@ GRVT_ENV=prod
|
||||
# GRVT_COOKIE="gravity=..." # Pre-provisioned session cookie (auto-refresh uses API key when absent)
|
||||
# GRVT_ACCOUNT_ID= # Populated automatically after login
|
||||
# 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
|
||||
|
||||
# EdgeX exchange configuration
|
||||
EDGEX_ACCOUNT_ID=
|
||||
EDGEX_PRIVATE_KEY=
|
||||
# EDGEX_POSITION_ID= # Defaults to EDGEX_ACCOUNT_ID when omitted
|
||||
# EDGEX_BASE_URL=https://pro.edgex.exchange
|
||||
# EDGEX_WS_PUBLIC_URL=wss://quote.edgex.exchange
|
||||
# EDGEX_WS_PRIVATE_URL=wss://quote.edgex.exchange
|
||||
# EDGEX_ORDER_TTL_MS=21600000 # Order expiration window (ms), default 6 hours
|
||||
|
||||
|
||||
# Paradex exchange configuration
|
||||
# Provide the EVM private key & wallet address for onboarded accounts.
|
||||
# When EXCHANGE=paradex these values are used automatically.
|
||||
|
||||
PARADEX_PRIVATE_KEY=
|
||||
PARADEX_WALLET_ADDRESS=
|
||||
|
||||
# Symbol defaults to TRADE_SYMBOL if omitted. Use ccxt unified format like BTC-USD-PERP.
|
||||
# PARADEX_SYMBOL=BTC-USD-PERP
|
||||
|
||||
# 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
|
||||
|
||||
@@ -1,19 +1,35 @@
|
||||
# Repository Guidelines
|
||||
|
||||
## 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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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,158 @@
|
||||
# ritmex-bot
|
||||
|
||||
一个基于 Bun 的 Aster 永续合约终端机器人,内置趋势跟随(SMA30)与做市策略,使用 websocket 实时行情,命令行界面由 Ink 驱动,可在断线后自动恢复运行。
|
||||
基于 Bun 的 Aster 永续合约量化终端,内置趋势跟随(SMA30)与做市策略,支持快速恢复、实时行情订阅与日志追踪。
|
||||
|
||||
## 快速上手
|
||||
* [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/41d60948-2a75-4d16-b7e9-523df74f2904)
|
||||
* [edgex 手续费优惠注册链接](https://pro.edgex.exchange/referral/BULL)
|
||||
|
||||
使用优惠码获取 30% 手续费折扣:[注册 Aster 获取手续费优惠](https://www.asterdex.com/zh-CN/referral/4665f3)
|
||||
## 文档索引
|
||||
- [English README](README_en.md)
|
||||
- [简明上手指南(零基础)](simple-readme.md)
|
||||
|
||||
如果你完全不懂代码,可以查看 **[小白教程](simple-readme.md) 了解使用方法。**
|
||||
## 项目亮点
|
||||
- **实时行情与风控**:Websocket + REST 自动同步账户、挂单与仓位。
|
||||
- **趋势策略**:SMA30 穿越入场,内置止损、移动止盈、布林带带宽过滤与步进锁盈。
|
||||
- **做市策略**:支持双边追价、风险阈值与订单自愈。
|
||||
- **模块化设计**:适配器、策略引擎与 CLI 解耦,方便扩展新交易所或策略。
|
||||
|
||||
遇到Bug,反馈问题,请到 [Telegram群组](https://t.me/+4fdo0quY87o4Mjhh)
|
||||
## 环境要求
|
||||
- Bun ≥ 1.2(含 `bun`、`bunx` 命令)
|
||||
- macOS、Linux 或 Windows (WSL 推荐)
|
||||
- Node.js 仅在某些安装路径需要,可选
|
||||
|
||||
### 一键脚本
|
||||
|
||||
一键安装并启动(macOS / Linux):
|
||||
## 快速启动脚本(macOS / Linux / WSL)
|
||||
```bash
|
||||
curl -fsSL https://github.com/discountry/ritmex-bot/raw/refs/heads/main/setup.sh | bash
|
||||
```
|
||||
脚本会自动安装 Bun、安装依赖、引导输入 API Key/Secret,生成 `.env` 并启动程序。
|
||||
脚本会安装 Bun、依赖,收集 Aster API Key/Secret,生成 `.env` 并启动 CLI。运行前请准备好 API 凭证。
|
||||
|
||||
Windows 使用 WSL(推荐):
|
||||
1. 先安装并启用 WSL,参考微软官方文档:[在 Windows 上安装 WSL](https://learn.microsoft.com/zh-cn/windows/wsl/install)
|
||||
2. 在命令行或者 PowerShell 输入 `wsl` 并按下回车打开 WSL。
|
||||
3. 在 WSL 中运行一键脚本:
|
||||
## 手动安装步骤
|
||||
1. **获取代码**
|
||||
```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、依赖并启动程序。
|
||||
|
||||
## 手动安装
|
||||
|
||||
1. **下载代码**
|
||||
- 如果会使用 Git:`git clone https://github.com/discountry/ritmex-bot.git`
|
||||
- 如果不会使用 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. **安装依赖**
|
||||
不方便使用 Git 时,可在仓库页面下载 ZIP 并手动解压。
|
||||
2. **安装 Bun**
|
||||
- macOS / Linux:`curl -fsSL https://bun.sh/install | bash`
|
||||
- Windows PowerShell:`powershell -c "irm bun.sh/install.ps1 | iex"`
|
||||
安装后重新打开终端,确认 `bun -v` 正常输出版本号。
|
||||
3. **安装依赖**
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
5. **配置环境变量**
|
||||
复制 `.env.example` 为 `.env` 并填入你的 Aster API Key/Secret:
|
||||
4. **复制环境变量模板并填写**
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
然后根据需要修改 `.env` 中的配置项:
|
||||
- API KEY 获取地址 [https://www.asterdex.com/zh-CN/api-management](https://www.asterdex.com/zh-CN/api-management)
|
||||
- `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. **运行机器人**
|
||||
按下文说明修改 `.env`,至少需要正确配置 Aster 或 GRVT 的 API。
|
||||
5. **运行 CLI**
|
||||
```bash
|
||||
bun run index.ts
|
||||
```
|
||||
在终端中按 ↑/↓ 选择 “趋势策略” 或 “做市策略”,回车启动。按 `Esc` 可返回选择菜单,`Ctrl+C` 退出。
|
||||
7. **风险提示**
|
||||
建议先在小额或仿真环境中测试策略;真实资金操作前请确认 API 仅开启必要权限,并逐步验证配置。
|
||||
方向键选择策略,回车启动;`Esc` 返回菜单,`Ctrl+C` 退出。
|
||||
|
||||
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.
|
||||
- **Maker strategy**: adaptive bid/ask chasing, risk stops, and target order introspection.
|
||||
- **Extensibility**: exchange gateway, engines, and UI components are modular for new venues or strategies.
|
||||
| 变量 | 说明 |
|
||||
| --- | --- |
|
||||
| `ASTER_API_KEY` / `ASTER_API_SECRET` | Aster API 凭证,运行策略必填 |
|
||||
| `TRADE_SYMBOL` | 交易对(默认 `BTCUSDT`) |
|
||||
| `TRADE_AMOUNT` | 单笔下单数量(标的资产计) |
|
||||
| `LOSS_LIMIT` | 单笔最大亏损触发的强平额度(USDT) |
|
||||
| `TRAILING_PROFIT` / `TRAILING_CALLBACK_RATE` | 动态止盈触发值(USDT)与回撤百分比 |
|
||||
| `PROFIT_LOCK_TRIGGER_USD` / `PROFIT_LOCK_OFFSET_USD` | 浮盈超过阈值后上调止损的触发金额与偏移 |
|
||||
| `BOLLINGER_LENGTH` / `BOLLINGER_STD_MULTIPLIER` | 布林带宽度判定的窗口长度与标准差倍数 |
|
||||
| `MIN_BOLLINGER_BANDWIDTH` | 仅当带宽 ≥ 此比例时才触发入场信号 |
|
||||
| `PRICE_TICK` / `QTY_STEP` | 交易所要求的最小报价与数量精度 |
|
||||
| `POLL_INTERVAL_MS` | 趋势策略循环间隔(毫秒) |
|
||||
| `MAX_CLOSE_SLIPPAGE_PCT` | 平仓时相对标记价允许的最大偏差 |
|
||||
| `MAKER_*` 系列 | 做市策略独有参数(追价阈值、报价偏移、刷新频率等) |
|
||||
|
||||
## Requirements
|
||||
- [Bun](https://bun.com) ≥ 1.2
|
||||
- Node.js (optional, only if you prefer `npm` tooling)
|
||||
- Valid Aster API credentials with futures access
|
||||
切换到 GRVT 时,将 `EXCHANGE=grvt` 并补齐 `GRVT_API_KEY`、`GRVT_API_SECRET`、`GRVT_SUB_ACCOUNT_ID` 等变量;详情见 `.env.example`。
|
||||
|
||||
## Installation
|
||||
> 提示:你也可以通过命令行参数临时指定交易所(优先级高于环境变量):
|
||||
> ```bash
|
||||
> bun run index.ts --exchange grvt
|
||||
> bun run index.ts -e lighter
|
||||
> ```
|
||||
|
||||
## 常用命令
|
||||
```bash
|
||||
bun install
|
||||
bun run index.ts # 启动 CLI(默认)
|
||||
bun run start # 同上
|
||||
bun run dev # 调试模式,等价于运行 index.ts
|
||||
bun x vitest run # 执行单元测试
|
||||
```
|
||||
|
||||
## Configuration
|
||||
Create an `.env` (or export environment variables) with at least:
|
||||
```bash
|
||||
ASTER_API_KEY=your_key
|
||||
ASTER_API_SECRET=your_secret
|
||||
TRADE_SYMBOL=BTCUSDT # optional, defaults to BTCUSDT
|
||||
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:
|
||||
```bash
|
||||
# Maker-specific (units in USDT unless noted)
|
||||
MAKER_LOSS_LIMIT=0.03 # override maker risk stop; defaults to LOSS_LIMIT
|
||||
MAKER_PRICE_CHASE=0.3 # chase threshold
|
||||
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:
|
||||
## 静默启动与后台运行
|
||||
### 直接静默启动
|
||||
无需进入 Ink 菜单,可用命令行直接拉起指定策略:
|
||||
|
||||
```bash
|
||||
EXCHANGE=grvt
|
||||
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
|
||||
bun run index.ts --strategy trend --silent # 启动趋势策略
|
||||
bun run index.ts --strategy maker --silent # 启动做市策略
|
||||
bun run index.ts --strategy offset-maker --silent # 启动偏移做市策略
|
||||
```
|
||||
|
||||
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 适配器:
|
||||
如需同时指定交易所,可叠加 `--exchange/-e`(将覆盖 `.env` 中的 `EXCHANGE`/`TRADE_EXCHANGE`):
|
||||
|
||||
```bash
|
||||
EXCHANGE=grvt
|
||||
GRVT_API_KEY=你的APIKey
|
||||
GRVT_API_SECRET=0xabc123... # 用于订单签名的私钥
|
||||
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=... # 登录后自动填充
|
||||
bun run index.ts --exchange grvt --strategy maker --silent
|
||||
bun run index.ts -e lighter -s offset-maker --silent
|
||||
```
|
||||
|
||||
适配器会基于 `GRVT_API_SECRET` 自动完成 EIP‑712 签名并提交订单。如果你需要自定义签名流程,可通过 `GRVT_SIGNER_PATH` 指定模块(CommonJS 或 ESM 均可)。模块需导出一个函数,接收签名上下文(未签名订单、nonce/expiration、合约元信息、链 ID 等)并返回包含 `signer/r/s/v/expiration/nonce` 字段的对象,例如:
|
||||
### 项目内置脚本
|
||||
`package.json` 提供了便捷脚本:
|
||||
|
||||
```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
|
||||
bun run index.ts # or: bun run dev / bun run start
|
||||
bun run start:trend:silent
|
||||
bun run start:maker:silent
|
||||
bun run start:offset:silent
|
||||
```
|
||||
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
|
||||
### 使用 pm2 守护并自动重启
|
||||
将 `pm2` 安装到项目中(示例:`bun add -d pm2`),之后即可在不安装全局 pm2 的情况下运行:
|
||||
|
||||
```bash
|
||||
bun run test # bun x vitest run
|
||||
bun run test:watch # stay in watch mode
|
||||
bunx pm2 start bun --name ritmex-trend --cwd . --restart-delay 5000 -- run index.ts --strategy trend --silent
|
||||
```
|
||||
Current tests cover the order coordinator utilities and strategy helpers; add unit tests beside new modules as you extend the system.
|
||||
|
||||
## Project Layout
|
||||
- `src/config.ts` – shared runtime configuration
|
||||
- `src/core/` – trend & maker engines plus order coordination
|
||||
- `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.
|
||||
- **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.
|
||||
```bash
|
||||
bun run pm2:start:trend
|
||||
bun run pm2:start:maker
|
||||
bun run pm2:start:offset
|
||||
```
|
||||
|
||||
## Contributing
|
||||
Issues and PRs are welcome. When adding strategies or exchanges, follow the modular patterns in `src/core` and add tests under `tests/`.
|
||||
根据需要调整 `--name`、`--cwd`、`--restart-delay` 等参数,完成后可执行 `pm2 save` 持久化进程列表。
|
||||
|
||||
## 测试
|
||||
项目使用 Vitest:
|
||||
```bash
|
||||
bun run test # 运行全部测试
|
||||
bun x vitest --watch
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
- 你需要至少 50-100 USDT 的资金才能运行策略
|
||||
- 请在交易所自行设置 50 倍左右的杠杆,本策略不包含杠杆设置
|
||||
- 请确保你电脑/服务器的时间是准确的真实世界时间
|
||||
- 持仓方式需要保持单向持仓
|
||||
- `.env` 未读取:确认文件位于项目根目录且变量名无误。
|
||||
- API 拒绝访问:检查交易所后台权限,确保开启合约读写。
|
||||
- 精度错误:同步交易对的最小价格与数量步长。
|
||||
更多排查步骤可参考 [简明上手指南](simple-readme.md)。
|
||||
|
||||
## 社区与支持
|
||||
- Telegram 交流群:[https://t.me/+4fdo0quY87o4Mjhh](https://t.me/+4fdo0quY87o4Mjhh)
|
||||
- 反馈或新特性建议请提交 Issue 或 PR
|
||||
|
||||
## 风险提示
|
||||
量化交易具备风险。建议在仿真或小额账户中验证策略表现,妥善保管 API 密钥,仅开启必要权限。
|
||||
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
# ritmex-bot
|
||||
|
||||
A Bun-powered trading workstation for Aster perpetual contracts that ships two production-ready agents: an SMA30 trend follower and a dual-sided market maker. The CLI is built with Ink, synchronises risk state from the exchange, and automatically recovers from restarts or disconnects.
|
||||
|
||||
## Documentation Map
|
||||
- [中文 README](README.md)
|
||||
- [Beginner-friendly Quick Start](simple-readme.md)
|
||||
|
||||
## Highlights
|
||||
- **Live market data & risk sync** via websocket feeds with REST fallbacks, full reconciliation on restart.
|
||||
- **Trend engine** featuring SMA30 entries, fixed stop loss, trailing stop, Bollinger bandwidth gate, and profit-lock stepping.
|
||||
- **Market-making loop** with adaptive quote chasing, loss caps, and automatic order healing.
|
||||
- **Extensible architecture** decoupling exchange adapters, engines, and the Ink CLI for easy venue or strategy additions.
|
||||
|
||||
## Requirements
|
||||
- Bun ≥ 1.2 (`bun`, `bunx` available on PATH)
|
||||
- macOS, Linux, or Windows via WSL (native Windows works but WSL is recommended)
|
||||
- Node.js is optional unless your environment requires it for tooling
|
||||
|
||||
## 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 your API Key/Secret before running.
|
||||
|
||||
## 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 confirm `bun -v` prints a version.
|
||||
3. **Install dependencies**
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
4. **Create your environment file**
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
Edit `.env` with your exchange credentials and overrides.
|
||||
5. **Launch the CLI**
|
||||
```bash
|
||||
bun run index.ts
|
||||
```
|
||||
Use the arrow keys to pick a strategy, `Enter` to start, `Esc` to return to the menu, and `Ctrl+C` to exit.
|
||||
|
||||
## Environment Variables
|
||||
The most important settings shipped in `.env.example` are summarised below:
|
||||
|
||||
| Variable | Purpose |
|
||||
| --- | --- |
|
||||
| `ASTER_API_KEY` / `ASTER_API_SECRET` | Required Aster exchange credentials |
|
||||
| `TRADE_SYMBOL` | Contract symbol, defaults to `BTCUSDT` |
|
||||
| `TRADE_AMOUNT` | Order size in base asset units |
|
||||
| `LOSS_LIMIT` | Max per-trade loss (USDT) before forced close |
|
||||
| `TRAILING_PROFIT` / `TRAILING_CALLBACK_RATE` | Trailing stop trigger amount (USDT) and pullback percentage |
|
||||
| `PROFIT_LOCK_TRIGGER_USD` / `PROFIT_LOCK_OFFSET_USD` | Move the base stop once unrealised PnL exceeds this trigger |
|
||||
| `BOLLINGER_LENGTH` / `BOLLINGER_STD_MULTIPLIER` | Window size and std-dev multiplier for bandwidth filtering |
|
||||
| `MIN_BOLLINGER_BANDWIDTH` | Minimum bandwidth ratio required before opening a new position |
|
||||
| `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 strategy knobs: chase threshold, quote offsets, refresh cadence, etc. |
|
||||
|
||||
To trade on GRVT, set `EXCHANGE=grvt` and populate `GRVT_API_KEY`, `GRVT_API_SECRET`, `GRVT_SUB_ACCOUNT_ID`, plus any optional overrides documented in `.env.example`.
|
||||
|
||||
> Tip: you can temporarily override the exchange via CLI flags (takes precedence over environment):
|
||||
> ```bash
|
||||
> bun run index.ts --exchange grvt
|
||||
> bun run index.ts -e lighter
|
||||
> ```
|
||||
|
||||
## Common Commands
|
||||
```bash
|
||||
bun run index.ts # Launch the CLI
|
||||
bun run start # Same as above
|
||||
bun run dev # Development entry point
|
||||
bun x vitest run # Execute the Vitest suite
|
||||
```
|
||||
|
||||
## Silent & Background Execution
|
||||
### Direct silent launch
|
||||
Skip the Ink menu and start a strategy straight from the CLI:
|
||||
|
||||
```bash
|
||||
bun run index.ts --strategy trend --silent # Trend engine
|
||||
bun run index.ts --strategy maker --silent # Maker engine
|
||||
bun run index.ts --strategy offset-maker --silent # Offset maker engine
|
||||
```
|
||||
|
||||
Combine with `--exchange/-e` to explicitly choose the venue (overrides `EXCHANGE`/`TRADE_EXCHANGE` from `.env`):
|
||||
|
||||
```bash
|
||||
bun run index.ts --exchange grvt --strategy maker --silent
|
||||
bun run index.ts -e lighter -s offset-maker --silent
|
||||
```
|
||||
|
||||
### Package scripts
|
||||
Convenience aliases are exposed in `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 without a global install:
|
||||
|
||||
```bash
|
||||
bunx pm2 start bun --name ritmex-trend --cwd . --restart-delay 5000 -- run index.ts --strategy trend --silent
|
||||
```
|
||||
|
||||
You can also reuse the bundled scripts:
|
||||
|
||||
```bash
|
||||
bun run pm2:start:trend
|
||||
bun run pm2:start:maker
|
||||
bun run pm2:start:offset
|
||||
```
|
||||
|
||||
Adjust `--name`, `--cwd`, or `--restart-delay` to suit your environment and run `pm2 save` if you want the process to auto-start after reboot.
|
||||
|
||||
## Testing
|
||||
Vitest powers the unit tests:
|
||||
```bash
|
||||
bun run test
|
||||
bun x vitest --watch
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
- You need at least 50–100 USDT of capital before deploying a live strategy.
|
||||
- Set leverage on the exchange beforehand (around 50x is recommended); the bot does not change it for you.
|
||||
- Keep server/desktop time in sync with real-world time to avoid signature errors.
|
||||
- Make sure the exchange account is in one-way position mode.
|
||||
- **Env not loading**: ensure `.env` resides in the repository root and variable names are spelled correctly.
|
||||
- **Order rejected for precision**: align `PRICE_TICK`, `QTY_STEP`, and `TRADE_SYMBOL` with the exchange filters.
|
||||
- **Permission or auth errors**: double-check exchange API scopes.
|
||||
More step-by-step guidance is available in [simple-readme.md](simple-readme.md).
|
||||
|
||||
## Community & Support
|
||||
- Telegram: [https://t.me/+4fdo0quY87o4Mjhh](https://t.me/+4fdo0quY87o4Mjhh)
|
||||
- Issues and PRs are welcome for bug reports and feature ideas
|
||||
|
||||
## Disclaimer
|
||||
Algorithmic trading carries risk. Validate strategies with paper accounts or small capital first, safeguard your API keys, and only grant the minimum required permissions.
|
||||
@@ -5,9 +5,11 @@
|
||||
"name": "ritmex-bot",
|
||||
"dependencies": {
|
||||
"@grvt/client": "^1.6.4",
|
||||
"@starkware-industries/starkware-crypto-utils": "^0.2.1",
|
||||
"axios": "^1.12.2",
|
||||
"ccxt": "^4.5.5",
|
||||
"dotenv": "^17.2.2",
|
||||
"ethereum-cryptography": "^2.1.3",
|
||||
"ink": "^6.3.1",
|
||||
"react": "^19.1.1",
|
||||
"ws": "^8.18.3",
|
||||
@@ -80,6 +82,10 @@
|
||||
|
||||
"@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/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-arm64": ["@rollup/rollup-android-arm64@4.52.0", "", { "os": "android", "cpu": "arm64" }, "sha512-pqDirm8koABIKvzL59YI9W9DWbRlTX7RWhN+auR8HXJxo89m4mjqbah7nJZjeKNTNYopqL+yGg+0mhCpf3xZtQ=="],
|
||||
@@ -124,6 +130,16 @@
|
||||
|
||||
"@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.1.9", "", {}, "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg=="],
|
||||
|
||||
"@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=="],
|
||||
|
||||
"@starkware-industries/starkware-crypto-utils": ["@starkware-industries/starkware-crypto-utils@0.2.1", "", { "dependencies": { "assert": "^2.0.0", "bip39": "^3.0.4", "bn.js": "^4.12.0", "brorand": "^1.1.0", "buffer": "^6.0.3", "crypto-browserify": "^3.12.0", "elliptic": "^6.5.4", "enc-utils": "^3.0.0", "ethereumjs-wallet": "^1.0.2", "hash.js": "^1.1.7", "hmac-drbg": "^1.0.1", "inherits": "^2.0.4", "js-sha3": "^0.8.0", "minimalistic-assert": "^1.0.1", "minimalistic-crypto-utils": "^1.0.1", "stream-browserify": "^3.0.0" } }, "sha512-rA5O9b53zaoBOQwQxBd0cbumFbQoBm9NH/vfu+o0Cq3oouEbNPALneLlLjOmFEId2/WOJ5ecC64rFLI/PwuIPQ=="],
|
||||
|
||||
"@types/bn.js": ["@types/bn.js@5.2.0", "", { "dependencies": { "@types/node": "*" } }, "sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q=="],
|
||||
|
||||
"@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=="],
|
||||
@@ -138,8 +154,12 @@
|
||||
|
||||
"@types/node": ["@types/node@24.5.2", "", { "dependencies": { "undici-types": "~7.12.0" } }, "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ=="],
|
||||
|
||||
"@types/pbkdf2": ["@types/pbkdf2@3.1.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew=="],
|
||||
|
||||
"@types/react": ["@types/react@19.1.13", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ=="],
|
||||
|
||||
"@types/secp256k1": ["@types/secp256k1@4.0.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-Rcvjl6vARGAKRO6jHeKMatGrvOMGrR/AR11N1x2LqintPCyDZ7NBhrh238Z2VZc7aM7KIwnFpFQ7fnfK4H/9Qw=="],
|
||||
|
||||
"@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="],
|
||||
|
||||
"@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="],
|
||||
@@ -154,26 +174,68 @@
|
||||
|
||||
"@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=="],
|
||||
|
||||
"aes-js": ["aes-js@3.1.2", "", {}, "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ=="],
|
||||
|
||||
"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-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||
|
||||
"asn1.js": ["asn1.js@4.10.1", "", { "dependencies": { "bn.js": "^4.0.0", "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" } }, "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw=="],
|
||||
|
||||
"assert": ["assert@2.1.0", "", { "dependencies": { "call-bind": "^1.0.2", "is-nan": "^1.3.2", "object-is": "^1.1.5", "object.assign": "^4.1.4", "util": "^0.12.5" } }, "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw=="],
|
||||
|
||||
"assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="],
|
||||
|
||||
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||
|
||||
"auto-bind": ["auto-bind@5.0.1", "", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="],
|
||||
|
||||
"available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"base-x": ["base-x@3.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA=="],
|
||||
|
||||
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
||||
|
||||
"bip39": ["bip39@3.1.0", "", { "dependencies": { "@noble/hashes": "^1.2.0" } }, "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A=="],
|
||||
|
||||
"blakejs": ["blakejs@1.2.1", "", {}, "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ=="],
|
||||
|
||||
"bn.js": ["bn.js@4.12.2", "", {}, "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw=="],
|
||||
|
||||
"brorand": ["brorand@1.1.0", "", {}, "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w=="],
|
||||
|
||||
"browserify-aes": ["browserify-aes@1.2.0", "", { "dependencies": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", "create-hash": "^1.1.0", "evp_bytestokey": "^1.0.3", "inherits": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA=="],
|
||||
|
||||
"browserify-cipher": ["browserify-cipher@1.0.1", "", { "dependencies": { "browserify-aes": "^1.0.4", "browserify-des": "^1.0.0", "evp_bytestokey": "^1.0.0" } }, "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w=="],
|
||||
|
||||
"browserify-des": ["browserify-des@1.0.2", "", { "dependencies": { "cipher-base": "^1.0.1", "des.js": "^1.0.0", "inherits": "^2.0.1", "safe-buffer": "^5.1.2" } }, "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A=="],
|
||||
|
||||
"browserify-rsa": ["browserify-rsa@4.1.1", "", { "dependencies": { "bn.js": "^5.2.1", "randombytes": "^2.1.0", "safe-buffer": "^5.2.1" } }, "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ=="],
|
||||
|
||||
"browserify-sign": ["browserify-sign@4.2.5", "", { "dependencies": { "bn.js": "^5.2.2", "browserify-rsa": "^4.1.1", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "elliptic": "^6.6.1", "inherits": "^2.0.4", "parse-asn1": "^5.1.9", "readable-stream": "^2.3.8", "safe-buffer": "^5.2.1" } }, "sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw=="],
|
||||
|
||||
"bs58": ["bs58@4.0.1", "", { "dependencies": { "base-x": "^3.0.2" } }, "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw=="],
|
||||
|
||||
"bs58check": ["bs58check@2.1.2", "", { "dependencies": { "bs58": "^4.0.0", "create-hash": "^1.1.0", "safe-buffer": "^5.1.2" } }, "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA=="],
|
||||
|
||||
"buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
||||
|
||||
"buffer-xor": ["buffer-xor@1.0.3", "", {}, "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
|
||||
|
||||
"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-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
||||
|
||||
"ccxt": ["ccxt@4.5.5", "", { "dependencies": { "ws": "^8.8.1" } }, "sha512-AyhwTFLkx4sO985ImIOfumEBox7AHD/iqk5tPGICObUSZG6wTXg0aRzU8Hjz974aCMG4msFwLk3A/iXPKAU4wA=="],
|
||||
|
||||
"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=="],
|
||||
@@ -182,6 +244,8 @@
|
||||
|
||||
"check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="],
|
||||
|
||||
"cipher-base": ["cipher-base@1.0.7", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.2" } }, "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA=="],
|
||||
|
||||
"cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="],
|
||||
|
||||
"cli-cursor": ["cli-cursor@4.0.0", "", { "dependencies": { "restore-cursor": "^4.0.0" } }, "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg=="],
|
||||
@@ -194,20 +258,42 @@
|
||||
|
||||
"convert-to-spaces": ["convert-to-spaces@2.0.1", "", {}, "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ=="],
|
||||
|
||||
"core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
|
||||
|
||||
"create-ecdh": ["create-ecdh@4.0.4", "", { "dependencies": { "bn.js": "^4.1.0", "elliptic": "^6.5.3" } }, "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A=="],
|
||||
|
||||
"create-hash": ["create-hash@1.2.0", "", { "dependencies": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", "md5.js": "^1.3.4", "ripemd160": "^2.0.1", "sha.js": "^2.4.0" } }, "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg=="],
|
||||
|
||||
"create-hmac": ["create-hmac@1.1.7", "", { "dependencies": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", "inherits": "^2.0.1", "ripemd160": "^2.0.0", "safe-buffer": "^5.0.1", "sha.js": "^2.4.8" } }, "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg=="],
|
||||
|
||||
"crypto-browserify": ["crypto-browserify@3.12.1", "", { "dependencies": { "browserify-cipher": "^1.0.1", "browserify-sign": "^4.2.3", "create-ecdh": "^4.0.4", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "diffie-hellman": "^5.0.3", "hash-base": "~3.0.4", "inherits": "^2.0.4", "pbkdf2": "^3.1.2", "public-encrypt": "^4.0.3", "randombytes": "^2.1.0", "randomfill": "^1.0.4" } }, "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
|
||||
|
||||
"define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
|
||||
|
||||
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
|
||||
|
||||
"des.js": ["des.js@1.1.0", "", { "dependencies": { "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" } }, "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg=="],
|
||||
|
||||
"diffie-hellman": ["diffie-hellman@5.0.3", "", { "dependencies": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", "randombytes": "^2.0.0" } }, "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg=="],
|
||||
|
||||
"dotenv": ["dotenv@17.2.2", "", {}, "sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q=="],
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
"elliptic": ["elliptic@6.6.1", "", { "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", "hash.js": "^1.0.0", "hmac-drbg": "^1.0.1", "inherits": "^2.0.4", "minimalistic-assert": "^1.0.1", "minimalistic-crypto-utils": "^1.0.1" } }, "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="],
|
||||
|
||||
"enc-utils": ["enc-utils@3.0.0", "", { "dependencies": { "is-typedarray": "1.0.0", "typedarray-to-buffer": "3.1.5" } }, "sha512-e57t/Z2HzWOLwOp7DZcV0VMEY8t7ptWwsxyp6kM2b2zrk6JqIpXxzkruHAMiBsy5wg9jp/183GdiRXCvBtzsYg=="],
|
||||
|
||||
"environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="],
|
||||
|
||||
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
||||
@@ -228,18 +314,30 @@
|
||||
|
||||
"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=="],
|
||||
|
||||
"ethereumjs-util": ["ethereumjs-util@7.1.5", "", { "dependencies": { "@types/bn.js": "^5.1.0", "bn.js": "^5.1.2", "create-hash": "^1.1.2", "ethereum-cryptography": "^0.1.3", "rlp": "^2.2.4" } }, "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg=="],
|
||||
|
||||
"ethereumjs-wallet": ["ethereumjs-wallet@1.0.2", "", { "dependencies": { "aes-js": "^3.1.2", "bs58check": "^2.1.2", "ethereum-cryptography": "^0.1.3", "ethereumjs-util": "^7.1.2", "randombytes": "^2.1.0", "scrypt-js": "^3.0.1", "utf8": "^3.0.0", "uuid": "^8.3.2" } }, "sha512-CCWV4RESJgRdHIvFciVQFnCHfqyhXWchTPlkfp28Qc53ufs+doi5I/cV2+xeK9+qEo25XCWfP9MiL+WEPAZfdA=="],
|
||||
|
||||
"evp_bytestokey": ["evp_bytestokey@1.0.3", "", { "dependencies": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" } }, "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="],
|
||||
|
||||
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||
|
||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
|
||||
"generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="],
|
||||
|
||||
"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=="],
|
||||
@@ -248,64 +346,148 @@
|
||||
|
||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||
|
||||
"has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"hash-base": ["hash-base@3.0.5", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1" } }, "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg=="],
|
||||
|
||||
"hash.js": ["hash.js@1.1.7", "", { "dependencies": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" } }, "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA=="],
|
||||
|
||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
|
||||
"hmac-drbg": ["hmac-drbg@1.0.1", "", { "dependencies": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", "minimalistic-crypto-utils": "^1.0.1" } }, "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg=="],
|
||||
|
||||
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||
|
||||
"indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="],
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
|
||||
"ink": ["ink@6.3.1", "", { "dependencies": { "@alcalzone/ansi-tokenize": "^0.2.0", "ansi-escapes": "^7.0.0", "ansi-styles": "^6.2.1", "auto-bind": "^5.0.1", "chalk": "^5.6.0", "cli-boxes": "^3.0.0", "cli-cursor": "^4.0.0", "cli-truncate": "^4.0.0", "code-excerpt": "^4.0.0", "es-toolkit": "^1.39.10", "indent-string": "^5.0.0", "is-in-ci": "^2.0.0", "patch-console": "^2.0.0", "react-reconciler": "^0.32.0", "signal-exit": "^3.0.7", "slice-ansi": "^7.1.0", "stack-utils": "^2.0.6", "string-width": "^7.2.0", "type-fest": "^4.27.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0", "ws": "^8.18.0", "yoga-layout": "~3.2.1" }, "peerDependencies": { "@types/react": ">=19.0.0", "react": ">=19.0.0", "react-devtools-core": "^6.1.2" }, "optionalPeers": ["@types/react", "react-devtools-core"] }, "sha512-3wGwITGrzL6rkWsi2gEKzgwdafGn4ZYd3u4oRp+sOPvfoxEHlnoB5Vnk9Uy5dMRUhDOqF3hqr4rLQ4lEzBc2sQ=="],
|
||||
|
||||
"is-arguments": ["is-arguments@1.2.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA=="],
|
||||
|
||||
"is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="],
|
||||
|
||||
"is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="],
|
||||
|
||||
"is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="],
|
||||
|
||||
"is-in-ci": ["is-in-ci@2.0.0", "", { "bin": { "is-in-ci": "cli.js" } }, "sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w=="],
|
||||
|
||||
"is-nan": ["is-nan@1.3.2", "", { "dependencies": { "call-bind": "^1.0.0", "define-properties": "^1.1.3" } }, "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w=="],
|
||||
|
||||
"is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
|
||||
|
||||
"is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="],
|
||||
|
||||
"is-typedarray": ["is-typedarray@1.0.0", "", {}, "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="],
|
||||
|
||||
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
||||
|
||||
"js-sha3": ["js-sha3@0.8.0", "", {}, "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="],
|
||||
|
||||
"js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],
|
||||
|
||||
"keccak": ["keccak@3.0.4", "", { "dependencies": { "node-addon-api": "^2.0.0", "node-gyp-build": "^4.2.0", "readable-stream": "^3.6.0" } }, "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
|
||||
"md5.js": ["md5.js@1.3.5", "", { "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1", "safe-buffer": "^5.1.2" } }, "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg=="],
|
||||
|
||||
"miller-rabin": ["miller-rabin@4.0.1", "", { "dependencies": { "bn.js": "^4.0.0", "brorand": "^1.0.1" }, "bin": { "miller-rabin": "bin/miller-rabin" } }, "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA=="],
|
||||
|
||||
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||
|
||||
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||
|
||||
"mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
|
||||
|
||||
"minimalistic-assert": ["minimalistic-assert@1.0.1", "", {}, "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="],
|
||||
|
||||
"minimalistic-crypto-utils": ["minimalistic-crypto-utils@1.0.1", "", {}, "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||
|
||||
"node-addon-api": ["node-addon-api@2.0.2", "", {}, "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA=="],
|
||||
|
||||
"node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="],
|
||||
|
||||
"object-is": ["object-is@1.1.6", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1" } }, "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q=="],
|
||||
|
||||
"object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="],
|
||||
|
||||
"object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="],
|
||||
|
||||
"onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
|
||||
|
||||
"parse-asn1": ["parse-asn1@5.1.9", "", { "dependencies": { "asn1.js": "^4.10.1", "browserify-aes": "^1.2.0", "evp_bytestokey": "^1.0.3", "pbkdf2": "^3.1.5", "safe-buffer": "^5.2.1" } }, "sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg=="],
|
||||
|
||||
"patch-console": ["patch-console@2.0.0", "", {}, "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA=="],
|
||||
|
||||
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="],
|
||||
|
||||
"pbkdf2": ["pbkdf2@3.1.5", "", { "dependencies": { "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "ripemd160": "^2.0.3", "safe-buffer": "^5.2.1", "sha.js": "^2.4.12", "to-buffer": "^1.2.1" } }, "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
|
||||
|
||||
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
||||
|
||||
"process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
|
||||
|
||||
"proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
|
||||
|
||||
"public-encrypt": ["public-encrypt@4.0.3", "", { "dependencies": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", "create-hash": "^1.1.0", "parse-asn1": "^5.0.0", "randombytes": "^2.0.1", "safe-buffer": "^5.1.2" } }, "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q=="],
|
||||
|
||||
"randombytes": ["randombytes@2.1.0", "", { "dependencies": { "safe-buffer": "^5.1.0" } }, "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ=="],
|
||||
|
||||
"randomfill": ["randomfill@1.0.4", "", { "dependencies": { "randombytes": "^2.0.5", "safe-buffer": "^5.1.0" } }, "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw=="],
|
||||
|
||||
"react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="],
|
||||
|
||||
"react-reconciler": ["react-reconciler@0.32.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ=="],
|
||||
|
||||
"readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
||||
|
||||
"restore-cursor": ["restore-cursor@4.0.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="],
|
||||
|
||||
"ripemd160": ["ripemd160@2.0.3", "", { "dependencies": { "hash-base": "^3.1.2", "inherits": "^2.0.4" } }, "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA=="],
|
||||
|
||||
"rlp": ["rlp@2.2.7", "", { "dependencies": { "bn.js": "^5.2.0" }, "bin": { "rlp": "bin/rlp" } }, "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
|
||||
"safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="],
|
||||
|
||||
"scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
|
||||
|
||||
"scrypt-js": ["scrypt-js@3.0.1", "", {}, "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA=="],
|
||||
|
||||
"secp256k1": ["secp256k1@4.0.4", "", { "dependencies": { "elliptic": "^6.5.7", "node-addon-api": "^5.0.0", "node-gyp-build": "^4.2.0" } }, "sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw=="],
|
||||
|
||||
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
|
||||
|
||||
"setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="],
|
||||
|
||||
"sha.js": ["sha.js@2.4.12", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.0" }, "bin": { "sha.js": "bin.js" } }, "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w=="],
|
||||
|
||||
"siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
|
||||
|
||||
"signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
|
||||
@@ -320,8 +502,12 @@
|
||||
|
||||
"std-env": ["std-env@3.9.0", "", {}, "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw=="],
|
||||
|
||||
"stream-browserify": ["stream-browserify@3.0.0", "", { "dependencies": { "inherits": "~2.0.4", "readable-stream": "^3.5.0" } }, "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA=="],
|
||||
|
||||
"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_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
|
||||
|
||||
"strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
||||
|
||||
"strip-literal": ["strip-literal@3.0.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA=="],
|
||||
@@ -338,18 +524,34 @@
|
||||
|
||||
"tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="],
|
||||
|
||||
"to-buffer": ["to-buffer@1.2.2", "", { "dependencies": { "isarray": "^2.0.5", "safe-buffer": "^5.2.1", "typed-array-buffer": "^1.0.3" } }, "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw=="],
|
||||
|
||||
"type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
|
||||
|
||||
"typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="],
|
||||
|
||||
"typedarray-to-buffer": ["typedarray-to-buffer@3.1.5", "", { "dependencies": { "is-typedarray": "^1.0.0" } }, "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"utf8": ["utf8@3.0.0", "", {}, "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ=="],
|
||||
|
||||
"util": ["util@0.12.5", "", { "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA=="],
|
||||
|
||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||
|
||||
"uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="],
|
||||
|
||||
"which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="],
|
||||
|
||||
"why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
|
||||
|
||||
"widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="],
|
||||
@@ -360,8 +562,38 @@
|
||||
|
||||
"yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="],
|
||||
|
||||
"browserify-rsa/bn.js": ["bn.js@5.2.2", "", {}, "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw=="],
|
||||
|
||||
"browserify-sign/bn.js": ["bn.js@5.2.2", "", {}, "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw=="],
|
||||
|
||||
"browserify-sign/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"ethereumjs-util/bn.js": ["bn.js@5.2.2", "", {}, "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw=="],
|
||||
|
||||
"ethereumjs-util/ethereum-cryptography": ["ethereum-cryptography@0.1.3", "", { "dependencies": { "@types/pbkdf2": "^3.0.0", "@types/secp256k1": "^4.0.1", "blakejs": "^1.1.0", "browserify-aes": "^1.2.0", "bs58check": "^2.1.2", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "hash.js": "^1.1.7", "keccak": "^3.0.0", "pbkdf2": "^3.0.17", "randombytes": "^2.1.0", "safe-buffer": "^5.1.2", "scrypt-js": "^3.0.0", "secp256k1": "^4.0.1", "setimmediate": "^1.0.5" } }, "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ=="],
|
||||
|
||||
"ethereumjs-wallet/ethereum-cryptography": ["ethereum-cryptography@0.1.3", "", { "dependencies": { "@types/pbkdf2": "^3.0.0", "@types/secp256k1": "^4.0.1", "blakejs": "^1.1.0", "browserify-aes": "^1.2.0", "bs58check": "^2.1.2", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "hash.js": "^1.1.7", "keccak": "^3.0.0", "pbkdf2": "^3.0.17", "randombytes": "^2.1.0", "safe-buffer": "^5.1.2", "scrypt-js": "^3.0.0", "secp256k1": "^4.0.1", "setimmediate": "^1.0.5" } }, "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ=="],
|
||||
|
||||
"ripemd160/hash-base": ["hash-base@3.1.2", "", { "dependencies": { "inherits": "^2.0.4", "readable-stream": "^2.3.8", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.1" } }, "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg=="],
|
||||
|
||||
"rlp/bn.js": ["bn.js@5.2.2", "", {}, "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw=="],
|
||||
|
||||
"secp256k1/node-addon-api": ["node-addon-api@5.1.0", "", {}, "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA=="],
|
||||
|
||||
"to-buffer/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
||||
|
||||
"browserify-sign/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
||||
|
||||
"browserify-sign/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
||||
|
||||
"cli-truncate/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="],
|
||||
|
||||
"ripemd160/hash-base/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||
|
||||
"ripemd160/hash-base/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
||||
|
||||
"ripemd160/hash-base/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# CCXT JavaScript Examples
|
||||
|
||||
These examples might require the following super-useful high-quality Node.js modules by [xpl](https://github.com/xpl):
|
||||
|
||||
- [ansicolor](https://github.com/xpl/ansicolor): A quality JavaScript library for the ANSI color/style management ([ansicolor @ npm](https://npmjs.com/package/ansicolor))
|
||||
- [as-table](https://github.com/xpl/as-table): A simple function that prints objects as ASCII tables ([as-table @ npm](https://npmjs.com/package/as-table))
|
||||
- [ololog](https://github.com/xpl/ololog): Platform-agnostic logging with blackjack and hookers ([ololog @ npm](https://npmjs.com/package/ololog))
|
||||
|
||||
All of the modules above are installed with the ccxt library devDependencies by npm automatically.
|
||||
|
||||
To run the ccxt JavaScript examples from any folder type in console:
|
||||
|
||||
```shell
|
||||
node path/to/example.js # substitute for actual filename here
|
||||
```
|
||||
@@ -0,0 +1,39 @@
|
||||
"use strict";
|
||||
|
||||
const ccxt = require ('../../ccxt.js');
|
||||
|
||||
// instantiate the exchange
|
||||
let exchange = new ccxt.coinbasepro ({
|
||||
'apiKey': 'XXXXXXXXXXXXXX',
|
||||
'secret': 'YYYYYYYYYYYYYY',
|
||||
});
|
||||
|
||||
async function checkOrders(){
|
||||
try {
|
||||
// fetch orders
|
||||
let orders = await exchange.fetchOrders ('BTC/USDT');
|
||||
// output the result
|
||||
console.log (exchange.id, 'fetched orders', orders);
|
||||
} catch (e) {
|
||||
if (e instanceof ccxt.DDoSProtection || e.message.includes ('ECONNRESET')) {
|
||||
console.log ('[DDoS Protection] ' + e.message);
|
||||
} else if (e instanceof ccxt.RequestTimeout) {
|
||||
console.log ('[Request Timeout] ' + e.message);
|
||||
} else if (e instanceof ccxt.AuthenticationError) {
|
||||
console.log ('[Authentication Error] ' + e.message);
|
||||
} else if (e instanceof ccxt.ExchangeNotAvailable) {
|
||||
console.log ('[Exchange Not Available Error] ' + e.message);
|
||||
} else if (e instanceof ccxt.ExchangeError) {
|
||||
console.log ('[Exchange Error] ' + e.message);
|
||||
} else if (e instanceof ccxt.NetworkError) {
|
||||
console.log ('[Network Error] ' + e.message);
|
||||
} else {
|
||||
// you can throw it if you want to stop the execution
|
||||
// console.log ('[Exception ' + e.constructor.name + '] ' + e.message);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for demonstrational purposes, we use 1000 ms interval
|
||||
setInterval(checkOrders, 1000);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user