Skip to main content

Broker-agnostic trading library for Python

Project description

Trading Lib

A broker-agnostic Python trading library that abstracts broker APIs for platform-independent algorithmic trading strategies.

Python 3.10+ License: MIT Code style: ruff

Features

  • Broker Agnostic: Write strategies once, run on any supported broker
  • Async-First: Built with asyncio for non-blocking I/O operations
  • Type-Safe: Full type hints with Pydantic models for runtime validation
  • Extensible: Easy to add new broker implementations via Protocol interfaces
  • Production Ready: Rate limiting, error handling, and connection management built-in

Supported Brokers

Broker Status Notes
MetaTrader 5 โœ… Implemented Full API coverage
Interactive Brokers ๐Ÿ”œ Planned Coming soon
Binance ๐Ÿ”œ Planned Coming soon

Installation

# Using pip
pip install tradingkit-mt5

# Using uv
uv add tradingkit-mt5

# From source
git clone https://github.com/LucianP984/py-trading-lib.git
cd py-trading-lib
pip install -e .

Requirements

  • Python 3.10+
  • MetaTrader 5 terminal installed (Windows only for MT5)
  • Pydantic 2.0+

Quick Start

import asyncio
from decimal import Decimal
from trading_lib import create_broker, BrokerType, OrderRequest, TradeAction, OrderType

async def main():
    # Create broker instance
    broker = create_broker(BrokerType.MT5)

    # Connect to MT5 terminal
    await broker.connect(
        login=12345678,
        password="your_password",
        server="YourBroker-Demo"
    )

    # Get account info
    account = await broker.get_account_info()
    print(f"Balance: {account.balance} {account.currency}")
    print(f"Equity: {account.equity}")
    print(f"Free Margin: {account.margin_free}")

    # Get symbol info
    symbol = await broker.get_symbol_info("EURUSD")
    print(f"EURUSD Spread: {symbol.spread} points")

    # Get latest tick
    tick = await broker.get_symbol_tick("EURUSD")
    print(f"Bid: {tick.bid}, Ask: {tick.ask}")

    # Get historical bars
    from trading_lib import Timeframe
    bars = await broker.get_bars("EURUSD", Timeframe.H1, start=0, count=100)
    print(f"Retrieved {len(bars)} bars")

    # Place a market order
    request = OrderRequest(
        action=TradeAction.DEAL,
        symbol="EURUSD",
        volume=Decimal("0.1"),
        type=OrderType.BUY,
        deviation=10,
        magic=12345,
        comment="Trading Lib"
    )

    # Check order before sending
    check = await broker.check_order(request)
    if check.valid:
        result = await broker.send_order(request)
        if result.success:
            print(f"Order placed! Ticket: {result.order}")

    # Get open positions
    positions = await broker.get_positions()
    for pos in positions:
        print(f"Position {pos.ticket}: {pos.symbol} {pos.volume} lots, P/L: {pos.profit}")

    # Disconnect
    await broker.disconnect()

asyncio.run(main())

Architecture

The library uses Protocol-based abstractions for broker-agnostic design:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                     Your Trading Strategy                    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                              โ”‚
                              โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      BrokerProtocol                          โ”‚
โ”‚  (ConnectionProtocol, AccountProtocol, OrderProtocol, ...)  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                              โ”‚
              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
              โ–ผ               โ–ผ               โ–ผ
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ”‚ MT5Clientโ”‚   โ”‚ IBClient โ”‚   โ”‚ Binance  โ”‚
        โ”‚          โ”‚   โ”‚ (planned)โ”‚   โ”‚ (planned)โ”‚
        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

API Reference

Factory

from trading_lib import create_broker, BrokerType

# Create MT5 broker
broker = create_broker(BrokerType.MT5)

# With custom path
broker = create_broker(BrokerType.MT5, path="C:/Program Files/MT5/terminal64.exe")

Connection

# Connect with login
await broker.connect(login=12345, password="pass", server="Demo-Server")

# Check connection
is_connected = await broker.is_connected()

# Disconnect
await broker.disconnect()

# Context manager
async with create_broker(BrokerType.MT5) as broker:
    await broker.connect(...)
    # Auto-disconnects on exit

Account Information

account = await broker.get_account_info()
# AccountInfo(login, balance, equity, margin, margin_free, leverage, ...)

terminal = await broker.get_terminal_info()
# TerminalInfo(name, company, connected, trade_allowed, ...)

Symbols

# Get all symbols
symbols = await broker.get_symbols()

# Filter by group
forex_symbols = await broker.get_symbols(group="*USD*")

# Get specific symbol info
eurusd = await broker.get_symbol_info("EURUSD")

# Get latest tick
tick = await broker.get_symbol_tick("EURUSD")

# Select symbol in Market Watch
await broker.select_symbol("EURUSD", enable=True)

Market Data

from trading_lib import Timeframe

# Get bars by position (0 = current bar)
bars = await broker.get_bars("EURUSD", Timeframe.H1, start=0, count=100)

# Get bars by date range
from datetime import datetime
bars = await broker.get_bars(
    "EURUSD",
    Timeframe.M15,
    start=datetime(2024, 1, 1),
    end=datetime(2024, 1, 31)
)

# Get ticks
ticks = await broker.get_ticks("EURUSD", start=datetime.now(), count=1000)

Orders

from trading_lib import OrderRequest, TradeAction, OrderType, OrderFilling

# Market order
request = OrderRequest(
    action=TradeAction.DEAL,
    symbol="EURUSD",
    volume=Decimal("0.1"),
    type=OrderType.BUY,
    deviation=10,
)

# Pending order
request = OrderRequest(
    action=TradeAction.PENDING,
    symbol="EURUSD",
    volume=Decimal("0.1"),
    type=OrderType.BUY_LIMIT,
    price=Decimal("1.0800"),
    sl=Decimal("1.0750"),
    tp=Decimal("1.0900"),
)

# Check and send
check = await broker.check_order(request)
if check.valid:
    result = await broker.send_order(request)

# Get active orders
orders = await broker.get_orders()
orders = await broker.get_orders(symbol="EURUSD")

Positions

# Get all positions
positions = await broker.get_positions()

# Filter by symbol
eurusd_positions = await broker.get_positions(symbol="EURUSD")

# Filter by ticket
position = await broker.get_positions(ticket=123456)

Trade History

from datetime import datetime, timedelta

start = datetime.now() - timedelta(days=30)
end = datetime.now()

# Get historical orders
orders = await broker.get_history_orders(start=start, end=end)

# Get deals
deals = await broker.get_history_deals(start=start, end=end)

Market Depth

# Subscribe to market depth
await broker.subscribe_book("EURUSD")

# Get current book
book = await broker.get_book("EURUSD")
for entry in book:
    print(f"{entry.type}: {entry.price} x {entry.volume}")

# Unsubscribe
await broker.unsubscribe_book("EURUSD")

Error Handling

from trading_lib import (
    TradingError,
    ConnectionError,
    AuthenticationError,
    OrderRejectedError,
    InsufficientMarginError,
)

try:
    await broker.connect(login=12345, password="wrong")
except AuthenticationError as e:
    print(f"Login failed: {e.message} (code: {e.code})")

try:
    result = await broker.send_order(request)
except OrderRejectedError as e:
    print(f"Order rejected: {e.message}")
    print(f"Original request: {e.request}")
except InsufficientMarginError:
    print("Not enough margin")

Adding Custom Brokers

Implement the BrokerProtocol to add new broker support:

from trading_lib import BrokerProtocol, register_broker, BrokerType

class MyBrokerClient(BrokerProtocol):
    @property
    def broker_name(self) -> str:
        return "MyBroker"

    async def connect(self, ...) -> bool:
        # Implementation
        ...

    async def get_account_info(self) -> AccountInfo:
        # Implementation
        ...

    # Implement all protocol methods...

# Register with factory
register_broker(BrokerType.MY_BROKER, MyBrokerClient)

Development

# Clone repository
git clone https://github.com/LucianP984/py-trading-lib.git
cd py-trading-lib

# Install with dev dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Type checking
mypy trading_lib

# Linting
ruff check trading_lib

License

MIT License - see LICENSE for details.

Disclaimer

This software is for educational and research purposes only. Trading financial instruments carries significant risk. Always test thoroughly with demo accounts before using real money. The authors are not responsible for any financial losses incurred through the use of this software.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tradingkit_mt5-0.1.0.tar.gz (23.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tradingkit_mt5-0.1.0-py3-none-any.whl (33.5 kB view details)

Uploaded Python 3

File details

Details for the file tradingkit_mt5-0.1.0.tar.gz.

File metadata

  • Download URL: tradingkit_mt5-0.1.0.tar.gz
  • Upload date:
  • Size: 23.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for tradingkit_mt5-0.1.0.tar.gz
Algorithm Hash digest
SHA256 ea4c15330a767c200851bb798391d40791c4082722835688c44d6ef4700115d0
MD5 61acb4b95e28030bf5f5b7f4c135e9bc
BLAKE2b-256 6b37345676fa60ffbbce596be66b9e6cb79f49ef0c91ae1301018de7fd6a1ff6

See more details on using hashes here.

Provenance

The following attestation bundles were made for tradingkit_mt5-0.1.0.tar.gz:

Publisher: publish.yml on LucianP984/py-trading-lib

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tradingkit_mt5-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: tradingkit_mt5-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 33.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for tradingkit_mt5-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d354875eaa82b7282543796c609db4674ce7f4cca90a3f71adb653a48bcb2f36
MD5 7c346255e261e0e73b3934c704207e76
BLAKE2b-256 cab1782dc37646d28f370717c079847bfb12eccf548ed4e748ef62e816b67fab

See more details on using hashes here.

Provenance

The following attestation bundles were made for tradingkit_mt5-0.1.0-py3-none-any.whl:

Publisher: publish.yml on LucianP984/py-trading-lib

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page