@raybooysen/etoro-agent
v1.0.25
Published
MCP server and CLI for the eToro Public API
Maintainers
Readme
etoro-agent
MCP server and CLI for the eToro Public API. Two interfaces, one codebase — use the MCP server from AI assistants (Claude Desktop, Claude Code, Cursor, OpenClaw, Windsurf) or the CLI from your terminal, scripts, and agents.
Table of Contents
- Features
- Prerequisites
- Installation
- Configuration
- CLI Reference
- MCP Server Setup
- MCP Tools Reference
- Demo vs Real
- Rate Limits
- Error Handling
- Development
- License
Features
| Category | Capabilities | |----------|-------------| | Market Data | Search instruments, live rates, historical candles (OHLC), closing prices, reference data (exchanges, industries, instrument types) | | Trading | Market orders (by amount or units), limit orders, close positions (full or partial), cancel orders. Demo and real environments. | | Portfolio | Current positions, P&L summary, order execution status, closed trade history | | Social | Discover/search traders, public portfolios, gain metrics, daily performance, copier data | | Watchlists | Full CRUD — create, rename, delete, reorder, add/remove instruments, default and public watchlists | | Feeds | Instrument and user social feeds, create posts, comment on posts | | Discovery | Curated investment lists, personalized market recommendations | | Agent Portfolios | Create/manage agent portfolios and user tokens |
Prerequisites
- Node.js 22+
- eToro API keys — two keys are required:
- API Key (
x-api-key) — obtained through the eToro API Portal subscription - User Key (
x-user-key) — generated in your eToro account at Settings > Trading > API Key Management
- API Key (
When creating your User Key, you choose:
- Environment — Demo or Real (keys are environment-specific)
- Permissions — Read (portfolio, market data) or Read+Write (trading, posts)
- IP Whitelist — optional restriction to specific IPs
- Expiration — optional expiry date
Installation
git clone https://github.com/raybooysen/etoro-agent.git
cd etoro-agent
npm install
npm run buildConfiguration
Both the CLI and MCP server accept configuration via environment variables with optional CLI argument overrides. CLI arguments take priority.
| Config | Env Var | CLI Arg | Required | Default |
|--------|---------|---------|----------|---------|
| API Key | ETORO_API_KEY | --api-key | Yes | — |
| User Key | ETORO_USER_KEY | --user-key | Yes | — |
| Environment | ETORO_ENVIRONMENT | --environment | No | demo |
Set environment variables once to avoid repeating credentials:
export ETORO_API_KEY=your-api-key
export ETORO_USER_KEY=your-user-key
export ETORO_ENVIRONMENT=demoOr pass inline with any command:
etoro-cli identity --api-key your-api-key --user-key your-user-keyCLI Reference
The CLI outputs JSON to stdout and errors to stderr. Exit code 0 on success, 1 on failure. Pipe output to jq for filtering or to other tools for processing.
etoro-cli <command> [subcommand] [args] [options]
etoro-cli helpIdentity
Get the authenticated user's account information.
etoro-cli identityMarket Data
Search instruments
Search for stocks, crypto, ETFs, indices, commodities, and currencies by symbol or name.
# Search by symbol (default — exact server-side match)
etoro-cli market search AAPL
# Search by name (client-side substring match)
etoro-cli market search Apple --filter-by name
# Paginate results
etoro-cli market search TSLA --page 1 --page-size 5Get instrument metadata
Retrieve detailed metadata for instruments by their IDs. Multiple IDs are automatically fetched individually and merged (the eToro API only supports one ID per request).
# Single instrument
etoro-cli market instrument 1137
# Multiple instruments (auto-fans out into individual requests with rate limiting)
etoro-cli market instrument 1137,1001,1003Get live rates
Get current market prices for instruments.
# Single instrument
etoro-cli market rates 1
# Multiple instruments (max 100, look up IDs with: etoro-cli market search <name>)
etoro-cli market rates <id1>,<id2>,<id3>Get historical candles
Retrieve OHLC candle data for charting and analysis.
# Default: 100 daily candles, newest first
etoro-cli market candles 1
# 1-hour candles, last 50
etoro-cli market candles 1 --interval OneHour --count 50
# 5-minute candles, oldest first
etoro-cli market candles 1 --interval FiveMinutes --count 200 --direction ascAvailable intervals: OneMinute, FiveMinutes, TenMinutes, FifteenMinutes, ThirtyMinutes, OneHour, FourHours, OneDay, OneWeek
Get reference data
Retrieve cached reference data (instrument types, exchanges, stock industries).
etoro-cli market ref instrument-types
etoro-cli market ref exchanges
etoro-cli market ref stocks-industriesCheck market status
Check if instruments are currently tradeable and whether their exchanges are open.
etoro-cli market status AAPL,BTC,TSLAReturns isCurrentlyTradable, isExchangeOpen, isBuyEnabled, exchange name, and asset class for each symbol. Use this before placing orders to verify the market is open.
Portfolio
View positions
Get all current open positions in your portfolio.
etoro-cli portfolio positionsP&L summary
Get profit and loss summary for your portfolio.
etoro-cli portfolio pnlOrder status
Check the execution status of a specific order.
etoro-cli portfolio order 12345678Trade history
Get closed trade history. The --min-date parameter is required.
# All trades since January 2025
etoro-cli portfolio history --min-date 2025-01-01
# Paginate
etoro-cli portfolio history --min-date 2025-01-01 --page 2 --page-size 10Trading
All trading commands route to demo or real API paths based on your configured environment. If you use a demo key with --environment demo, trades execute on your virtual portfolio.
Open a market order (by cash amount)
# Buy $100 of Apple stock with 1x leverage
etoro-cli trade open --instrument 1 --buy --leverage 1 --amount 100
# Short sell $50 of an instrument with 2x leverage (look up ID first)
# etoro-cli market search "Tesla" | jq '.items[0].instrumentId'
etoro-cli trade open --instrument <instrument_id> --sell --leverage 2 --amount 50
# With stop loss and take profit
etoro-cli trade open --instrument 1 --buy --leverage 1 --amount 100 \
--stop-loss 145.00 --take-profit 200.00Open a market order (by units)
# Buy 0.5 units of an instrument (look up ID first)
# etoro-cli market search "Bitcoin" | jq '.items[0].instrumentId'
etoro-cli trade open-units --instrument <instrument_id> --buy --leverage 1 --units 0.5Close a position
etoro-cli trade close 12345678Place a limit or stop entry order
A limit order executes when the instrument reaches your specified price. The same command works for both limit entries (buy the dip) and stop entries (buy the breakout) — the API accepts any rate:
# Limit buy — buy when price DROPS to $150
etoro-cli trade limit --instrument 1001 --buy --leverage 1 --amount 100 --rate 150.00 \
--stop-loss 140.00 --take-profit 180.00
# Stop entry buy (breakout) — buy when price RISES to $260
etoro-cli trade limit --instrument 1111 --buy --leverage 1 --amount 500 --rate 260.00 \
--stop-loss 250.00 --take-profit 280.00
# Stop entry sell (breakdown) — short when price DROPS to $4300
etoro-cli trade limit --instrument 18 --sell --leverage 1 --amount 1000 --rate 4300.00 \
--stop-loss 4400.00 --take-profit 4100.00Cancel an order
Cancel a pending open order or limit order.
etoro-cli trade cancel 12345678Social
Search for traders
Discover traders using eToro's search with filters.
# Find Popular Investors this year
etoro-cli social search --period CurrYear --popular-investor
# Paginate results
etoro-cli social search --period CurrYear --page 1 --page-size 10Available periods: CurrMonth, CurrQuarter, CurrYear, LastYear, LastTwoYears, OneMonthAgo, TwoMonthsAgo, ThreeMonthsAgo, SixMonthsAgo, OneYearAgo
Get user info
View detailed information about a specific trader.
# Public portfolio
etoro-cli social user sometrader portfolio
# Historical gain metrics
etoro-cli social user sometrader gain
# Trade statistics
etoro-cli social user sometrader tradeinfo
# Daily gain data
etoro-cli social user sometrader daily-gainGet copiers
Get copier information for the authenticated user (Popular Investors).
etoro-cli social copiersWatchlists
List all watchlists
etoro-cli watchlist listGet a specific watchlist
etoro-cli watchlist get abc123Create a watchlist
etoro-cli watchlist create "Tech Leaders"Delete a watchlist
etoro-cli watchlist delete abc123Add instruments to a watchlist
# Add instruments by ID (look up IDs first with: etoro-cli market search <name>)
etoro-cli watchlist add-items abc123 <id1>,<id2>,<id3>Remove instruments from a watchlist
etoro-cli watchlist remove-items abc123 <id1>,<id2>Feeds
Read instrument feed
Get social posts about a specific instrument.
# Default: 20 posts
etoro-cli feed instrument 1
# Custom pagination
etoro-cli feed instrument 1 --take 50 --offset 20Read user feed
Get social posts from a specific user.
etoro-cli feed user 12345 --take 10Create a post
etoro-cli feed post --message "Bullish on tech this quarter!" --owner 12345Discovery
Curated investment lists
etoro-cli discovery curatedPersonalized recommendations
# Default: 10 recommendations
etoro-cli discovery recommendations
# Custom count
etoro-cli discovery recommendations --count 25Scripting examples
The CLI outputs JSON, making it easy to use with jq and in scripts:
# Get Apple's current price
etoro-cli market rates 1 | jq '.[0].Ask'
# List instrument IDs from search
etoro-cli market search BTC | jq '.items[].instrumentId'
# Get total P&L
etoro-cli portfolio pnl | jq '.TotalPnL'
# Find Popular Investors with >20% yearly gain (using jq filtering)
etoro-cli social search --period CurrYear --popular-investor --page-size 50 | \
jq '.items[] | select(.gain > 20) | {userName, gain, riskScore}'MCP Server Setup
The MCP server exposes the same eToro API capabilities as 18 MCP tools, accessible from any MCP-compatible client. The server communicates via stdio transport.
Claude Desktop
Edit your config file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"etoro": {
"command": "node",
"args": ["/absolute/path/to/etoro-agent/dist/index.js"],
"env": {
"ETORO_API_KEY": "your-api-key",
"ETORO_USER_KEY": "your-user-key",
"ETORO_ENVIRONMENT": "demo"
}
}
}
}Restart Claude Desktop after saving.
Claude Code
Add via CLI:
claude mcp add etoro \
-e ETORO_API_KEY=your-api-key \
-e ETORO_USER_KEY=your-user-key \
-e ETORO_ENVIRONMENT=demo \
-- node /absolute/path/to/etoro-agent/dist/index.jsOr add to ~/.claude.json manually:
{
"mcpServers": {
"etoro": {
"command": "node",
"args": ["/absolute/path/to/etoro-agent/dist/index.js"],
"env": {
"ETORO_API_KEY": "your-api-key",
"ETORO_USER_KEY": "your-user-key",
"ETORO_ENVIRONMENT": "demo"
}
}
}
}Verify with claude mcp list.
OpenClaw
Via CLI:
openclaw mcp set etoro '{"command":"node","args":["/absolute/path/to/etoro-agent/dist/index.js"],"env":{"ETORO_API_KEY":"your-api-key","ETORO_USER_KEY":"your-user-key","ETORO_ENVIRONMENT":"demo"}}'Or add to your OpenClaw config under mcp.servers:
{
"mcp": {
"servers": {
"etoro": {
"command": "node",
"args": ["/absolute/path/to/etoro-agent/dist/index.js"],
"env": {
"ETORO_API_KEY": "your-api-key",
"ETORO_USER_KEY": "your-user-key",
"ETORO_ENVIRONMENT": "demo"
}
}
}
}
}Verify with openclaw mcp list.
Cursor
Create .cursor/mcp.json in your workspace root:
{
"mcpServers": {
"etoro": {
"command": "node",
"args": ["/absolute/path/to/etoro-agent/dist/index.js"],
"env": {
"ETORO_API_KEY": "your-api-key",
"ETORO_USER_KEY": "your-user-key",
"ETORO_ENVIRONMENT": "demo"
}
}
}
}Windsurf
Edit ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"etoro": {
"command": "node",
"args": ["/absolute/path/to/etoro-agent/dist/index.js"],
"env": {
"ETORO_API_KEY": "your-api-key",
"ETORO_USER_KEY": "your-user-key",
"ETORO_ENVIRONMENT": "demo"
}
}
}
}Dual Environment (Demo + Real)
Configure two server instances to access both environments simultaneously. This works with any MCP client:
{
"mcpServers": {
"etoro-demo": {
"command": "node",
"args": ["/absolute/path/to/etoro-agent/dist/index.js"],
"env": {
"ETORO_API_KEY": "demo-api-key",
"ETORO_USER_KEY": "demo-user-key",
"ETORO_ENVIRONMENT": "demo"
}
},
"etoro-real": {
"command": "node",
"args": ["/absolute/path/to/etoro-agent/dist/index.js"],
"env": {
"ETORO_API_KEY": "real-api-key",
"ETORO_USER_KEY": "real-user-key",
"ETORO_ENVIRONMENT": "real"
}
}
}
}Example MCP conversations
Once connected, you can ask your AI assistant things like:
- "Search for Apple stock and show me the current price"
- "Buy $500 of Tesla with 1x leverage on my demo account"
- "Show my portfolio and calculate my total P&L"
- "Find the top popular investors this year with low risk scores"
- "Create a watchlist called 'Tech Leaders' and add AAPL, MSFT, GOOGL"
- "Show me 1-hour candles for Bitcoin over the last 24 hours"
- "Close my Tesla position"
- "What limit orders do I have open?"
MCP Tools Reference
| Tool | Description | Parameters |
|------|-------------|------------|
| get_identity | Get authenticated user's account info | — |
| search_instruments | Search instruments by symbol or name | query, filterBy (symbol/name), page, pageSize |
| get_instruments | Get instrument metadata by ID | instrumentIds (comma-separated) |
| get_rates | Get current prices or closing prices | instrumentIds, type (current/closing_price), includeNames (opt-in) |
| get_candles | Get OHLC candle data | instrumentId, interval, count, direction |
| get_reference_data | Get instrument types, exchanges, or industries | type, ids (optional filter) |
| get_market_status | Check if instruments are currently tradeable | symbols (comma-separated, e.g. "AAPL,BTC") |
| open_order | Open a market order | order_type (by_amount/by_units), InstrumentID, IsBuy, Leverage, Amount/AmountInUnits |
| close_position | Close an open position | positionId, UnitsToDeduct (optional partial close) |
| manage_order | Cancel or place limit orders | action (cancel_open_order/cancel_close_order/place_limit_order/cancel_limit_order), orderId, order params |
| get_portfolio | View positions, P&L, or order status | view (positions/pnl/order), orderId |
| get_trade_history | Get closed trade history | minDate (YYYY-MM-DD), page, pageSize |
| search_people | Search/discover traders | action (search/lookup), period, filters |
| get_user_info | Get user portfolio, gain, trade info | view (portfolio/tradeinfo/gain/daily_gain/copiers), username |
| manage_watchlists | Full watchlist CRUD | action (list/get/create/delete/rename/add_items/remove_items/...), params per action |
| get_discovery | Curated lists or recommendations | type (curated_lists/recommendations), count |
| get_feeds | Read instrument or user feeds | type (instrument/user), id, take, offset |
| create_post | Create a post or comment | action (post/comment), message, owner/postId |
| manage_agent_portfolios | Agent portfolio CRUD + tokens | action (list/create/delete/create_token/revoke_token/update_token), params per action |
Demo vs Real
eToro API keys are environment-specific — you choose Demo or Real when generating keys. The server uses different API paths based on your configured environment:
| Category | Demo Path | Real Path |
|----------|-----------|-----------|
| Trading execution | /trading/execution/demo/... | /trading/execution/... |
| Portfolio | /trading/info/demo/portfolio | /trading/info/portfolio |
| P&L | /trading/info/demo/pnl | /trading/info/real/pnl |
| Order info | /trading/info/demo/orders/{id} | /trading/info/real/orders/{id} |
Market data, social, watchlists, feeds, and discovery endpoints are shared — they work the same regardless of environment.
If there's a key/environment mismatch (e.g., a demo key with --environment real), the eToro API returns an authentication error. The server never blocks requests — it routes to the configured paths and passes through whatever eToro returns.
Rate Limits
The eToro API enforces rate limits on a rolling 1-minute window:
| Limit | Operations | |-------|-----------| | 60 req/min | GET — market data, portfolio queries, social, watchlist reads | | 20 req/min | Write — trade execution, watchlist mutations, posts |
The client proactively tracks request timestamps and waits when a bucket is full. If the server still returns HTTP 429, it retries with exponential backoff (max 3 retries).
Reference data (instrument types, exchanges, industries) is cached for 24 hours to conserve rate limit quota.
Error Handling
CLI errors
Errors are written to stderr as JSON with a non-zero exit code:
{
"error": "HTTP 401: Unauthorized",
"statusCode": 401,
"errorCode": "Unauthorized",
"body": {
"errorCode": "Unauthorized",
"errorMessage": "Unauthorized"
}
}MCP errors
MCP tool handlers never throw. On failure, they return { isError: true, content: [{ type: "text", text: "error message" }] }, allowing the AI assistant to handle the error gracefully.
Common errors
| Status | Meaning | Resolution | |--------|---------|------------| | 401 | Invalid or expired API keys | Regenerate keys in eToro Settings | | 403 | Insufficient permissions | Ensure key has Read+Write for trading | | 429 | Rate limit exceeded | Wait and retry (automatic) | | 400 | Invalid request parameters | Check parameter values and types |
Development
npm run build # Compile TypeScript (MCP + CLI)
npm run dev # Run MCP server with hot-reload
npm run cli # Run CLI with hot-reload (tsx)
npm test # Run unit tests
npm run test:watch # Run tests in watch mode
npm run test:coverage # Run tests with coverage report
npm run test:integration # Run integration tests (requires .env)
npm run lint # Run ESLintRun a single test file:
npx vitest run tests/unit/utils/cache.test.tsIntegration tests
Copy .env.example to .env, fill in your API keys, and run:
cp .env.example .env
# Edit .env with your keys
npm run test:integration