loading…
Search for a command to run...
loading…
Provides AI agents with tools for paper trading stocks, options, ETFs, and bonds, including advanced options strategies and risk analysis, using real market dat
Provides AI agents with tools for paper trading stocks, options, ETFs, and bonds, including advanced options strategies and risk analysis, using real market data without financial risk.
A comprehensive paper trading simulator with dual interfaces: REST API (FastAPI) and AI agent tools (MCP). Designed for algorithmic trading development, strategy backtesting, options trading simulation, and training AI agents in realistic market environments without financial risk.
🎉 PRODUCTION READY QUALITY - Successfully implemented and deployed dual-server architecture:
/docsBefore you begin, ensure you have the following installed:
# 1. Clone the repository
git clone https://github.com/yourusername/open-paper-trading-mcp.git
cd open-paper-trading-mcp
# 2. Start everything with Docker
docker-compose up --build
# 3. Services are now available at:
# - Frontend & API: http://localhost:2080/
# - MCP Server: http://localhost:2081/
# - API Docs: http://localhost:2080/docs
REST Client AI Agent
| |
v v
FastAPI Server MCP Server
(Port 2080) (Port 2081)
| |
+------------------+
|
v
TradingService
|
+---------+---------+
| |
v v
PostgreSQL DB Robinhood API
(Trading State) (Market Data)
Split Architecture Benefits:
get_async_session() pattern across entire codebaseget_async_session() dependency injection, never AsyncSessionLocal() directlyside_effect for async generators ensures reliable tests@pytest.mark.robinhood for real-world validation# Install uv package manager
curl -LsSf https://astral.sh/uv/install.sh | sh
# Create virtual environment and install dependencies
uv venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
uv pip sync pyproject.toml
# Set up PostgreSQL and update .env
cp .env.example .env
# Edit DATABASE_URL in .env
# Run the application
uv run python app/main.py
The application is configured using environment variables. Copy the .env.example file to .env and update the following variables:
DATABASE_URL: The connection string for your PostgreSQL database.ROBINHOOD_USERNAME: Your Robinhood username (for live market data).ROBINHOOD_PASSWORD: Your Robinhood password.QUOTE_ADAPTER_TYPE: The quote adapter to use (test or robinhood).# Format code
python scripts/dev.py format
# Run linting
python scripts/dev.py lint
# Type checking
python scripts/dev.py typecheck
# Run all tests
python scripts/dev.py test
# Run all checks
python scripts/dev.py check
Always use consistent database session patterns:
# ✅ CORRECT - Use get_async_session()
from app.storage.database import get_async_session
async def database_operation():
async for db in get_async_session():
result = await db.execute(select(Model))
return result.scalars().all()
# ❌ INCORRECT - Never use AsyncSessionLocal() directly
from app.storage.database import AsyncSessionLocal
async with AsyncSessionLocal() as db: # Breaks testing!
pass
Testing database code:
from unittest.mock import patch
@patch('app.storage.database.get_async_session')
async def test_function(mock_get_session, test_session):
async def mock_generator():
yield test_session
mock_get_session.return_value = mock_generator()
# Your test code here
result = await database_operation()
assert result is not None
This ensures consistent behavior between production and testing environments.
Critical for async test stability:
# tests/conftest.py - Create fresh engines per test
@pytest_asyncio.fixture(scope="function")
async def db_session() -> AsyncGenerator[AsyncSession, None]:
# Create engine in current event loop (critical for AsyncIO compatibility)
test_engine = create_async_engine(
database_url,
echo=False,
future=True,
pool_pre_ping=True, # Verify connections before use
pool_recycle=300 # Recycle connections every 5 minutes
)
test_session_factory = async_sessionmaker(
bind=test_engine,
class_=AsyncSession,
expire_on_commit=False
)
try:
async with test_session_factory() as session:
yield session
finally:
await test_engine.dispose() # Critical for preventing leaks
1. Missing Await Keywords
# ❌ WRONG - Async method without await
result = adapter.get_account_ids() # Returns coroutine!
assert len(result) == 3 # TypeError: object of type 'coroutine' has no len()
# ✅ CORRECT - Always await async methods
result = await adapter.get_account_ids()
assert len(result) == 3
2. DateTime Timezone Issues
# ❌ WRONG - Mixed timezone awareness
from datetime import datetime
created_at = datetime.now(timezone.utc) # timezone-aware
updated_at = datetime.now() # timezone-naive
# ✅ CORRECT - Consistent timezone handling
from datetime import datetime, timezone
created_at = datetime.now(timezone.utc)
updated_at = datetime.now(timezone.utc)
3. Database Session Mocking Pattern
# ✅ CORRECT - Proper async session mocking
async def test_database_operation(self, db_session: AsyncSession):
adapter = DatabaseAccountAdapter()
with patch('app.adapters.accounts.get_async_session') as mock_get_session:
async def mock_session_generator():
yield db_session
mock_get_session.side_effect = lambda: mock_session_generator()
# Test database operations with real session
result = await adapter.get_account("test-id")
assert result is not None
@pytest.mark.robinhood markerThe test suite includes integration tests that make live, read-only calls to the Robinhood API:
# Run all tests including live Robinhood calls
uv run pytest
# Exclude live Robinhood API tests (faster, no external dependencies)
uv run pytest -m "not robinhood"
# Run only Robinhood integration tests
uv run pytest -m "robinhood"
Robinhood Test Features:
@pytest.mark.slowtrading_service_robinhood fixtureRun in your terminal:
claude mcp add open-paper-trading-mcp -- npx Security
Low riskAutomated heuristic from public metadata — not a security guarantee.