Quickstart
==========
Requirements
------------
- Python 3.11+
- An eToro Public API key pair (``api_key`` + ``user_key``)
Installation
------------
.. code-block:: bash
pip install etoropy
Or with `uv `_:
.. code-block:: bash
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:
.. code-block:: bash
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**
.. code-block:: python
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)
:class:`~etoropy.EToroConfig` is a
`pydantic-settings `_
``BaseSettings`` subclass, so any field can be set via its ``ETORO_``-prefixed
environment variable.
First API call
--------------
.. code-block:: python
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**
.. code-block:: python
# 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**
.. code-block:: python
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**
.. code-block:: python
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**
.. code-block:: python
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 :class:`~etoropy.EToroError`:
.. code-block:: python
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
:mod:`logging`:
.. code-block:: python
import logging
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("etoropy").setLevel(logging.INFO)