A minimal Python wrapper for Alpaca Markets API
Project description
🦙 AlpacaFarmer
A modern, async-first Python wrapper for the Alpaca Markets API with full Pydantic v2 validation.
AlpacaFarmer provides a clean, type-safe interface to Alpaca's Trading, Broker, and Market Data APIs with comprehensive validation, async support, and intuitive error handling.
✨ Features
- 🔄 Async-first design - Built with
httpxfor high-performance async operations - 📊 Pydantic v2 validation - All request/response models with strict type checking
- 🎯 Full API coverage - Trader, Broker, and Market Data APIs
- 🔒 Type hints throughout - Complete type annotations for IDE support
- 🌍 Environment-aware - Easy switching between paper and live trading
- ⚠️ Comprehensive error handling - Typed exceptions for all error scenarios
- 🧩 35+ Pydantic models - Complete data models for all API responses
- 🏷️ 18 type-safe enums - No magic strings, full autocomplete support
📦 Installation
Using pip
pip install alpacafarmer
Using UV (recommended)
uv add alpacafarmer
Development Installation
git clone https://github.com/alpacafarmer/alpacafarmer.git
cd alpacafarmer
uv sync
🚀 Quick Start
Basic Setup
import asyncio
from alpacafarmer import AlpacaAuth, TraderClient, Environment
async def main():
# Initialize authentication
auth = AlpacaAuth(
api_key="your-api-key",
api_secret="your-api-secret",
environment=Environment.PAPER # Use LIVE for production
)
# Use the client with async context manager
async with TraderClient(auth) as client:
account = await client.get_account()
print(f"Account ID: {account.id}")
print(f"Buying Power: ${account.buying_power}")
print(f"Portfolio Value: ${account.portfolio_value}")
asyncio.run(main())
Using Environment Variables
export ALPACA_API_KEY="your-api-key"
export ALPACA_SECRET_KEY="your-api-secret"
from alpacafarmer import AlpacaAuth, TraderClient
# Credentials loaded automatically from environment
auth = AlpacaAuth()
📈 TraderClient API
The TraderClient is for individual traders using Alpaca's Trading API.
Getting Account Information
from alpacafarmer import AlpacaAuth, TraderClient
async with TraderClient(AlpacaAuth()) as client:
# Get account details
account = await client.get_account()
print(f"Cash: ${account.cash}")
print(f"Buying Power: ${account.buying_power}")
print(f"Day Trading BP: ${account.daytrading_buying_power}")
print(f"Pattern Day Trader: {account.pattern_day_trader}")
Creating Orders
from alpacafarmer import (
AlpacaAuth, TraderClient, OrderRequest,
OrderSide, OrderType, TimeInForce
)
async with TraderClient(AlpacaAuth()) as client:
# Market order
market_order = await client.create_order(OrderRequest(
symbol="AAPL",
qty=10,
side=OrderSide.BUY,
type=OrderType.MARKET,
time_in_force=TimeInForce.DAY
))
print(f"Market Order ID: {market_order.id}")
# Limit order
limit_order = await client.create_order(OrderRequest(
symbol="MSFT",
qty=5,
side=OrderSide.BUY,
type=OrderType.LIMIT,
time_in_force=TimeInForce.GTC,
limit_price=350.00
))
print(f"Limit Order ID: {limit_order.id}")
# Stop-loss order
stop_order = await client.create_order(OrderRequest(
symbol="GOOGL",
qty=2,
side=OrderSide.SELL,
type=OrderType.STOP,
time_in_force=TimeInForce.GTC,
stop_price=140.00
))
print(f"Stop Order ID: {stop_order.id}")
# Notional order (dollar amount instead of shares)
notional_order = await client.create_order(OrderRequest(
symbol="TSLA",
notional=1000.00, # Buy $1000 worth
side=OrderSide.BUY,
type=OrderType.MARKET,
time_in_force=TimeInForce.DAY
))
Managing Orders
from alpacafarmer import ListOrdersRequest, OrderUpdate
async with TraderClient(AlpacaAuth()) as client:
# List open orders
open_orders = await client.list_orders(ListOrdersRequest(
status="open",
limit=100
))
# Get specific order
order = await client.get_order("order-id")
# Replace/modify an order
updated_order = await client.replace_order(
order_id="order-id",
request=OrderUpdate(
qty=15,
limit_price=355.00
)
)
# Cancel an order
await client.cancel_order("order-id")
# Cancel all orders
cancelled = await client.cancel_all_orders()
Managing Positions
from decimal import Decimal
async with TraderClient(AlpacaAuth()) as client:
# List all positions
positions = await client.list_positions()
for pos in positions:
print(f"{pos.symbol}: {pos.qty} shares @ ${pos.avg_entry_price}")
print(f" P/L: ${pos.unrealized_pl} ({pos.unrealized_plpc}%)")
# Get specific position
aapl_position = await client.get_position("AAPL")
# Close a position completely
close_order = await client.close_position("AAPL")
# Close partial position (by quantity)
partial_close = await client.close_position("MSFT", qty=Decimal("5"))
# Close partial position (by percentage)
half_close = await client.close_position("GOOGL", percentage=Decimal("50"))
# Close all positions
await client.close_all_positions(cancel_orders=True)
Listing Assets
from alpacafarmer import ListAssetsRequest, AssetClass, AssetStatus
async with TraderClient(AlpacaAuth()) as client:
# List all tradable US equities
assets = await client.list_assets(ListAssetsRequest(
asset_class=AssetClass.US_EQUITY,
status=AssetStatus.ACTIVE
))
# Get specific asset info
aapl = await client.get_asset("AAPL")
print(f"Name: {aapl.name}")
print(f"Tradable: {aapl.tradable}")
print(f"Fractionable: {aapl.fractionable}")
print(f"Shortable: {aapl.shortable}")
🏦 BrokerClient API
The BrokerClient is for broker-dealers managing customer accounts via Alpaca's Broker API.
Creating Customer Accounts
from datetime import datetime, timezone
from alpacafarmer import (
AlpacaAuth, BrokerClient, Environment,
CreateAccountRequest, Contact, Identity, Disclosures, Agreement
)
auth = AlpacaAuth(
api_key="broker-api-key",
api_secret="broker-api-secret",
environment=Environment.PAPER # Uses sandbox for paper
)
async with BrokerClient(auth) as client:
account = await client.create_account(CreateAccountRequest(
contact=Contact(
email_address="john.doe@example.com",
phone_number="555-123-4567",
street_address=["123 Main St"],
city="New York",
state="NY",
postal_code="10001"
),
identity=Identity(
given_name="John",
family_name="Doe",
date_of_birth="1990-01-15",
tax_id="123-45-6789",
country_of_citizenship="USA",
country_of_tax_residence="USA"
),
disclosures=Disclosures(
is_control_person=False,
is_affiliated_exchange_or_finra=False,
is_politically_exposed=False,
immediate_family_exposed=False
),
agreements=[
Agreement(
agreement="customer_agreement",
signed_at=datetime.now(timezone.utc),
ip_address="192.168.1.1"
)
]
))
print(f"Created account: {account.id}")
Managing Accounts
from alpacafarmer import ListAccountsRequest
async with BrokerClient(auth) as client:
# List all accounts
accounts = await client.list_accounts()
# List with filtering
recent_accounts = await client.list_accounts(ListAccountsRequest(
query="john",
created_after=datetime(2024, 1, 1)
))
# Get specific account
account = await client.get_account("account-id")
# Delete/close account
await client.delete_account("account-id")
Trading on Behalf of Accounts
from alpacafarmer import OrderRequest, OrderSide, OrderType, TimeInForce
async with BrokerClient(auth) as client:
# Create order for customer account
order = await client.create_order(
account_id="customer-account-id",
request=OrderRequest(
symbol="AAPL",
qty=10,
side=OrderSide.BUY,
type=OrderType.MARKET,
time_in_force=TimeInForce.DAY
)
)
# Get orders for account
orders = await client.get_orders("customer-account-id")
# Cancel order for account
await client.cancel_order("customer-account-id", "order-id")
Fund Transfers (ACH)
from alpacafarmer import (
ACHRelationshipRequest, TransferRequest,
BankAccountType, TransferType, TransferDirection
)
async with BrokerClient(auth) as client:
# Create ACH relationship
ach = await client.create_ach_relationship(
account_id="customer-account-id",
request=ACHRelationshipRequest(
account_owner_name="John Doe",
bank_account_type=BankAccountType.CHECKING,
bank_account_number="123456789",
bank_routing_number="021000021",
nickname="Primary Checking"
)
)
# List ACH relationships
relationships = await client.get_ach_relationships("customer-account-id")
# Create deposit transfer
transfer = await client.create_transfer(
account_id="customer-account-id",
request=TransferRequest(
transfer_type=TransferType.ACH,
relationship_id=ach.id,
amount=1000.00,
direction=TransferDirection.INCOMING
)
)
# Get transfers
transfers = await client.get_transfers("customer-account-id")
Journal Entries
from alpacafarmer import JournalRequest, JournalEntryType
async with BrokerClient(auth) as client:
# Cash journal between accounts
journal = await client.create_journal(JournalRequest(
from_account="source-account-id",
to_account="destination-account-id",
entry_type=JournalEntryType.JNLC, # Cash journal
amount=500.00,
description="Account transfer"
))
# Security journal (transfer shares)
security_journal = await client.create_journal(JournalRequest(
from_account="source-account-id",
to_account="destination-account-id",
entry_type=JournalEntryType.JNLS, # Security journal
symbol="AAPL",
qty=10,
description="Share transfer"
))
# Get all journals
journals = await client.get_journals()
📊 MarketDataClient API
The MarketDataClient provides access to real-time and historical market data.
Historical Bars (OHLCV)
from datetime import datetime, timedelta
from alpacafarmer import (
AlpacaAuth, MarketDataClient,
BarsRequest, Timeframe, DataFeed
)
async with MarketDataClient(AlpacaAuth()) as client:
# Get historical bars for multiple symbols
bars = await client.get_bars(BarsRequest(
symbols=["AAPL", "MSFT", "GOOGL"],
timeframe=Timeframe.DAY_1,
start=datetime.now() - timedelta(days=30),
end=datetime.now(),
limit=1000,
feed=DataFeed.IEX
))
for symbol, symbol_bars in bars.items():
print(f"\n{symbol}:")
for bar in symbol_bars[-5:]: # Last 5 bars
print(f" {bar.t}: O={bar.o} H={bar.h} L={bar.l} C={bar.c} V={bar.v}")
# Get bars for single symbol
aapl_bars = await client.get_bars_single("AAPL", BarsRequest(
timeframe=Timeframe.HOUR_1,
start=datetime.now() - timedelta(days=7)
))
# Get latest bars
latest = await client.get_latest_bars(
symbols=["AAPL", "MSFT"],
feed=DataFeed.SIP
)
Latest Quotes
async with MarketDataClient(AlpacaAuth()) as client:
# Get latest quotes
quotes = await client.get_latest_quotes(
symbols=["AAPL", "MSFT", "GOOGL"],
feed=DataFeed.SIP
)
for symbol, quote in quotes.items():
spread = quote.ap - quote.bp
print(f"{symbol}: Bid={quote.bp} x {quote.bs}, Ask={quote.ap} x {quote.as_}")
print(f" Spread: ${spread:.2f}")
Market Snapshots
async with MarketDataClient(AlpacaAuth()) as client:
# Get comprehensive snapshot
snapshot = await client.get_snapshot("AAPL")
if snapshot.latest_trade:
print(f"Latest Trade: ${snapshot.latest_trade.p} ({snapshot.latest_trade.s} shares)")
if snapshot.latest_quote:
print(f"Latest Quote: {snapshot.latest_quote.bp} x {snapshot.latest_quote.ap}")
if snapshot.daily_bar:
print(f"Today: O={snapshot.daily_bar.o} H={snapshot.daily_bar.h} "
f"L={snapshot.daily_bar.l} C={snapshot.daily_bar.c}")
# Get multiple snapshots
snapshots = await client.get_snapshots(
symbols=["AAPL", "MSFT", "GOOGL", "TSLA"],
feed=DataFeed.SIP
)
Historical Trades
from alpacafarmer import TradesRequest
async with MarketDataClient(AlpacaAuth()) as client:
# Get historical trades
trades = await client.get_trades(TradesRequest(
symbols=["AAPL"],
start=datetime.now() - timedelta(hours=1),
limit=100
))
# Get latest trades
latest_trades = await client.get_latest_trades(
symbols=["AAPL", "MSFT"]
)
🏗️ Pydantic Models
AlpacaFarmer provides 35+ Pydantic models for complete type safety and validation.
Key Models
| Category | Models |
|---|---|
| Account | Account, AccountConfiguration |
| Orders | OrderRequest, Order, OrderUpdate, ListOrdersRequest |
| Positions | Position, ListPositionsRequest |
| Assets | Asset, ListAssetsRequest |
| Market Data | Bar, Quote, Trade, Snapshot, BarsRequest, QuotesRequest, TradesRequest |
| Broker | BrokerAccount, CreateAccountRequest, Contact, Identity, Disclosures, Agreement, TrustedContact |
| Transfers | Transfer, TransferRequest, ACHRelationship, ACHRelationshipRequest |
| Journals | Journal, JournalRequest |
Validation Example
from pydantic import ValidationError
from alpacafarmer import OrderRequest, OrderSide, OrderType, TimeInForce
# Validation catches errors before API calls
try:
order = OrderRequest(
symbol="", # Invalid: empty symbol
qty=-10, # Invalid: negative quantity
side=OrderSide.BUY,
type=OrderType.MARKET,
time_in_force=TimeInForce.DAY
)
except ValidationError as e:
print("Validation errors:")
for error in e.errors():
print(f" {error['loc']}: {error['msg']}")
Type-Safe Enums
from alpacafarmer import (
OrderSide, OrderType, TimeInForce, OrderStatus,
AssetClass, AssetStatus, PositionSide, AccountStatus,
Timeframe, DataFeed,
TransferDirection, TransferType, TransferStatus,
BankAccountType, JournalEntryType, JournalStatus,
FundingSource, TaxIdType
)
# Full IDE autocomplete support
order_type = OrderType.LIMIT # Not "limit" string
timeframe = Timeframe.HOUR_1 # Not "1Hour" string
⚠️ Error Handling
AlpacaFarmer provides typed exceptions for all error scenarios:
from alpacafarmer import (
AlpacaError,
APIError,
AuthenticationError,
RateLimitError,
ValidationError,
TraderClient,
AlpacaAuth
)
async with TraderClient(AlpacaAuth()) as client:
try:
account = await client.get_account()
except AuthenticationError as e:
# Invalid or missing API credentials
print(f"Auth failed: {e.message}")
except RateLimitError as e:
# API rate limit exceeded
print(f"Rate limited: {e.message}")
if e.retry_after:
print(f"Retry after {e.retry_after} seconds")
except APIError as e:
# HTTP API error with status code
print(f"API Error [{e.status_code}]: {e.message}")
print(f"Response: {e.response_body}")
except ValidationError as e:
# Request validation failed
print(f"Validation failed: {e.message}")
for error in e.errors:
print(f" - {error}")
except AlpacaError as e:
# Base exception for all Alpaca errors
print(f"Error: {e.message}")
⚙️ Configuration
Environment Options
| Environment | Trading URL | Data URL | Broker URL |
|---|---|---|---|
PAPER |
paper-api.alpaca.markets |
data.alpaca.markets |
broker-api.sandbox.alpaca.markets |
LIVE |
api.alpaca.markets |
data.alpaca.markets |
broker-api.alpaca.markets |
Environment Variables
| Variable | Description |
|---|---|
ALPACA_API_KEY |
API key for authentication |
ALPACA_SECRET_KEY |
Secret key for authentication |
ALPACA_OAUTH_TOKEN |
OAuth token (alternative to API key/secret) |
Authentication Methods
from alpacafarmer import AlpacaAuth, Environment
# Method 1: Direct credentials
auth = AlpacaAuth(
api_key="your-key",
api_secret="your-secret",
environment=Environment.PAPER
)
# Method 2: Environment variables
auth = AlpacaAuth() # Reads from ALPACA_API_KEY and ALPACA_SECRET_KEY
# Method 3: OAuth token
auth = AlpacaAuth(oauth_token="your-oauth-token")
# Check environment
if auth.is_paper:
print("Using paper trading environment")
🛠️ Development
Clone and Setup
# Clone the repository
git clone https://github.com/alpacafarmer/alpacafarmer.git
cd alpacafarmer
# Install with UV (recommended)
uv sync
# Or with pip
pip install -e ".[dev]"
Running Tests
# Run all tests
uv run pytest
# Run with coverage
uv run pytest --cov=alpacafarmer
# Run specific test file
uv run pytest tests/test_trader_client.py
# Run with verbose output
uv run pytest -v
Code Quality
# Type checking
uv run mypy src/alpacafarmer
# Linting
uv run ruff check src/
# Formatting
uv run ruff format src/
📤 Building and Publishing
Build with UV
# Build the package
uv build
# This creates:
# - dist/alpacafarmer-0.1.0.tar.gz
# - dist/alpacafarmer-0.1.0-py3-none-any.whl
Publish to PyPI
# Publish to PyPI (requires credentials)
uv publish
# Publish to TestPyPI first
uv publish --repository testpypi
📋 API Coverage
| API | Category | Status |
|---|---|---|
| Trader | Account | ✅ Complete |
| Orders (create, list, get, cancel, replace) | ✅ Complete | |
| Positions (list, get, close) | ✅ Complete | |
| Assets (list, get) | ✅ Complete | |
| Broker | Accounts (create, list, get, update, delete) | ✅ Complete |
| Trading (orders on behalf of accounts) | ✅ Complete | |
| ACH Relationships | ✅ Complete | |
| Transfers | ✅ Complete | |
| Journals | ✅ Complete | |
| Market Data | Bars (historical, latest) | ✅ Complete |
| Quotes (historical, latest) | ✅ Complete | |
| Trades (historical, latest) | ✅ Complete | |
| Snapshots | ✅ Complete |
📚 Resources
🤝 Contributing
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests for new functionality
- Ensure all tests pass (
uv run pytest) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Guidelines
- Follow existing code style
- Add type hints to all functions
- Write docstrings for public APIs
- Include tests for new features
- Update documentation as needed
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
⚠️ Disclaimer
This library is not affiliated with, endorsed by, or connected to Alpaca Markets, Inc. Use at your own risk. Always test thoroughly with paper trading before using real money. The authors are not responsible for any financial losses incurred through the use of this software.
Made with ❤️ for the trading community
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file alpacafarmer-0.1.5.tar.gz.
File metadata
- Download URL: alpacafarmer-0.1.5.tar.gz
- Upload date:
- Size: 27.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90b219f543fad1499dc0118ea7592f1b0e863e593788e58b4390c05669f5eca6
|
|
| MD5 |
8d44dbdda0a6474a44aada1bbae52c3b
|
|
| BLAKE2b-256 |
e79063326cda902cd31bbc8799edee7e88ee30e161c1e19fe63f9b2bb78e62e1
|
File details
Details for the file alpacafarmer-0.1.5-py3-none-any.whl.
File metadata
- Download URL: alpacafarmer-0.1.5-py3-none-any.whl
- Upload date:
- Size: 29.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f16c6d0731168c82a99970a4283c717628d1b6913f0b9bdbf4a69cb765ce1a9b
|
|
| MD5 |
aaeeb4a0574690c4252c11a936bba4b3
|
|
| BLAKE2b-256 |
b889d6d9334610bb9702a4e15bfbc32285c38abce65ee9272d4b56e7af0df23c
|