Quickstart

Requirements

  • Python 3.11+

  • An eToro Public API key pair (api_key + user_key)

Installation

pip install etoropy

Or with uv:

uv add etoropy

Configuration

The SDK reads configuration from environment variables (prefix ETORO_) or accepts them directly in code.

Environment variables

Create a .env file:

ETORO_API_KEY=your-api-key
ETORO_USER_KEY=your-user-key
ETORO_MODE=demo          # "demo" or "real"
ETORO_BASE_URL=https://public-api.etoro.com
ETORO_WS_URL=wss://ws.etoro.com/ws

In code

from etoropy import EToroConfig, EToroTrading

config = EToroConfig(
    api_key="your-api-key",
    user_key="your-user-key",
    mode="demo",           # default
    timeout=30.0,          # HTTP timeout in seconds
    retry_attempts=3,      # retries on 5xx / rate-limit / connection errors
    retry_delay=1.0,       # base delay between retries (exponential backoff)
)
etoro = EToroTrading(config=config)

EToroConfig is a pydantic-settings BaseSettings subclass, so any field can be set via its ETORO_-prefixed environment variable.

First API call

import asyncio
from etoropy import EToroTrading, OrderOptions

async def main():
    async with EToroTrading() as etoro:
        # Load bundled instrument mappings for fast symbol resolution
        etoro.resolver.load_bundled_csv()

        # Fetch live rates
        rates = await etoro.get_rates(["AAPL", "TSLA", "BTC"])
        for r in rates:
            symbol = etoro.resolver.get_symbol(r.instrument_id) or str(r.instrument_id)
            print(f"{symbol}: bid={r.bid}, ask={r.ask}")

        # Place a market buy order (demo mode)
        result = await etoro.buy_by_amount(
            "AAPL",
            amount=100.0,
            options=OrderOptions(leverage=1, stop_loss=100.0, take_profit=200.0),
        )
        print(f"Order ID: {result.order_for_open.order_id}")

asyncio.run(main())

Trading basics

All trading methods accept a symbol string ("AAPL") or an integer instrument ID. The SDK resolves symbols automatically.

Market orders

# Buy / Sell by dollar amount
await etoro.buy_by_amount("AAPL", amount=500.0, options=OrderOptions(leverage=2))
await etoro.sell_by_amount("TSLA", amount=200.0)

# Buy / Sell by units
await etoro.buy_by_units("BTC", units=0.01)
await etoro.sell_by_units("ETH", units=1.5)

Limit orders

from etoropy import OrderOptions

token = await etoro.place_limit_order(
    "AAPL",
    is_buy=True,
    trigger_rate=140.0,
    amount=500.0,
    options=OrderOptions(leverage=1, stop_loss=130.0, take_profit=160.0),
)

Close positions

await etoro.close_position(position_id=123456)
await etoro.close_position(position_id=123456, units_to_deduct=0.5)  # partial
await etoro.close_all_positions()

Cancel orders

await etoro.cancel_order(order_id)
await etoro.cancel_limit_order(order_id)
await etoro.cancel_all_orders()
await etoro.cancel_all_limit_orders()

Error handling

All SDK errors inherit from EToroError:

from etoropy import EToroApiError, EToroRateLimitError, EToroAuthError

try:
    await etoro.buy_by_amount("AAPL", 100.0)
except EToroRateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after_s}s")
except EToroAuthError:
    print("Check your API key / user key")
except EToroApiError as e:
    print(f"API error {e.status_code}: {e.response_body}")

Logging

The SDK logs to the "etoropy" logger. Configure it with standard logging:

import logging
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("etoropy").setLevel(logging.INFO)