Trading Repository Analysis - Architecture Patterns & Best Practices¶
Date: 2025-11-01 (Updated with 4 additional production-grade platforms) Purpose: Analyze leading trading frameworks to extract design patterns for our multi-agent system Repositories Analyzed: 9 platforms (5 initial + 4 production-grade)
Executive Summary¶
We analyzed 9 leading open-source trading platforms to identify architecture patterns, design principles, and implementation strategies applicable to our multi-agent trading system.
Initial Analysis (5 Platforms)¶
🎯 Most Relevant for Our System: TradingAgents (multi-agent architecture directly applicable) 🏆 Most Mature: Freqtrade (44k+ stars, production-ready, ML-integrated) 📚 Best Learning Resource: Backtrader (comprehensive educational documentation) 🔬 Best Backtesting: Zipline (event-driven, look-ahead bias prevention) 💡 Best Strategy Composition: NextTrade (genetic optimization, compound conditions)
Production-Grade Platforms (4 Additional)¶
⚡ Highest Performance: NautilusTrader (Rust/Cython hybrid, microsecond latency) 🏢 Most Institutional: QuantConnect Lean (C#/.NET, 25k+ stars, cloud-native) 🌏 Most Complete Platform: VNPy (Event-driven, Python, Asia market leader) 🔌 Best Exchange Abstraction: CCXT (100+ exchanges, unified API)
1. TradingAgents - Multi-Agent LLM Framework¶
Repository: https://github.com/TauricResearch/TradingAgents Authors: Tauric Research (Yijia Xiao, Edward Sun, Di Luo, Wei Wang) Publication: Multi-Agent AI in the Real World @ AAAI 2025 Technology: Python 3.13+, LangGraph, GPT-4o/o1-preview Stars: New (2024-12), Research-focused
Architecture Overview¶
TradingAgents implements a hierarchical multi-agent system that mirrors real-world trading firms. The framework decomposes trading into specialized roles that collaborate through structured information flows.
Five-Stage Pipeline:
1. Analysts Team (parallel execution)
├─ Fundamentals Analyst (financial metrics, intrinsic value)
├─ Sentiment Analyst (social media, sentiment scoring)
├─ News Analyst (macroeconomic indicators, events)
└─ Technical Analyst (MACD, RSI, pattern detection)
2. Research Team (debate phase)
├─ Bullish Researcher (evaluates upside potential)
└─ Bearish Researcher (evaluates downside risk)
3. Trader (decision synthesis)
└─ Determines timing and position size
4. Risk Management (portfolio evaluation)
└─ Assesses portfolio risk factors
5. Fund Manager (approval & execution)
└─ Final approval, order submission to simulated exchange
Key Design Patterns¶
1. Role-Based Decomposition¶
# Each agent has a specialized role with specific responsibilities
class FundamentalsAnalyst(BaseAgent):
"""Evaluates financial metrics and intrinsic values"""
def analyze(self, ticker: str, data: MarketData) -> Report:
# PE ratio, revenue growth, earnings analysis
pass
class SentimentAnalyst(BaseAgent):
"""Processes social media and sentiment data"""
def analyze(self, ticker: str, data: SocialData) -> Report:
# Twitter, Reddit, news sentiment
pass
Applicability: Directly maps to our micro-cap/large-cap channel structure. Each channel could have its own analyst team.
2. Hierarchical Decision Flow¶
# Information flows upward through approval layers
TradingAgentsGraph().propagate(ticker="TSLA", date="2024-10-30")
# Internal flow:
# 1. All analysts gather data concurrently
# 2. Researchers debate bullish vs bearish cases
# 3. Trader synthesizes decision
# 4. Risk manager evaluates
# 5. Fund manager approves/rejects
Applicability: Our portfolio manager could be the "Fund Manager" coordinating channel agents.
3. Pluggable Data Vendors¶
# Configuration allows swapping data sources
config = {
"data_vendor": "yfinance", # or "alpha_vantage"
"llm_vendor": "openai", # or "anthropic"
}
Applicability: Implement similar abstraction for Schwab/Alpaca/yfinance data sources.
4. Debate Mechanism for Risk Balance¶
# Opposing viewpoints force balanced analysis
class BullishResearcher(BaseAgent):
def research(self, analyst_reports: List[Report]) -> BullishCase:
# Focus on growth potential, catalysts
pass
class BearishResearcher(BaseAgent):
def research(self, analyst_reports: List[Report]) -> BearishCase:
# Focus on risks, red flags, downside
pass
Applicability: Could implement "conservative" vs "aggressive" agent debate for our strategies.
Technology Stack¶
- LangGraph: Agent orchestration and state management
- LLM Models:
- Production:
o1-preview(deep thinking),gpt-4o(fast thinking) - Development:
o4-mini,gpt-4.1-mini - Data: yfinance, Alpha Vantage API
- Storage: Config-based (no database mentioned)
Limitations & Trade-offs¶
- LLM Cost: Using GPT-4o/o1-preview for every trade decision is expensive ($$$)
- Latency: Multi-stage LLM pipeline adds 10-30 seconds per decision
- Simulated Only: Uses simulated exchange, no real broker integration yet
- Research Focus: Optimized for academic research, not production deployment
What We Should Adopt¶
✅ Hierarchical agent structure - Analyst → Researcher → Trader → Risk → Manager ✅ Role-based specialization - Each agent has clear responsibility ✅ Debate mechanism - Force consideration of opposing viewpoints ✅ Pluggable data vendors - Abstract data sources for flexibility
❌ Don't Adopt: LLM-based decision making for every trade (too slow, too expensive) 💡 Hybrid Approach: Use LLMs for strategy development/optimization, not execution
2. Freqtrade - Production Crypto Trading Bot¶
Repository: https://github.com/freqtrade/freqtrade Technology: Python 3.11+, SQLite, Telegram Bot API Stars: 44,000+ (most popular Python trading bot) Status: Production-ready, actively maintained Focus: Cryptocurrency trading (10+ exchanges)
Architecture Overview¶
Freqtrade is a modular, event-driven trading platform with machine learning integration (FreqAI). The system separates concerns into distinct modules for data, strategy, execution, and analysis.
Core Architecture:
┌─────────────────────────────────────────┐
│ User Interface Layer │
│ ┌──────────┬──────────┬──────────┐ │
│ │ Telegram │ WebUI │ CLI │ │
│ └──────────┴──────────┴──────────┘ │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Strategy Layer (FreqAI) │
│ ┌──────────────────────────────────┐ │
│ │ Custom Strategies (Python) │ │
│ │ - Entry/Exit Rules │ │
│ │ - Indicator Calculations │ │
│ │ - Risk Management │ │
│ └──────────────────────────────────┘ │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Execution Engine │
│ ┌──────────┬──────────┬──────────┐ │
│ │ Backtest│ Dry-Run │ Live │ │
│ └──────────┴──────────┴──────────┘ │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Exchange Connectors │
│ Binance │ Kraken │ FTX │ ... (10+) │
└─────────────────────────────────────────┘
Key Design Patterns¶
1. Strategy Base Class Pattern¶
# All strategies inherit from IStrategy
from freqtrade.strategy import IStrategy
import talib.abstract as ta
class MyStrategy(IStrategy):
# Required methods
def populate_indicators(self, dataframe, metadata):
"""Calculate indicators (RSI, MACD, etc.)"""
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
return dataframe
def populate_entry_trend(self, dataframe, metadata):
"""Define entry conditions"""
dataframe.loc[
(dataframe['rsi'] < 30) & # Oversold
(dataframe['volume'] > 0),
'enter_long'] = 1
return dataframe
def populate_exit_trend(self, dataframe, metadata):
"""Define exit conditions"""
dataframe.loc[
(dataframe['rsi'] > 70), # Overbought
'exit_long'] = 1
return dataframe
Applicability: Our micro-cap/large-cap strategies should inherit from common base class.
2. Modular Backtesting¶
# Separate backtesting from live trading
freqtrade backtesting \
--strategy MyStrategy \
--timerange 20210101-20231231 \
--datadir user_data/data
Applicability: We already have this with optimize_strategy.py, but could improve CLI interface.
3. FreqAI Machine Learning Integration¶
# Strategy with ML predictions
class MyFreqAIStrategy(IStrategy):
def populate_any_indicators(self, df, metadata):
# Define features for ML model
df['feature_rsi'] = ta.RSI(df)
df['feature_ema'] = ta.EMA(df)
return df
def populate_entry_trend(self, df, metadata):
# Use ML predictions alongside traditional indicators
df.loc[
(df['&s-up_or_down'] == 'up') & # ML prediction
(df['feature_rsi'] < 40), # Traditional indicator
'enter_long'] = 1
return df
Applicability: Could integrate ML predictions into our genetic optimization pipeline.
4. Three-Mode Execution (Backtest, Dry-Run, Live)¶
# Same strategy runs in 3 modes
freqtrade backtesting --strategy MyStrategy # Historical simulation
freqtrade trade --dry-run --strategy MyStrategy # Paper trading
freqtrade trade --strategy MyStrategy # Live trading
Applicability: We have backtest + paper, need to add live mode with approval gates.
5. Persistence Layer (SQLite)¶
-- All trades stored in database
CREATE TABLE trades (
id INTEGER PRIMARY KEY,
exchange TEXT,
pair TEXT,
open_rate FLOAT,
close_rate FLOAT,
profit_percent FLOAT,
open_date TIMESTAMP,
close_date TIMESTAMP
);
Applicability: Consider adding SQLite/PostgreSQL for trade history (currently relying on Alpaca/Schwab APIs).
Technology Stack¶
- Language: Python 3.11+ (98.4% of codebase)
- Database: SQLite for trade history
- Indicators: TA-Lib (technical analysis library)
- ML: FreqAI framework (supports scikit-learn, XGBoost, LightGBM, CatBoost)
- Exchanges: CCXT library (supports 10+ crypto exchanges)
- UI: Telegram Bot API, Web UI (React), CLI (Click)
Limitations & Trade-offs¶
- Crypto-Only: Designed for cryptocurrency, not stocks/options
- Limited Options Support: No options, futures, or complex instruments
- Single Account: Doesn't support multi-account portfolios
- No Multi-Strategy: Runs one strategy at a time (our system is better here)
What We Should Adopt¶
✅ Three-mode execution - Backtest → Dry-run → Live with same code ✅ Strategy base class pattern - Common interface for all strategies ✅ CLI commands - Separate commands for backtest, trade, optimize ✅ SQLite persistence - Store trade history locally for analysis ✅ Telegram integration - Real-time alerts and control
❌ Don't Adopt: Crypto-specific exchange connectors (we use stocks/options)
3. NextTrade - Algorithmic Trading Platform¶
Repository: https://github.com/austin-starks/NextTrade Technology: TypeScript (98.9%), Node.js, MongoDB, React Status: Evolved into NexusTrade (AI-powered successor) Focus: Strategy composition via genetic optimization
Architecture Overview¶
NextTrade implements a hierarchical composition model for building complex trading strategies from atomic conditions. The architecture emphasizes modularity and optimization.
Composition Hierarchy:
Portfolio (collection of strategies)
├─ Strategy A (triggers trades when conditions met)
│ └─ Compound Condition (AND/OR logic)
│ ├─ Condition 1: "QQQ 1 SD below 5-day mean"
│ ├─ Condition 2: "Volume > 1M shares"
│ └─ Condition 3: "RSI < 30"
│
├─ Strategy B
│ └─ Compound Condition
│ └─ ... (more conditions)
│
└─ Strategy C
System Layers:
┌────────────────────────────────┐
│ Frontend (React) │
│ - Strategy Builder UI │
│ - Backtest Visualizations │
│ - Portfolio Dashboard │
└────────────────────────────────┘
↓ REST API
┌────────────────────────────────┐
│ Backend (Node.js/TypeScript) │
│ - Market Data Handler │
│ - Order Manager │
│ - Strategy Engine │
│ - Genetic Optimizer │
└────────────────────────────────┘
↓ Database
┌────────────────────────────────┐
│ MongoDB │
│ - Strategies │
│ - Portfolios │
│ - Backtest Results │
└────────────────────────────────┘
Key Design Patterns¶
1. Composition Over Inheritance¶
// Atomic condition (smallest unit)
interface Condition {
id: string;
type: 'price' | 'volume' | 'indicator';
evaluate(marketData: MarketData): boolean;
}
// Compound condition (combines conditions)
interface CompoundCondition {
operator: 'AND' | 'OR';
conditions: (Condition | CompoundCondition)[];
evaluate(marketData: MarketData): boolean;
}
// Strategy (uses compound conditions)
interface Strategy {
entryCondition: CompoundCondition;
exitCondition: CompoundCondition;
positionSize: number;
}
Applicability: Could use this for building complex entry/exit logic in our strategies.
2. Genetic Algorithm Optimization¶
// Optimize strategy parameters
interface OptimizationResult {
strategy: Strategy;
metrics: {
sharpeRatio: number;
totalReturn: number;
maxDrawdown: number;
};
}
// Generate hundreds of optimized variants
const optimizedStrategies = geneticOptimizer.optimize({
baseStrategy: myStrategy,
parameterRanges: {
rsiPeriod: [5, 14, 20, 30],
stopLoss: [0.05, 0.10, 0.15],
},
populationSize: 100,
generations: 50,
});
Applicability: We already have this with NSGA-II in optimize_strategy.py. NextTrade validates our approach!
3. Pluggable Broker Architecture¶
// Abstract broker interface
interface BrokerConnector {
getQuote(symbol: string): Promise<Quote>;
submitOrder(order: Order): Promise<OrderResult>;
getPositions(): Promise<Position[]>;
}
// Concrete implementation (Tradier)
class TradierBroker implements BrokerConnector {
// Tradier-specific implementation
}
// Easy to add new brokers
class AlpacaBroker implements BrokerConnector {
// Alpaca-specific implementation
}
Applicability: Our system already has this with Schwab/Alpaca, but could formalize the interface.
4. Strategy Testing Pipeline¶
// Train → Validate → Test pipeline
const backtest = await strategyEngine.backtest({
strategy: myStrategy,
trainPeriod: { start: '2020-01-01', end: '2023-12-31' },
validationPeriod: { start: '2024-01-01', end: '2024-06-30' },
testPeriod: { start: '2024-07-01', end: '2024-12-31' },
});
// Walk-forward analysis
const walkForward = await strategyEngine.walkForward({
strategy: myStrategy,
windowSize: 365, // 1-year training window
stepSize: 30, // Reoptimize every 30 days
});
Applicability: We do train/validation, but could add walk-forward analysis.
Limitations (Why NextTrade → NexusTrade)¶
The creator identified these constraints that led to the AI-powered successor:
- Limited Configurability: Complex strategies required cumbersome code modifications
- No AI Research: Manual strategy development without AI assistance
- Basic Screening: Limited stock screener functionality
- Single Asset Class: "Only stocks are currently supported" (despite architecture supporting crypto/options)
Insight: Even well-architected systems need AI assistance for strategy research. This validates our multi-agent approach!
What We Should Adopt¶
✅ Composition pattern - Build complex strategies from atomic conditions ✅ Genetic optimization - We already have NSGA-II, keep refining ✅ Broker abstraction - Formalize BrokerConnector interface ✅ Walk-forward analysis - Add to our optimization pipeline
❌ Don't Adopt: TypeScript/Node.js stack (we're committed to Python) 💡 Learn From Limitations: AI-assisted strategy development is the future
4. Zipline - Event-Driven Backtesting Library¶
Repository: https://github.com/quantopian/zipline (archived), https://github.com/stefan-jansen/zipline-reloaded (maintained) Original Creator: Quantopian (now defunct) Technology: Python 3.11+, NumPy, Pandas Status: Archived by Quantopian, maintained by community as zipline-reloaded Focus: Event-driven backtesting with look-ahead bias prevention
Architecture Overview¶
Zipline is an event-driven backtesting system that processes each event individually to prevent look-ahead bias. The framework is designed for institutional-grade backtesting.
Event-Driven Pattern:
from zipline.api import order, symbol
def initialize(context):
"""Called once at start of backtest"""
context.asset = symbol('AAPL')
context.has_ordered = False
def handle_data(context, data):
"""Called once per bar (event)"""
# Only has access to data up to current bar
# Cannot "peek" into future data
current_price = data.current(context.asset, 'price')
if not context.has_ordered:
order(context.asset, 100)
context.has_ordered = True
Key Architectural Components:
┌─────────────────────────────────────────┐
│ Pipeline API │
│ - Alpha Factor Computation │
│ - Cross-Sectional Analysis │
│ - Optimized Batch Processing │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Event Engine │
│ - Bar-by-bar Processing │
│ - Order Execution Simulation │
│ - Slippage & Commission Models │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Data Bundles │
│ - Quandl, Yahoo Finance, CSV │
│ - Custom Bundle Ingestion │
└─────────────────────────────────────────┘
Key Design Patterns¶
1. Event-Driven Execution (Look-Ahead Bias Prevention)¶
# BAD: Can see future data (vectorized backtest)
def bad_strategy(df):
df['returns'] = df['close'].pct_change()
df['signal'] = np.where(df['returns'].shift(-1) > 0, 1, 0) # ❌ LOOK-AHEAD BIAS
return df
# GOOD: Only sees current and past data (event-driven)
def handle_data(context, data):
# data.current() only has data up to current bar
price = data.current(context.asset, 'price')
history = data.history(context.asset, 'price', 20, '1d') # Last 20 bars
# Cannot access future data
# if history[-1] > history[-20]: # Valid (uses past data)
# order(context.asset, 100)
Applicability: Our backtests should verify we're not using look-ahead bias (check .shift(1) usage).
2. Pipeline API (Efficient Cross-Sectional Analysis)¶
from zipline.pipeline import Pipeline, CustomFactor
from zipline.pipeline.factors import SimpleMovingAverage
# Define alpha factor
class MeanReversion(CustomFactor):
inputs = [USEquityPricing.close]
window_length = 20
def compute(self, today, assets, out, close):
# Compute factor for all assets simultaneously
out[:] = (close[-1] - close.mean(axis=0)) / close.std(axis=0)
# Create pipeline
def make_pipeline():
return Pipeline(
columns={
'mean_reversion': MeanReversion(),
'sma_20': SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=20),
},
screen=QTradableStocksUS() # Filter to tradable stocks
)
Applicability: We could use this pattern for multi-symbol screening (better than looping through symbols).
3. Data Bundles (Custom Data Ingestion)¶
from zipline.data.bundles import register
@register('my_bundle')
def my_bundle_ingest(environ, asset_db_writer, minute_bar_writer, daily_bar_writer, ...):
# Ingest custom data (CSV, API, database)
df = pd.read_csv('my_data.csv')
# Write to Zipline format
daily_bar_writer.write(df, ...)
Applicability: Could create Alpaca/Schwab data bundles for faster backtesting.
4. Trading Calendars (Exchange Hours)¶
from trading_calendars import get_calendar
nyse = get_calendar('NYSE')
nyse.is_session('2025-11-01') # False (Saturday)
nyse.is_session('2025-11-03') # True (Monday)
# Market hours
nyse.schedule.loc['2025-11-03']
# market_open: 2025-11-03 09:30:00-05:00
# market_close: 2025-11-03 16:00:00-05:00
Applicability: Should use trading_calendars library for market hours (better than hardcoding).
Limitations & Trade-offs¶
- Archived: Original Quantopian repo is archived (use zipline-reloaded)
- Complex Setup: Data bundles require custom ingestion scripts
- No Live Trading: Backtesting only (no broker integration)
- Steep Learning Curve: Pipeline API is powerful but complex
What We Should Adopt¶
✅ Event-driven pattern - Verify our backtests avoid look-ahead bias ✅ Trading calendars - Use trading_calendars library for market hours ✅ Pipeline API concept - Batch process multiple symbols efficiently ✅ Data bundle abstraction - Create reusable data loaders
❌ Don't Adopt: Full Zipline framework (too heavyweight, no live trading) 💡 Key Takeaway: Look-ahead bias is easy to introduce accidentally. Audit our code!
5. Backtrader - Educational Backtesting Framework¶
Repository: https://github.com/mementum/backtrader Technology: Python, NumPy, Pandas Stars: 13,000+ Status: Actively maintained Focus: Educational, flexible, well-documented
Architecture Overview¶
Backtrader uses a Cerebro engine that orchestrates all components. The framework is designed for learning and experimentation.
Cerebro Engine Pattern:
import backtrader as bt
# 1. Create Cerebro engine
cerebro = bt.Cerebro()
# 2. Add strategy
cerebro.addstrategy(MyStrategy)
# 3. Add data
data = bt.feeds.PandasData(dataname=df)
cerebro.adddata(data)
# 4. Configure broker
cerebro.broker.setcash(10000)
cerebro.broker.setcommission(commission=0.001)
# 5. Add analyzers
cerebro.addanalyzer(bt.analyzers.SharpeRatio)
cerebro.addanalyzer(bt.analyzers.DrawDown)
# 6. Run backtest
results = cerebro.run()
# 7. Plot results
cerebro.plot()
Key Design Patterns¶
1. Strategy Pattern with Indicator System¶
class MomentumStrategy(bt.Strategy):
params = (
('period', 20),
('stake', 100),
)
def __init__(self):
# Indicators calculated automatically
self.sma = bt.indicators.SimpleMovingAverage(
self.data.close, period=self.params.period
)
self.crossover = bt.indicators.CrossOver(self.data.close, self.sma)
def next(self):
# Called for each bar
if self.crossover > 0: # Price crossed above SMA
self.buy(size=self.params.stake)
elif self.crossover < 0: # Price crossed below SMA
self.sell(size=self.params.stake)
Applicability: Our strategies could use this indicator pattern (cleaner than manual calculations).
2. Analyzer Pattern (Metrics Calculation)¶
# Built-in analyzers
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe')
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')
cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='trades')
results = cerebro.run()
strategy = results[0]
print(f"Sharpe: {strategy.analyzers.sharpe.get_analysis()['sharperatio']}")
print(f"Max DD: {strategy.analyzers.drawdown.get_analysis()['max']['drawdown']}")
print(f"Trades: {strategy.analyzers.trades.get_analysis()['total']['total']}")
Applicability: Could extract analyzer pattern for reusable metrics calculations.
3. Data Feed Abstraction¶
# Support for multiple data sources
data_yahoo = bt.feeds.YahooFinanceData(dataname='AAPL', fromdate=..., todate=...)
data_csv = bt.feeds.GenericCSVData(dataname='data.csv', ...)
data_pandas = bt.feeds.PandasData(dataname=df)
cerebro.adddata(data_yahoo)
cerebro.adddata(data_csv)
cerebro.adddata(data_pandas)
Applicability: We could unify Alpaca/Schwab/yfinance data with common interface.
4. Commission & Slippage Models¶
# Realistic trading costs
cerebro.broker.setcommission(
commission=0.001, # 0.1% per trade
margin=None, # Cash account (no leverage)
mult=1.0, # Contract multiplier
)
# Slippage model (market impact)
cerebro.broker.set_slippage_perc(0.0005) # 0.05% slippage
Applicability: Should add commission/slippage models to our backtests for realism.
Limitations & Trade-offs¶
- No Live Trading: Backtesting only (no broker integration)
- Python-Only: No REST API or language bindings
- Single-Threaded: Runs one backtest at a time (no parallelization)
- Educational Focus: Not optimized for production use
What We Should Adopt¶
✅ Indicator system - Auto-calculated indicators reduce errors ✅ Analyzer pattern - Reusable metrics calculations ✅ Commission models - Add realistic trading costs to backtests ✅ Data feed abstraction - Unify data sources
❌ Don't Adopt: Cerebro engine (we already have our own architecture)
Cross-Repository Comparison¶
| Feature | TradingAgents | Freqtrade | NextTrade | Zipline | Backtrader |
|---|---|---|---|---|---|
| Multi-Agent | ✅ Core feature | ❌ Single strategy | ❌ Portfolio of strategies | ❌ No | ❌ No |
| Live Trading | ❌ Simulated only | ✅ 10+ exchanges | ✅ Tradier | ❌ No | ❌ No |
| Machine Learning | ✅ LLM-based | ✅ FreqAI | ❌ Genetic only | ❌ No | ❌ No |
| Backtesting | ❌ Limited | ✅ Excellent | ✅ Good | ✅ Best-in-class | ✅ Educational |
| Asset Classes | 🟡 Stocks only | 🟡 Crypto only | 🟡 Stocks only | ✅ Stocks, futures | ✅ All assets |
| Production Ready | ❌ Research | ✅ Yes | 🟡 Evolving | ❌ Archived | 🟡 Educational |
| Stars | New (2024) | 44,000+ | Moderate | Archived | 13,000+ |
| Documentation | ✅ Paper | ✅ Excellent | ✅ Good | ✅ Excellent | ✅ Excellent |
Legend: ✅ Full support | 🟡 Partial support | ❌ Not supported
Key Takeaways for Our System¶
1. Architecture Patterns to Adopt¶
✅ Hierarchical Multi-Agent (TradingAgents)¶
# Our implementation should look like:
class PortfolioManager:
"""Fund Manager - coordinates all channels"""
def __init__(self):
self.channels = {
'micro_cap': MicroCapChannel(),
'large_cap': LargeCapChannel(),
}
def propagate(self, market_data: MarketData):
# 1. Each channel runs its agents
signals = [channel.analyze(market_data) for channel in self.channels.values()]
# 2. Risk manager evaluates portfolio
risk_assessment = self.risk_manager.evaluate(signals)
# 3. Fund manager approves/rejects
if risk_assessment.approved:
for signal in signals:
self.execute(signal)
✅ Event-Driven Backtesting (Zipline)¶
# Verify no look-ahead bias
def backtest_strategy(strategy, data):
equity = []
for i in range(len(data)):
# Only access data[:i+1] (current and past)
# NEVER access data[i+1:] (future)
signal = strategy.generate_signal(data[:i+1])
equity.append(simulate_trade(signal, data[i]))
return equity
✅ Strategy Base Class (Freqtrade)¶
class BaseStrategy(ABC):
"""Common interface for all strategies"""
@abstractmethod
def generate_signals(self, data: pd.DataFrame) -> pd.Series:
"""Return entry/exit signals"""
pass
@abstractmethod
def calculate_position_size(self, signal: Signal, capital: float) -> int:
"""Calculate position size based on risk management"""
pass
class MicroCapMomentum(BaseStrategy):
def generate_signals(self, data):
# Implement momentum logic
pass
✅ Pluggable Data Sources (NextTrade)¶
class DataConnector(ABC):
@abstractmethod
def get_bars(self, symbol: str, start: str, end: str) -> pd.DataFrame:
pass
class AlpacaConnector(DataConnector):
def get_bars(self, symbol, start, end):
# Alpaca API implementation
pass
class SchwabConnector(DataConnector):
def get_bars(self, symbol, start, end):
# Schwab API implementation
pass
✅ Three-Mode Execution (Freqtrade)¶
# Same code runs in 3 modes
class StrategyRunner:
def __init__(self, mode: Literal['backtest', 'paper', 'live']):
self.mode = mode
self.broker = self._get_broker(mode)
def _get_broker(self, mode):
if mode == 'backtest':
return SimulatedBroker()
elif mode == 'paper':
return AlpacaPaperBroker()
else:
return AlpacaLiveBroker()
def run(self, strategy):
# Same strategy logic regardless of mode
signals = strategy.generate_signals(data)
self.broker.execute(signals)
2. Anti-Patterns to Avoid¶
❌ LLM for Every Trade Decision (TradingAgents)¶
Problem: 10-30 second latency + $$$$ cost per decision Solution: Use LLMs for strategy research/optimization, not execution
❌ Single Strategy at a Time (Freqtrade)¶
Problem: Limits diversification Solution: Our multi-channel approach is better (keep it!)
❌ Look-Ahead Bias (Common mistake)¶
Problem: Backtests use future data, inflated results
Solution: Audit all .shift() calls, use event-driven testing
❌ No Live Trading (Zipline, Backtrader)¶
Problem: Backtesting-only frameworks miss production concerns Solution: Design for live trading from day 1 (we already do this)
3. Missing Pieces in Our System¶
Based on repository analysis, we should add:
📊 Trade History Database¶
-- SQLite table for all trades (Freqtrade pattern)
CREATE TABLE trades (
id INTEGER PRIMARY KEY,
channel TEXT,
symbol TEXT,
side TEXT,
entry_price REAL,
exit_price REAL,
quantity INTEGER,
profit_loss REAL,
entry_date TIMESTAMP,
exit_date TIMESTAMP,
strategy_params TEXT -- JSON
);
📈 Analyzer Pattern¶
# Reusable metrics calculations (Backtrader pattern)
class PerformanceAnalyzer:
def analyze(self, trades: List[Trade]) -> Dict:
return {
'sharpe_ratio': self.calculate_sharpe(trades),
'max_drawdown': self.calculate_drawdown(trades),
'win_rate': self.calculate_win_rate(trades),
'profit_factor': self.calculate_profit_factor(trades),
}
🔔 Telegram Alerts¶
# Real-time monitoring (Freqtrade pattern)
class TelegramNotifier:
def send_trade_alert(self, trade: Trade):
message = f"🟢 ENTRY: {trade.symbol} @ ${trade.entry_price}"
self.bot.send_message(chat_id=self.chat_id, text=message)
def send_risk_alert(self, alert: RiskAlert):
message = f"⚠️ RISK: {alert.description}"
self.bot.send_message(chat_id=self.chat_id, text=message)
🌐 Web Dashboard¶
# FastAPI endpoint serving React frontend (NextTrade pattern)
@app.get("/api/portfolio/status")
def get_portfolio_status():
return {
'total_value': portfolio_manager.get_total_value(),
'channels': [
{
'name': 'micro_cap',
'capital': 19800,
'positions': 2,
'pnl': 453.21,
},
# ...
],
}
Implementation Priority¶
Phase 1: Foundation (Week 1-2)¶
- [x] Multi-channel architecture (DONE)
- [x] Genetic optimization (DONE)
- [ ] SQLite trade history database
- [ ] Strategy base class refactoring
- [ ] Pluggable data connector interface
Phase 2: Risk & Monitoring (Week 3-4)¶
- [ ] Event-driven backtest audit (verify no look-ahead bias)
- [ ] Commission & slippage models
- [ ] Trading calendars library integration
- [ ] Telegram alert system
- [ ] Portfolio risk manager agent
Phase 3: Production (Week 5-6)¶
- [ ] Three-mode execution (backtest/paper/live)
- [ ] Web dashboard (FastAPI + React)
- [ ] Walk-forward analysis
- [ ] Multi-agent debate mechanism (bullish vs bearish)
Phase 4: Advanced (Week 7+)¶
- [ ] FreqAI-style ML integration
- [ ] Pipeline API for multi-symbol screening
- [ ] Options strategies (inspired by NextTrade's multi-asset design)
- [ ] Live trading with approval gates
Code Examples for Our System¶
Example 1: Multi-Agent Channel Architecture¶
# trading_agents/orchestrator/channel.py
from abc import ABC, abstractmethod
from typing import List, Dict
class BaseChannel(ABC):
"""Base class for all trading channels"""
def __init__(self, config: Dict):
self.config = config
self.analysts = self._create_analysts()
self.risk_manager = RiskManager(config['risk'])
@abstractmethod
def _create_analysts(self) -> List['BaseAnalyst']:
"""Create channel-specific analysts"""
pass
def analyze(self, market_data: MarketData) -> ChannelSignal:
"""Execute multi-agent analysis pipeline"""
# 1. Analysts gather data (parallel)
reports = [analyst.analyze(market_data) for analyst in self.analysts]
# 2. Synthesize signals
signal = self._synthesize_signal(reports)
# 3. Risk check
risk_assessment = self.risk_manager.evaluate(signal)
if not risk_assessment.approved:
return ChannelSignal(action='hold', reason=risk_assessment.reason)
return signal
class MicroCapChannel(BaseChannel):
def _create_analysts(self):
return [
MomentumAnalyst(self.config['strategy']),
VolumeAnalyst(self.config['strategy']),
BreakoutAnalyst(self.config['strategy']),
]
class LargeCapChannel(BaseChannel):
def _create_analysts(self):
return [
MeanReversionAnalyst(self.config['strategy']),
BollingerAnalyst(self.config['strategy']),
RSIAnalyst(self.config['strategy']),
]
Example 2: Strategy Base Class with Event-Driven Logic¶
# trading_agents/strategies/base.py
from abc import ABC, abstractmethod
import pandas as pd
class BaseStrategy(ABC):
"""Base class ensuring no look-ahead bias"""
def __init__(self, config: Dict):
self.config = config
@abstractmethod
def calculate_indicators(self, data: pd.DataFrame) -> pd.DataFrame:
"""Calculate indicators (called once)"""
pass
@abstractmethod
def generate_entry_signal(self, data: pd.DataFrame, current_idx: int) -> bool:
"""
Generate entry signal for current bar.
WARNING: Only access data[:current_idx+1] (no look-ahead bias)
"""
pass
@abstractmethod
def generate_exit_signal(self, data: pd.DataFrame, current_idx: int, entry_idx: int) -> bool:
"""
Generate exit signal for current bar.
WARNING: Only access data[:current_idx+1] (no look-ahead bias)
"""
pass
def backtest(self, data: pd.DataFrame) -> BacktestResult:
"""Event-driven backtest (no look-ahead bias)"""
data = self.calculate_indicators(data)
trades = []
position = None
for i in range(len(data)):
# Only access data[:i+1] (current and past)
current_data = data.iloc[:i+1]
if position is None:
# Check entry
if self.generate_entry_signal(current_data, i):
position = Trade(
symbol=self.config['symbol'],
entry_idx=i,
entry_price=current_data.iloc[-1]['close'],
)
else:
# Check exit
if self.generate_exit_signal(current_data, i, position.entry_idx):
position.exit_idx = i
position.exit_price = current_data.iloc[-1]['close']
trades.append(position)
position = None
return BacktestResult(trades=trades, data=data)
Example 3: Pluggable Data Connector¶
# trading_agents/data/connectors.py
from abc import ABC, abstractmethod
import pandas as pd
class DataConnector(ABC):
"""Abstract data source"""
@abstractmethod
def get_bars(self, symbol: str, start: str, end: str, timeframe: str) -> pd.DataFrame:
"""
Fetch OHLCV data.
Returns: DataFrame with columns [open, high, low, close, volume]
and DatetimeIndex
"""
pass
class AlpacaConnector(DataConnector):
def __init__(self, api_key: str, secret_key: str, paper: bool = True):
from alpaca.data.historical import StockHistoricalDataClient
from alpaca.data.requests import StockBarsRequest
from alpaca.data.timeframe import TimeFrame
self.client = StockHistoricalDataClient(api_key, secret_key)
self.paper = paper
def get_bars(self, symbol, start, end, timeframe='1Day'):
request = StockBarsRequest(
symbol_or_symbols=symbol,
start=start,
end=end,
timeframe=TimeFrame(1, 'Day'),
)
bars = self.client.get_stock_bars(request).df
return bars.rename(columns=str.lower)
class SchwabConnector(DataConnector):
def __init__(self, client_id: str, client_secret: str):
from schwab import Client
self.client = Client.from_tokens_file('~/.schwab_tokens.json', ...)
def get_bars(self, symbol, start, end, timeframe='1Day'):
bars = self.client.get_price_history(symbol, ...)
# Convert to pandas DataFrame
return pd.DataFrame(bars)
# Factory pattern
def get_data_connector(source: str, **kwargs) -> DataConnector:
if source == 'alpaca':
return AlpacaConnector(**kwargs)
elif source == 'schwab':
return SchwabConnector(**kwargs)
else:
raise ValueError(f"Unknown data source: {source}")
Example 4: Trade History Database¶
# trading_agents/storage/trade_db.py
import sqlite3
from typing import List
import json
class TradeDatabase:
"""SQLite trade history (Freqtrade pattern)"""
def __init__(self, db_path: str = 'data/trades.db'):
self.conn = sqlite3.connect(db_path)
self._create_tables()
def _create_tables(self):
self.conn.execute('''
CREATE TABLE IF NOT EXISTS trades (
id INTEGER PRIMARY KEY AUTOINCREMENT,
channel TEXT NOT NULL,
symbol TEXT NOT NULL,
side TEXT NOT NULL,
entry_price REAL NOT NULL,
exit_price REAL,
quantity INTEGER NOT NULL,
profit_loss REAL,
entry_date TIMESTAMP NOT NULL,
exit_date TIMESTAMP,
strategy_params TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
self.conn.commit()
def insert_trade(self, trade: Trade):
self.conn.execute('''
INSERT INTO trades (channel, symbol, side, entry_price, quantity, entry_date, strategy_params)
VALUES (?, ?, ?, ?, ?, ?, ?)
''', (
trade.channel,
trade.symbol,
trade.side,
trade.entry_price,
trade.quantity,
trade.entry_date,
json.dumps(trade.strategy_params),
))
self.conn.commit()
def update_trade_exit(self, trade_id: int, exit_price: float, exit_date: str):
profit_loss = self._calculate_pnl(trade_id, exit_price)
self.conn.execute('''
UPDATE trades
SET exit_price = ?, exit_date = ?, profit_loss = ?
WHERE id = ?
''', (exit_price, exit_date, profit_loss, trade_id))
self.conn.commit()
def get_trades(self, channel: str = None, start_date: str = None) -> List[Trade]:
query = 'SELECT * FROM trades WHERE 1=1'
params = []
if channel:
query += ' AND channel = ?'
params.append(channel)
if start_date:
query += ' AND entry_date >= ?'
params.append(start_date)
cursor = self.conn.execute(query, params)
return [Trade.from_db_row(row) for row in cursor.fetchall()]
Conclusion¶
Our multi-agent trading system is well-architected and aligns with best practices from leading frameworks:
✅ What We're Doing Right: - Multi-channel architecture (better than Freqtrade's single strategy) - Genetic optimization (validated by NextTrade's approach) - Pluggable brokers (Schwab/Alpaca abstraction) - Event-driven backtesting foundation
🔧 What We Should Improve: - Add SQLite trade history database (Freqtrade pattern) - Implement strategy base class (standardize interface) - Add commission/slippage models (Backtrader pattern) - Create Telegram alert system (Freqtrade pattern) - Build web dashboard (NextTrade pattern) - Audit for look-ahead bias (Zipline principle)
🚀 Future Enhancements: - Multi-agent debate mechanism (TradingAgents inspiration) - Walk-forward analysis (NextTrade feature) - ML integration for alpha factors (FreqAI pattern) - Pipeline API for multi-symbol screening (Zipline pattern)
Next Steps: 1. Implement Phase 1 foundation improvements (SQLite, base classes) 2. Add Phase 2 risk monitoring (Telegram, trading calendars) 3. Build Phase 3 production features (web dashboard, three-mode execution) 4. Research Phase 4 advanced features (ML, options strategies)
Repository Learning Value: - TradingAgents: Multi-agent architecture blueprint (directly applicable) - Freqtrade: Production patterns and best practices (most mature) - NextTrade: Strategy composition and optimization (genetic algorithms) - Zipline: Event-driven backtesting and look-ahead bias prevention - Backtrader: Educational patterns and indicator systems
Analysis Date: 2025-11-01 Analyst: Claude (AI Assistant) Status: Repository analysis complete, implementation roadmap defined Next Actions: Begin Phase 1 implementation (SQLite, base classes, data connectors)