Skip to main content
Glama
VW3st

Vale Monitor

by VW3st

Vale Monitor

A self-hosted dashboard for watching Bybit trading accounts. It aggregates positions, balances and realised P&L across several accounts, reads candles, watches price alerts, stores strategy definitions, and exposes all of it to AI agents over MCP.

The dashboard is read-only against the exchange — it never places, modifies or cancels orders. Use read-only API keys.

This repository also contains backend-setup/, a separate Python trading bot that does place real leveraged orders. It is an independent system with its own setup, config and credentials — nothing in the dashboard imports it. Its setup and every config key are documented below.

  • Local SQLite — no database to provision. First boot creates the file.

  • Multi-account — aggregates any number of Bybit accounts.

  • Candles and prices — work with no exchange keys at all.

  • Alerts — price thresholds, evaluated on a background poll.

  • Strategies — stored definitions with free-form JSON rules.

  • AI analysis — optional, provider-agnostic (Claude or OpenAI).

  • MCP server — lets Claude Code read candles and manage alerts/strategies.

ContentsQuick start · Using the dashboard · Configuration · Claude Code / MCP · AI layer · Trading backend · Scripts · Architecture · Security


Quick start

Requires Node.js 20+ (tested on 24).

git clone <your-fork-url> vale-monitor
cd vale-monitor
npm install
npm run dev

Open http://127.0.0.1:5000. There is no configuration step and no login — the SQLite database is created at ./data/vale.db on first boot.

Copy .env.example to .env when you want to connect an exchange account or enable the AI layer. Nothing in it is required.

Verify a running instance end to end:

npm run smoke

There is no authentication. Anything that can reach the port can read your exchange balances and add or delete accounts. HOST defaults to 127.0.0.1 so the server is reachable only from your own machine — see Security notes before changing that.


Related MCP server: Bybit MCP Server

Using the dashboard

The app is a single page with five tabs:

Tab

What it does

Positions (default)

Open positions across every configured account, with live P&L, and a TradingView chart

Dashboard

Weekly highlights, trading reports by timeframe, and a per-account balance card (equity, 7-day and week-to-date performance, per-symbol breakdown, 1–5% position sizing)

Accounts

Add, test and remove exchange accounts

Streaming

Live wallet balances pushed over the WebSocket

Calculator

Compound growth projection across multiple accounts

Positions, Dashboard and Streaming stay empty until you connect an account — they read credentialed exchange endpoints. Candles, prices and market hours work immediately.

Alerts and strategies are API- and MCP-first: they have full REST and MCP coverage but no dedicated tab yet. Drive them from Claude Code (below), or directly over HTTP — see docs/API.md.


Configuration

Every setting lives in .env; see .env.example for the annotated list. Nothing is required — the app runs with no .env at all.

Variable

Default

Purpose

PORT / HOST

5000 / 127.0.0.1

Only change HOST behind an authenticating proxy

DATABASE_PATH

./data/vale.db

Where the SQLite file lives

ALERT_POLL_MS

30000

How often active alerts are re-checked

BYBIT_API_KEY / BYBIT_API_SECRET

Single-account fallback; prefer the Accounts tab

Connecting exchange accounts

Candles, prices and market hours need no credentials. Positions, balances and realised P&L do.

Add accounts in the Accounts tab so you can track several at once. Credentials are stored in the local database and are never returned by the API — account responses only report whether credentials are present. The BYBIT_API_KEY environment pair is a single-account fallback, used only when no accounts are configured.


Using it from Claude Code

The MCP server exposes 22 tools — candles, prices, positions, balances, trading reports, and full CRUD for alerts and strategies. No credential is needed.

1. Point Claude Code at it:

cp .mcp.json.example .mcp.json
{
  "mcpServers": {
    "vale-monitor": {
      "command": "npx",
      "args": ["tsx", "server/mcp/index.ts"],
      "env": { "VALE_API_URL": "http://127.0.0.1:5000" }
    }
  }
}

2. Start the app (npm run dev) — the MCP server talks to it over HTTP, so it must be running. Then ask Claude Code things like:

Read the last 200 hourly BTCUSDT candles and tell me where support is sitting.

Set an alert for ETHUSDT below 3000, and list what's already active.

Save a strategy for SOLUSDT on the 4h with these rules, then review it.

Available tools: server_status, get_candles, get_price, get_market_hours, get_positions, get_account_balances, get_trading_report, get_weekly_highlights, list_accounts, list_alerts, create_alert, cancel_alert, delete_alert, list_strategies, get_strategy, create_strategy, update_strategy, delete_strategy, ai_config, analyze_market, analyze_performance, review_strategy.

The HTTP API is equally open — no headers required:

curl "http://127.0.0.1:5000/api/market/candles?symbol=BTCUSDT&interval=60&limit=5"

The AI layer

Optional. Without a provider key, the AI endpoints return 503 and everything else works normally.

Three analyses are available — market commentary over a candle window, performance comparison across accounts, and strategy review — via POST /api/ai/market, /api/ai/performance and /api/ai/strategies/:id/review, and via the matching MCP tools.

Switching models

Two environment variables:

AI_PROVIDER=anthropic        # anthropic | openai
AI_MODEL=claude-opus-5       # any id in the registry
AI_EFFORT=medium             # low | medium | high | xhigh | max
ANTHROPIC_API_KEY=sk-ant-...

Registered by default:

Model

Provider

Context

$/Mtok in

$/Mtok out

claude-opus-5 (default)

Anthropic

1M

5

25

claude-sonnet-5

Anthropic

1M

3

15

claude-haiku-4-5

Anthropic

200K

1

5

claude-opus-4-8

Anthropic

1M

5

25

gpt-5.1, gpt-5, gpt-5-mini

OpenAI

400K

1.25 / 0.25

10 / 2

GET /api/ai/config returns the registry with an available flag per model based on which keys are set. Each response reports the model that answered, the token counts and an estimated cost.

The OpenAI ids and pricing are best-effort and move quickly — check https://platform.openai.com/docs/models before relying on them.

Adding a model or provider

Nothing outside server/ai/ hardcodes a model id.

A new model on an existing provider — add an entry to MODELS in server/ai/config.ts:

"claude-sonnet-4-6": {
  id: "claude-sonnet-4-6",
  provider: "anthropic",
  label: "Claude Sonnet 4.6",
  contextWindow: 1_000_000,
  maxOutputTokens: 128_000,
  pricing: { inputPerMTok: 3, outputPerMTok: 15 },
  supportsEffort: true,
},

Then set AI_MODEL=claude-sonnet-4-6.

A new provider — add its name to ProviderName, implement the Provider interface (a single complete() method) under server/ai/providers/, and wire it into the switch in createProvider. The switch is exhaustively typed, so TypeScript will point at anything you miss.

Note the request interface deliberately has no temperature: current Claude models reject temperature/top_p/top_k with a 400. Reasoning depth is expressed as effort and each provider maps it onto its own knob.

Prompts live in server/ai/analyze.ts — one system prompt plus one builder per analysis.


The trading backend (backend-setup/)

A separate Python system that runs a signal strategy across several Bybit accounts, manages take-profit ladders and trailing stops, and reports over Telegram.

WARNING: this one places real orders

Everything above is read-only. backend-setup/ opens and closes leveraged positions with real money and needs API keys with trade permission. A wrong position_usdt or leverage mis-sizes trades on every enabled account at once. Run on testnet first, and enable live accounts one at a time.

It has no automated tests. Nothing in the dashboard imports it, and the two share no configuration.

Setup

Requires Python 3.10+ (developed on 3.12).

cd backend-setup
python -m venv .venv
.venv\Scripts\activate        # Windows
source .venv/bin/activate      # macOS / Linux

pip install -r requirements.txt

cp .env.example .env
cp accounts_config.example.json accounts_config.json
cp trading_config.example.json  trading_config.json

All three copies are gitignored. Fill in .env with testnet keys and leave USE_TESTNET=true. Then check credentials before anything trades:

python account_balance_report.py

Run every script from inside backend-setup/. They open their config by relative path, so launching from the repository root fails to find it.

Configuration

Three files, split deliberately: credentials only in .env, account identity and sizing in accounts_config.json, strategy behaviour in trading_config.json.

accounts_config.json - who trades, and how big

{
  "accounts": [
    {
      "name": "testnet",
      "api_key_env": "BYBIT_TESTNET_API_KEY",
      "api_secret_env": "BYBIT_TESTNET_API_SECRET",
      "enabled": true,
      "position_usdt": 5,
      "leverage": 10
    }
  ],
  "trading_symbol": "BTCUSDT",
  "parallel_execution": true
}

Field

Meaning

api_key_env / api_secret_env

Names of .env variables, never the keys themselves

enabled

false takes the account out of every trade

position_usdt

Margin committed per trade, in USDT

leverage

Leverage applied to that margin

parallel_execution

Submit to all accounts at once rather than in sequence

How size is computed - qty = (position_usdt * leverage) / price. So position_usdt is the margin and the notional is position_usdt x leverage: 10 at 40x is a 400 USDT position, not 10. Raising leverage multiplies exposure on every enabled account simultaneously.

If accounts_config.json is absent, trade_manager.py falls back to single-account mode using the BYBIT_API_KEY pair from .env.

trading_config.json - how positions are managed

Key

Meaning

auto_mode

false keeps a human in the loop; true lets it act unattended

take_profit_mode

simple or strategic, selecting which ladder applies

simple_tp_levels / strategic_tp_levels

Ladders of {profit_pct, size_pct} - close size_pct of the position at profit_pct ROI

trailing_stop.target_roi_pct

ROI at which the trailing stop activates

trailing_stop.callback_rate

How far price may retrace before it fires

trailing_stop.check_interval_minutes

Poll interval for the trailing logic

signal_strength_threshold

Minimum signal score to act on

trading_filters.min_signal_strength

Weak / Moderate / Strong gate

trading_filters.allow_counter_trend

Permit entries against the higher-timeframe trend

enforce_highest_timeframe_trend

Require agreement from the top timeframe

skip_signals_for_open_positions

Ignore new signals while a position is open (no stacking)

trading_pairs

Symbols the strategy watches

symbol_config.<SYMBOL>

Per-symbol leverage and position_usdt override

size_pct values are portions of the position and need not total 100 - whatever is left rides to the stop.

The backend's AI model

s1.py can ask an LLM to comment on a signal before acting. Optional: with no key set, that step is skipped and the strategy still runs. The model is not hardcoded.

Setting

Effect

AI_MODEL

Model id to use (MODEL is accepted as an alias)

(neither set)

Falls back to gpt-5-mini

OPENROUTER_API_KEY set

Routed via https://openrouter.ai/api/v1

OPENROUTER_API_KEY unset

Goes to OpenAI using OPENAI_API_KEY

AI_MODEL=gpt-5-mini                    # OpenAI - bare ids
OPENAI_API_KEY=sk-...

AI_MODEL=anthropic/claude-sonnet-5     # or OpenRouter - "vendor/model" ids
OPENROUTER_API_KEY=sk-or-...

An unknown id fails at call time, not startup. This is separate from the dashboard's AI layer above - each has its own AI_MODEL, so you can run different models in each.

Running it

Script

Role

s1.py

Multi-timeframe EMA-cross strategy; emits signals from Bybit WebSocket data

trade_manager.py

Opens and manages positions - the main trading loop

multi_account_trader.py

Fans one decision out to every enabled account, sizing each

tpsl.py

Take-profit / stop-loss monitor and trailing stops

martingale_manager.py

Martingale ladder state

tele.py

Telegram bot - pushes signals, accepts commands

trading_watchdog.py

Supervises the other processes and restarts them

account_balance_report.py

Balance / PnL / position-sizing report

smi_indicator.py

Stochastic Momentum Index

time_sync.py

Keeps local time aligned with the exchange

cd backend-setup
python s1.py                      # strategy / signals
python tpsl.py                    # TP/SL monitor
python tele.py                    # Telegram bot
python trading_watchdog.py        # supervisor
python account_balance_report.py  # one-off report

Read the top of a script first - several assume others are already running.

Before enabling a live account

  • USE_TESTNET=true with testnet keys until the behaviour is understood.

  • auto_mode: false so nothing acts unattended.

  • One account enabled: true, the rest false.

  • Smallest position_usdt and lowest leverage the exchange accepts.

  • Confirm position_usdt x leverage is the notional you actually intend.

  • Watch a full open -> TP -> close cycle before adding a second account.

What it writes (all gitignored)

accounts_config.json, trading_config.json, .env, plus runtime state (all_trade_signals.json, pending_signals.json, session_state.json, tpsl.json, martingale_log.json, trading_decision_history.json, stream_data.json, pnl_history.json), the logs, and backups/. Deleting the state files resets it; the bot recreates what it needs - but not while running.

Deeper reference, including per-script detail: backend-setup/README.md.


Scripts

Command

What it does

npm run dev

API + Vite dev server on one port

npm run build

Client to dist/public, server bundle to dist/index.js

npm start

Run the production build

npm run check

TypeScript, no emit — the project's only static gate

npm run smoke

End-to-end HTTP check against a running server

npm run mcp

Run the MCP server directly (normally launched by the client)

npm run db:push

Push shared/schema.ts with drizzle-kit

There is no test runner or linter configured; npm run check and npm run smoke are the checks.


Architecture

One Express process serves both the API and the client. In development Vite runs as middleware inside it, so there is a single port and no separate dev server; in production the built assets are served statically. Vite owns the catch-all route and is registered last, so new routes must be added inside registerRoutes().

server/
  index.ts        Bootstrap: schema init, middleware, listen
  routes.ts       All HTTP routes
  storage.ts      Every database query
  db.ts           SQLite connection and schema bootstrap
  trading.ts      Multi-account aggregation and analytics
  bybit-api.ts    Authenticated exchange reads
  market.ts       Public market data (candles, prices)
  market-hours.ts Exchange open/closed calculation
  alerts.ts       Background alert evaluation
  websocket.ts    Session-authenticated /ws stream
  vite.ts         Dev middleware / static serving, logger
  ai/             Model registry, providers, analysis prompts
  mcp/            MCP server for agents
shared/schema.ts  Drizzle tables, Zod schemas, shared types
client/src/       React + Vite front end
scripts/          smoke test CLI
docs/API.md       HTTP and WebSocket reference

backend-setup/    Separate Python trading bot — places real orders, own README

Three tables: accounts, alerts, strategies. There are no users.

Two conventions run through the analytics:

  • ROI is margin-based, not notional. A trade's committed capital is cumEntryValue / leverage, so a 10x position reports return on the margin actually put up.

  • Weeks run Monday 00:00:00 UTC to Sunday 23:59:59.999 UTC, independent of the server's timezone.

Ratios are null rather than Infinity when there are no losses to divide by.

No authentication

Every route is open, and the WebSocket at /ws accepts any connection. There are no users, sessions, passwords or tokens in the codebase.

This is deliberate for a single-user tool bound to loopback. It also means the only thing standing between the internet and your exchange credentials is the HOST binding — see below.

Full endpoint and WebSocket reference: docs/API.md.


Security notes

  • There is no authentication. Any process or person who can reach the port can read your balances and positions, and add or delete exchange accounts.

  • HOST defaults to 127.0.0.1, which is the control that makes the above acceptable — the server is reachable only from your own machine. The app logs a warning if you bind it anywhere else. Before exposing it, put a reverse proxy with TLS and access control in front; do not simply set HOST=0.0.0.0.

  • Use read-only exchange API keys. Nothing here needs trade permissions, and a leaked read-only key cannot move funds.

  • Exchange credentials are stored unencrypted in the local SQLite file. Protect the file; it is as sensitive as the keys themselves.

  • .env, data/ and .mcp.json are gitignored. Keep it that way.

License

MIT — see LICENSE.

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    B
    quality
    D
    maintenance
    A Model Context Protocol server that enables AI coding tools like Claude Code and Cursor to interact with Bybit's trading platform for market data retrieval, account management, and trading operations.
    11
    17
    13
    MIT
  • F
    license
    B
    quality
    D
    maintenance
    A comprehensive MCP server providing full access to Bybit's v5 API for real-time market data, trading operations, and account management. It enables AI assistants to execute trades, manage positions, and monitor wallet balances with built-in safety controls for both testnet and production environments.
    22
    6
  • A
    license
    C
    quality
    D
    maintenance
    MCP server for Bybit exchange enabling 246 tools for trading, market data, account management, and more via natural language.
    100
    2
    MIT

View all related MCP servers

Related MCP Connectors

  • MCP server for Gainium — manage trading bots, deals, and balances via AI assistants

  • Open-source MCP server for Zerodha Kite Connect. Portfolio, market data, backtesting, alerts.

  • Hosted Amazon Seller and Vendor MCP server for Claude, ChatGPT, Cursor, Codex, Gemini, Copilot.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/VW3st/crypto-monitor'

If you have feedback or need assistance with the MCP directory API, please join our Discord server