Browse documentation
Extending BrokerBridge with plugins
This page is for developers writing Python to add a data source. If you just want to trade, see Help instead. AI models are not pluggable today. Pick your model and pay with AI credits, as described in AI Providers. Retail accounts connect Interactive Brokers for market data; linking your own third-party data subscription (Unusual Whales, EODHD, or similar) is not available today.
Add a custom data source by implementing one abstract base class. The plugin can supply ranked signals to the research pipeline. It does not gain model selection, approval, or broker authority.
Architecture overview
DataSourcePlugin
Provides trading signals. The pipeline calls fetch_signals() during each scan interval to get ranked signals.
Examples: IBKR market data, custom scanner
DataSourcePlugin supports three auth modes: api_key, oauth, and connection. These credentials are for your own third-party data source, never for an AI provider.
DataSourcePlugin interface
class DataSourcePlugin(ABC):
name: str # Unique plugin identifier
display_name: str # Human-readable name
auth_type: AuthType # "api_key" | "oauth" | "connection"
required: bool = False # System won't start without it if True
async def validate_credentials(self) -> bool: ...
async def fetch_signals(self, symbols: list[str]) -> list[Signal]: ...
async def health_check(self) -> PluginStatus: ...Tutorial: custom data source
from datetime import datetime, timezone
import httpx
from brokerbridge.plugins.base import DataSourcePlugin
from brokerbridge.plugins.types import AuthType, PluginStatus, Signal
class MyScannerPlugin(DataSourcePlugin):
name = "my_scanner"
display_name = "My Custom Scanner"
auth_type: AuthType = "api_key"
def __init__(self, api_key: str):
self._api_key = api_key
async def validate_credentials(self) -> bool:
async with httpx.AsyncClient() as client:
resp = await client.get(
"https://api.example.com/ping",
headers={"Authorization": f"Bearer {self._api_key}"},
)
return resp.status_code == 200
async def fetch_signals(self, symbols: list[str]) -> list[Signal]:
async with httpx.AsyncClient() as client:
resp = await client.post(
"https://api.example.com/scan",
headers={"Authorization": f"Bearer {self._api_key}"},
json={"symbols": symbols},
)
resp.raise_for_status()
data = resp.json()
signals = []
for item in data.get("signals", []):
signals.append(Signal(
symbol=item["symbol"],
direction=item["direction"],
score=item["score"],
timestamp=datetime.now(timezone.utc),
source=self.name,
))
return sorted(signals, key=lambda s: s.score, reverse=True)
async def health_check(self) -> PluginStatus:
try:
valid = await self.validate_credentials()
return PluginStatus(connected=valid)
except Exception as e:
return PluginStatus(connected=False, message=str(e))Available plugins
| Plugin | Type | Status |
|---|---|---|
| IBKR Market Data | DataSource | Available |