Skip to content

Commit 0106092

Browse files
Merge pull request #1 from rohitashwachaks/init
Init
2 parents a500e46 + 215f273 commit 0106092

File tree

73 files changed

+2621
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+2621
-2
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,5 @@ cython_debug/
172172

173173
# PyPI configuration file
174174
.pypirc
175+
.idea/
176+
.qodo/

README.md

Lines changed: 172 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,172 @@
1-
# traderplusplus
2-
going wild
1+
# 🚀 Trader++: The Next-Gen Quant Trading Engine
2+
3+
> **Unleash the power of modular, realistic, and extensible portfolio simulation.**
4+
5+
---
6+
7+
## ✨ Why Trader++?
8+
9+
Trader++ isn’t just another backtesting tool. It’s a full-fledged quant trading engine built for:
10+
- **True Portfolio Simulation:** Manage multiple assets, cash, and trades as real portfolios—not just isolated strategies.
11+
- **Plug-and-Play Modularity:** Swap in new strategies, data sources, or risk guardrails with minimal code.
12+
- **Event-Driven Realism:** Simulate trades, slippage, and portfolio changes in a way that mimics real markets.
13+
- **Powerful Guardrails:** Risk management hooks that go beyond stop-losses—unregister assets, enforce capital limits, and more.
14+
- **Transparent & Hackable:** Built for experimentation, learning, and research. Every core component is swappable and inspectable.
15+
16+
**How is it different from Backtrader, Zipline, or QuantConnect?**
17+
- 🧩 **Cleaner separation of concerns:** Market data, strategies, execution, and portfolio logic are fully decoupled.
18+
- 🛡️ **Advanced guardrails:** Custom risk modules, not just basic stop-losses.
19+
- 💡 **Portfolio as a first-class citizen:** Track capital, trades, and metadata in one place.
20+
- 🧪 **Designed for research:** Easy to debug, extend, and run controlled experiments.
21+
- 🌱 **Open, modern, and Pythonic:** No black boxes, no vendor lock-in, and ready for your next big idea.
22+
23+
---
24+
25+
## 🧠 Objective
26+
27+
Empower quants and developers to:
28+
- Cleanly separate market data, strategies, execution logic, and portfolio tracking
29+
- Run realistic, event-driven backtests and simulations
30+
- Plug-and-play both single-asset and multi-asset strategies
31+
32+
Built for robust experimentation and real-world readiness, with proper portfolio management and capital accounting.
33+
34+
---
35+
36+
## 🗺️ System Architecture
37+
38+
```mermaid
39+
flowchart TD
40+
subgraph Data Layer
41+
A[MarketData]
42+
end
43+
subgraph Strategy Layer
44+
B[StrategyBase]
45+
end
46+
subgraph Execution Layer
47+
C[PortfolioExecutor]
48+
D[Guardrails]
49+
end
50+
subgraph Portfolio & Analytics
51+
E[Portfolio]
52+
F[Performance Analytics]
53+
end
54+
A -- Price/Volume Data --> B
55+
B -- Signals --> C
56+
C -- Orders/Trades --> E
57+
C -- Risk Checks --> D
58+
D -- Approve/Block Trades --> C
59+
E -- Holdings/PnL --> F
60+
F -- Reports --> E
61+
```
62+
63+
---
64+
65+
## 🔧 Core Components
66+
67+
| Module | Purpose |
68+
|-------------------|-------------------------------------------------------------------------|
69+
| Portfolio | Tracks assets, cash, trades, and strategy metadata. Self-contained unit. |
70+
| PortfolioExecutor | Orchestrates strategy execution, manages trade logic, evaluates guards. |
71+
| MarketData | Historical price data & sliding window views for strategies. |
72+
| StrategyBase | Interface for strategy design, single/multi-asset support. |
73+
| Backtester | Runs simulations, exports performance reports and logs. |
74+
75+
---
76+
77+
## 💡 Main Features
78+
79+
- 📈 **Backtesting Engine** — Realistic execution, guardrails, cash balance checks
80+
- 🧠 **Pluggable Strategy Interface** — Stateful/stateless signal generation
81+
- 💼 **Portfolio Tracking** — Accurate PnL with trade logs, equity curves
82+
- 🛡️ **Guardrail System** — Risk management hooks (stop-loss, asset unregister)
83+
- 📊 **Performance Reporting** — Sharpe, max drawdown, win rate, CAGR, more
84+
- 🔬 **Benchmark Comparison** — Alpha, beta, vs SPY or other tickers
85+
- 🧪 **Test Strategies** — Debug pipeline (e.g., “buy once on day 1”)
86+
87+
---
88+
89+
## 🚀 Quick Start
90+
91+
```sh
92+
# Install dependencies
93+
pip install -r requirements.txt
94+
95+
# Run a backtest
96+
python run_backtest.py --strategy momentum --tickers AAPL,MSFT --start 2023-01-01 --end 2023-12-31 --plot
97+
98+
# Or use the Streamlit UI
99+
streamlit run streamlit_app.py
100+
```
101+
102+
---
103+
104+
## 🧩 Project Structure
105+
106+
```text
107+
📦traderplusplus
108+
├── contracts
109+
│ ├── asset.py # Asset & CashAsset classes
110+
│ └── portfolio.py # Portfolio definition
111+
├── core
112+
│ ├── backtester.py # Runs simulation
113+
│ ├── executor.py # Executes trades
114+
│ ├── market_data.py # Loads, stores & queries market data
115+
│ ├── data_loader.py # Yahoo/Polygon loaders + caching
116+
│ ├── guardrails # Risk guardrail classes
117+
│ └── visualizer.py # Matplotlib + Plotly charts
118+
├── strategies
119+
│ ├── base.py # StrategyBase + factory
120+
│ └── stock
121+
│ ├── momentum.py # Example strategy
122+
├── analytics
123+
│ └── performance.py # Sharpe, Alpha etc.
124+
├── run_backtest.py # CLI tool
125+
└── streamlit_app.py # UI
126+
```
127+
128+
---
129+
130+
## 🌱 Vision for Future Work
131+
132+
### 🎯 Execution & Simulation
133+
- Live trading interface (Alpaca, IBKR)
134+
- Slippage and commission modeling
135+
- Real-time execution with event-based feed
136+
137+
### 🧠 Strategy Framework
138+
- Portfolio optimization (Risk Parity, Markowitz)
139+
- Signal pipelines (multi-indicator, ML-based)
140+
- RL and LLM-based adaptive strategies
141+
142+
### 📊 Analytics & Visualization
143+
- Interactive dashboard (Streamlit/Plotly)
144+
- Trade replay, diagnostics
145+
- Alpha decomposition, factor attribution
146+
147+
### 🧱 Engine Internals
148+
- Custom logging, debugging, test harness
149+
- Multiprocess strategy evaluation
150+
- Config-driven simulation pipelines
151+
152+
---
153+
154+
## 🙌 Contributing
155+
156+
Pull requests and suggestions are welcome! For major changes, please open an issue first to discuss what you’d like to change.
157+
158+
---
159+
160+
## 📄 License
161+
162+
Distributed under the MIT License.
163+
164+
---
165+
166+
## 📬 Contact
167+
168+
Open an issue or reach out via GitHub for questions and collaboration!
169+
170+
---
171+
172+
Enjoy building and experimenting with Trader++! 🚀

__init__.py

Whitespace-only changes.

analytics/__init__.py

Whitespace-only changes.

analytics/performance.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import pandas as pd
2+
from typing import Dict
3+
4+
5+
def evaluate_portfolio_performance(trade_log: pd.DataFrame, benchmark_data: pd.DataFrame) -> Dict[str, float]:
6+
"""
7+
Evaluate portfolio performance using trade logs and benchmark returns.
8+
9+
:param trade_log: DataFrame of trade history with 'date' and 'cash_remaining'
10+
:param benchmark_data: DataFrame with 'Close' prices indexed by date
11+
:return: Dictionary with performance metrics like sharpe and alpha
12+
"""
13+
if trade_log.empty or benchmark_data.empty:
14+
return {"sharpe": float('nan'), "alpha": float('nan')}
15+
16+
equity_curve = trade_log.groupby('date')['cash_remaining'].last().fillna(method='ffill')
17+
returns = equity_curve.pct_change().dropna()
18+
19+
benchmark_returns = benchmark_data['Close'].pct_change().dropna()
20+
aligned = pd.concat([returns, benchmark_returns], axis=1).dropna()
21+
aligned.columns = ['portfolio', 'benchmark']
22+
23+
excess_returns = aligned['portfolio'] - aligned['benchmark']
24+
alpha = excess_returns.mean() * 252
25+
sharpe = aligned['portfolio'].mean() / aligned['portfolio'].std() * (252 ** 0.5)
26+
27+
return {
28+
"sharpe": round(sharpe, 4),
29+
"alpha": round(alpha, 4)
30+
}

contracts/__init__.py

Whitespace-only changes.

contracts/asset.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
class Asset:
2+
"""
3+
Represents a tradable asset with a ticker and quantity held.
4+
"""
5+
def __init__(self, ticker: str, shares: int = 0):
6+
self.ticker = ticker
7+
self.shares = shares
8+
self.trade_history = []
9+
10+
def buy(self, quantity: int):
11+
self.shares += quantity
12+
13+
def sell(self, quantity: int):
14+
if quantity > self.shares:
15+
raise ValueError("Cannot sell more than held quantity")
16+
self.shares -= quantity
17+
18+
def is_empty(self):
19+
return self.shares == 0
20+
21+
22+
class CashAsset(Asset):
23+
"""
24+
Represents the cash reserve in a portfolio.
25+
"""
26+
def __init__(self, initial_cash: float = 0.0):
27+
super().__init__(ticker='CASH', shares=initial_cash)
28+
29+
@property
30+
def balance(self):
31+
return self.shares
32+
33+
def deposit_cash(self, amount: float):
34+
if amount < 0:
35+
raise ValueError("Cannot add negative cash")
36+
self.shares += amount
37+
38+
def withdraw_cash(self, amount: float):
39+
if amount > self.shares:
40+
raise ValueError("Cannot withdraw more than available cash")
41+
self.shares -= amount

contracts/portfolio.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
from typing import Dict, List
2+
3+
import pandas as pd
4+
5+
from analytics.performance import evaluate_portfolio_performance
6+
from contracts.asset import Asset, CashAsset
7+
from strategies.stock.base import StrategyBase
8+
9+
10+
class Portfolio:
11+
def __init__(self,
12+
name: str,
13+
tickers: List[str],
14+
starting_cash: float,
15+
strategy: StrategyBase,
16+
benchmark: str = "^SPY",
17+
rebalance_freq: str = "monthly",
18+
metadata: Dict = None):
19+
"""
20+
Initialize a Portfolio object with strategy, tickers, and starting cash.
21+
22+
:param name: Name of the portfolio
23+
:param tickers: List of tickers in the portfolio
24+
:param starting_cash: Initial cash in the portfolio
25+
:param strategy: Strategy object associated with this portfolio
26+
:param benchmark: Benchmark ticker used to compare portfolio performance (e.g., ^SPY)
27+
:param rebalance_freq: Frequency of rebalancing (e.g., monthly, quarterly)
28+
:param metadata: Additional metadata or user-defined attributes
29+
"""
30+
self.name = name
31+
self.tickers = tickers
32+
self.strategy = strategy
33+
self.benchmark = benchmark
34+
self.rebalance_freq = rebalance_freq
35+
self.metadata = metadata or {}
36+
37+
self.positions: Dict[str, Asset | CashAsset] = {
38+
ticker: Asset(ticker)
39+
for ticker in tickers
40+
}
41+
self.positions['CASH'] = CashAsset(starting_cash)
42+
self.trade_log = []
43+
self.position_history: Dict[str, List[int]] = {}
44+
45+
def execute_trade(self, date, ticker, action, shares, price, note='Strategy Signal'):
46+
"""
47+
Executes a trade and adjusts portfolio cash and position accordingly.
48+
49+
:param date: Trade date
50+
:param ticker: Ticker symbol
51+
:param action: 'BUY' or 'SELL'
52+
:param shares: Number of shares
53+
:param price: Trade price per share
54+
:param note: Optional note (e.g., 'Strategy Signal')
55+
"""
56+
trade_value = shares * price
57+
cash_asset = self.positions['CASH']
58+
59+
if action == 'BUY':
60+
if cash_asset.balance < trade_value:
61+
raise ValueError(f"Insufficient cash to buy {shares} shares of {ticker}")
62+
cash_asset.withdraw_cash(trade_value)
63+
self.update_position(ticker, shares)
64+
65+
elif action == 'SELL':
66+
held = self.get_position(ticker)
67+
if held < shares:
68+
raise ValueError(f"Trying to sell more shares than held for {ticker}")
69+
self.update_position(ticker, -shares)
70+
cash_asset.deposit_cash(trade_value)
71+
72+
else:
73+
raise ValueError("Action must be either 'BUY' or 'SELL'")
74+
75+
self.add_trade(date, ticker, action, shares, price, cash_asset.balance, note)
76+
77+
def get_cash(self) -> float:
78+
return self.positions['CASH'].balance
79+
80+
def add_trade(self, date, ticker, action, shares, price, cash_remaining, note=''):
81+
entry = {
82+
'date': date,
83+
'ticker': ticker,
84+
'action': action,
85+
'shares': shares,
86+
'price': price,
87+
'cash_remaining': cash_remaining,
88+
'note': note
89+
}
90+
if action == 'BUY':
91+
entry['cost'] = shares * price
92+
elif action == 'SELL':
93+
entry['revenue'] = shares * price
94+
self.trade_log.append(entry)
95+
96+
def update_position(self, ticker, shares_delta):
97+
if ticker not in self.positions:
98+
self.positions[ticker] = Asset(ticker)
99+
if shares_delta > 0:
100+
self.positions[ticker].buy(shares_delta)
101+
elif shares_delta < 0:
102+
self.positions[ticker].sell(abs(shares_delta))
103+
if self.positions[ticker].is_empty():
104+
del self.positions[ticker]
105+
106+
def get_trade_log(self) -> pd.DataFrame:
107+
return pd.DataFrame(self.trade_log)
108+
109+
def get_position(self, ticker) -> int:
110+
if ticker not in self.positions:
111+
return 0
112+
return self.positions[ticker].shares
113+
114+
def evaluate_performance(self, benchmark_data: pd.DataFrame) -> Dict[str, float]:
115+
"""
116+
Evaluate portfolio performance using trade logs and benchmark returns.
117+
118+
:param benchmark_data: DataFrame with 'Close' prices indexed by date
119+
:return: Dictionary with performance metrics like Sharpe ratio and Alpha
120+
"""
121+
return evaluate_portfolio_performance(self.get_trade_log(), benchmark_data)

core/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)