mcli-framework 7.1.3__py3-none-any.whl → 7.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mcli-framework might be problematic. Click here for more details.
- mcli/__init__.py +160 -0
- mcli/__main__.py +14 -0
- mcli/app/__init__.py +23 -0
- mcli/app/main.py +10 -0
- mcli/app/model/__init__.py +0 -0
- mcli/app/video/__init__.py +5 -0
- mcli/chat/__init__.py +34 -0
- mcli/lib/__init__.py +0 -0
- mcli/lib/api/__init__.py +0 -0
- mcli/lib/auth/__init__.py +1 -0
- mcli/lib/config/__init__.py +1 -0
- mcli/lib/custom_commands.py +424 -0
- mcli/lib/erd/__init__.py +25 -0
- mcli/lib/files/__init__.py +0 -0
- mcli/lib/fs/__init__.py +1 -0
- mcli/lib/logger/__init__.py +3 -0
- mcli/lib/paths.py +12 -0
- mcli/lib/performance/__init__.py +17 -0
- mcli/lib/pickles/__init__.py +1 -0
- mcli/lib/shell/__init__.py +0 -0
- mcli/lib/toml/__init__.py +1 -0
- mcli/lib/watcher/__init__.py +0 -0
- mcli/ml/__init__.py +16 -0
- mcli/ml/api/__init__.py +30 -0
- mcli/ml/api/routers/__init__.py +27 -0
- mcli/ml/api/schemas.py +2 -2
- mcli/ml/auth/__init__.py +45 -0
- mcli/ml/auth/models.py +2 -2
- mcli/ml/backtesting/__init__.py +39 -0
- mcli/ml/cli/__init__.py +5 -0
- mcli/ml/cli/main.py +1 -1
- mcli/ml/config/__init__.py +33 -0
- mcli/ml/configs/__init__.py +16 -0
- mcli/ml/dashboard/__init__.py +12 -0
- mcli/ml/dashboard/app.py +13 -13
- mcli/ml/dashboard/app_integrated.py +1309 -148
- mcli/ml/dashboard/app_supabase.py +46 -21
- mcli/ml/dashboard/app_training.py +14 -14
- mcli/ml/dashboard/components/__init__.py +7 -0
- mcli/ml/dashboard/components/charts.py +258 -0
- mcli/ml/dashboard/components/metrics.py +125 -0
- mcli/ml/dashboard/components/tables.py +228 -0
- mcli/ml/dashboard/pages/__init__.py +6 -0
- mcli/ml/dashboard/pages/cicd.py +382 -0
- mcli/ml/dashboard/pages/predictions_enhanced.py +834 -0
- mcli/ml/dashboard/pages/scrapers_and_logs.py +1060 -0
- mcli/ml/dashboard/pages/test_portfolio.py +373 -0
- mcli/ml/dashboard/pages/trading.py +714 -0
- mcli/ml/dashboard/pages/workflows.py +533 -0
- mcli/ml/dashboard/utils.py +154 -0
- mcli/ml/data_ingestion/__init__.py +39 -0
- mcli/ml/database/__init__.py +47 -0
- mcli/ml/experimentation/__init__.py +29 -0
- mcli/ml/features/__init__.py +39 -0
- mcli/ml/mlops/__init__.py +33 -0
- mcli/ml/models/__init__.py +94 -0
- mcli/ml/monitoring/__init__.py +25 -0
- mcli/ml/optimization/__init__.py +27 -0
- mcli/ml/predictions/__init__.py +5 -0
- mcli/ml/preprocessing/__init__.py +28 -0
- mcli/ml/scripts/__init__.py +1 -0
- mcli/ml/trading/__init__.py +60 -0
- mcli/ml/trading/alpaca_client.py +353 -0
- mcli/ml/trading/migrations.py +164 -0
- mcli/ml/trading/models.py +418 -0
- mcli/ml/trading/paper_trading.py +326 -0
- mcli/ml/trading/risk_management.py +370 -0
- mcli/ml/trading/trading_service.py +480 -0
- mcli/ml/training/__init__.py +10 -0
- mcli/ml/training/train_model.py +569 -0
- mcli/mygroup/__init__.py +3 -0
- mcli/public/__init__.py +1 -0
- mcli/public/commands/__init__.py +2 -0
- mcli/self/__init__.py +3 -0
- mcli/self/self_cmd.py +579 -91
- mcli/workflow/__init__.py +0 -0
- mcli/workflow/daemon/__init__.py +15 -0
- mcli/workflow/daemon/daemon.py +21 -3
- mcli/workflow/dashboard/__init__.py +5 -0
- mcli/workflow/docker/__init__.py +0 -0
- mcli/workflow/file/__init__.py +0 -0
- mcli/workflow/gcloud/__init__.py +1 -0
- mcli/workflow/git_commit/__init__.py +0 -0
- mcli/workflow/interview/__init__.py +0 -0
- mcli/workflow/politician_trading/__init__.py +4 -0
- mcli/workflow/politician_trading/data_sources.py +259 -1
- mcli/workflow/politician_trading/models.py +159 -1
- mcli/workflow/politician_trading/scrapers_corporate_registry.py +846 -0
- mcli/workflow/politician_trading/scrapers_free_sources.py +516 -0
- mcli/workflow/politician_trading/scrapers_third_party.py +391 -0
- mcli/workflow/politician_trading/seed_database.py +539 -0
- mcli/workflow/registry/__init__.py +0 -0
- mcli/workflow/repo/__init__.py +0 -0
- mcli/workflow/scheduler/__init__.py +25 -0
- mcli/workflow/search/__init__.py +0 -0
- mcli/workflow/sync/__init__.py +5 -0
- mcli/workflow/videos/__init__.py +1 -0
- mcli/workflow/wakatime/__init__.py +80 -0
- mcli/workflow/workflow.py +8 -27
- {mcli_framework-7.1.3.dist-info → mcli_framework-7.3.1.dist-info}/METADATA +3 -1
- {mcli_framework-7.1.3.dist-info → mcli_framework-7.3.1.dist-info}/RECORD +105 -29
- mcli/workflow/daemon/api_daemon.py +0 -800
- mcli/workflow/daemon/commands.py +0 -1196
- mcli/workflow/dashboard/dashboard_cmd.py +0 -120
- mcli/workflow/file/file.py +0 -100
- mcli/workflow/git_commit/commands.py +0 -430
- mcli/workflow/politician_trading/commands.py +0 -1939
- mcli/workflow/scheduler/commands.py +0 -493
- mcli/workflow/sync/sync_cmd.py +0 -437
- mcli/workflow/videos/videos.py +0 -242
- {mcli_framework-7.1.3.dist-info → mcli_framework-7.3.1.dist-info}/WHEEL +0 -0
- {mcli_framework-7.1.3.dist-info → mcli_framework-7.3.1.dist-info}/entry_points.txt +0 -0
- {mcli_framework-7.1.3.dist-info → mcli_framework-7.3.1.dist-info}/licenses/LICENSE +0 -0
- {mcli_framework-7.1.3.dist-info → mcli_framework-7.3.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
"""Trading and portfolio models for the ML system"""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from decimal import Decimal
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Any, Dict, List, Optional, Union
|
|
8
|
+
from uuid import UUID, uuid4
|
|
9
|
+
|
|
10
|
+
from sqlalchemy import (
|
|
11
|
+
Boolean, Column, DateTime, Enum as SQLEnum, Float, ForeignKey,
|
|
12
|
+
Integer, String, Text, JSON, Numeric, Index
|
|
13
|
+
)
|
|
14
|
+
from sqlalchemy.dialects.postgresql import UUID as PostgresUUID
|
|
15
|
+
from sqlalchemy.ext.declarative import declarative_base
|
|
16
|
+
from sqlalchemy.orm import relationship
|
|
17
|
+
from pydantic import BaseModel, Field
|
|
18
|
+
|
|
19
|
+
Base = declarative_base()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class OrderStatus(Enum):
|
|
23
|
+
"""Order status enumeration"""
|
|
24
|
+
PENDING = "pending"
|
|
25
|
+
SUBMITTED = "submitted"
|
|
26
|
+
FILLED = "filled"
|
|
27
|
+
PARTIALLY_FILLED = "partially_filled"
|
|
28
|
+
CANCELLED = "cancelled"
|
|
29
|
+
REJECTED = "rejected"
|
|
30
|
+
EXPIRED = "expired"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class OrderType(Enum):
|
|
34
|
+
"""Order type enumeration"""
|
|
35
|
+
MARKET = "market"
|
|
36
|
+
LIMIT = "limit"
|
|
37
|
+
STOP = "stop"
|
|
38
|
+
STOP_LIMIT = "stop_limit"
|
|
39
|
+
TRAILING_STOP = "trailing_stop"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class OrderSide(Enum):
|
|
43
|
+
"""Order side enumeration"""
|
|
44
|
+
BUY = "buy"
|
|
45
|
+
SELL = "sell"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class PositionSide(Enum):
|
|
49
|
+
"""Position side enumeration"""
|
|
50
|
+
LONG = "long"
|
|
51
|
+
SHORT = "short"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class PortfolioType(Enum):
|
|
55
|
+
"""Portfolio type enumeration"""
|
|
56
|
+
TEST = "test"
|
|
57
|
+
LIVE = "live"
|
|
58
|
+
PAPER = "paper"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class RiskLevel(Enum):
|
|
62
|
+
"""Risk level enumeration"""
|
|
63
|
+
CONSERVATIVE = "conservative"
|
|
64
|
+
MODERATE = "moderate"
|
|
65
|
+
AGGRESSIVE = "aggressive"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# Database Models
|
|
69
|
+
class TradingAccount(Base):
|
|
70
|
+
"""Trading account information"""
|
|
71
|
+
__tablename__ = "trading_accounts"
|
|
72
|
+
|
|
73
|
+
id = Column(PostgresUUID(as_uuid=True), primary_key=True, default=uuid4)
|
|
74
|
+
user_id = Column(PostgresUUID(as_uuid=True), nullable=False) # Remove foreign key for now
|
|
75
|
+
account_name = Column(String(100), nullable=False)
|
|
76
|
+
account_type = Column(SQLEnum(PortfolioType), nullable=False, default=PortfolioType.TEST)
|
|
77
|
+
|
|
78
|
+
# Alpaca credentials (encrypted)
|
|
79
|
+
alpaca_api_key = Column(String(255), nullable=True)
|
|
80
|
+
alpaca_secret_key = Column(String(255), nullable=True)
|
|
81
|
+
alpaca_base_url = Column(String(255), nullable=True)
|
|
82
|
+
|
|
83
|
+
# Account settings
|
|
84
|
+
paper_trading = Column(Boolean, default=True)
|
|
85
|
+
risk_level = Column(SQLEnum(RiskLevel), default=RiskLevel.MODERATE)
|
|
86
|
+
max_position_size = Column(Float, default=0.1) # Max 10% per position
|
|
87
|
+
max_portfolio_risk = Column(Float, default=0.2) # Max 20% portfolio risk
|
|
88
|
+
|
|
89
|
+
# Metadata
|
|
90
|
+
created_at = Column(DateTime, default=datetime.utcnow)
|
|
91
|
+
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
92
|
+
is_active = Column(Boolean, default=True)
|
|
93
|
+
|
|
94
|
+
# Relationships
|
|
95
|
+
portfolios = relationship("Portfolio", back_populates="trading_account")
|
|
96
|
+
orders = relationship("TradingOrder", back_populates="trading_account")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class Portfolio(Base):
|
|
100
|
+
"""Portfolio information"""
|
|
101
|
+
__tablename__ = "portfolios"
|
|
102
|
+
|
|
103
|
+
id = Column(PostgresUUID(as_uuid=True), primary_key=True, default=uuid4)
|
|
104
|
+
trading_account_id = Column(PostgresUUID(as_uuid=True), ForeignKey("trading_accounts.id"), nullable=False)
|
|
105
|
+
name = Column(String(100), nullable=False)
|
|
106
|
+
description = Column(Text, nullable=True)
|
|
107
|
+
|
|
108
|
+
# Portfolio settings
|
|
109
|
+
initial_capital = Column(Numeric(15, 2), nullable=False, default=100000.00)
|
|
110
|
+
current_value = Column(Numeric(15, 2), nullable=False, default=100000.00)
|
|
111
|
+
cash_balance = Column(Numeric(15, 2), nullable=False, default=100000.00)
|
|
112
|
+
|
|
113
|
+
# Performance metrics
|
|
114
|
+
total_return = Column(Float, default=0.0)
|
|
115
|
+
total_return_pct = Column(Float, default=0.0)
|
|
116
|
+
daily_return = Column(Float, default=0.0)
|
|
117
|
+
daily_return_pct = Column(Float, default=0.0)
|
|
118
|
+
sharpe_ratio = Column(Float, default=0.0)
|
|
119
|
+
sortino_ratio = Column(Float, default=0.0)
|
|
120
|
+
max_drawdown = Column(Float, default=0.0)
|
|
121
|
+
max_drawdown_duration = Column(Integer, default=0)
|
|
122
|
+
volatility = Column(Float, default=0.0)
|
|
123
|
+
|
|
124
|
+
# Risk metrics
|
|
125
|
+
var_95 = Column(Float, default=0.0) # Value at Risk 95%
|
|
126
|
+
cvar_95 = Column(Float, default=0.0) # Conditional Value at Risk 95%
|
|
127
|
+
beta = Column(Float, default=1.0)
|
|
128
|
+
|
|
129
|
+
# Status
|
|
130
|
+
is_active = Column(Boolean, default=True)
|
|
131
|
+
created_at = Column(DateTime, default=datetime.utcnow)
|
|
132
|
+
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
133
|
+
|
|
134
|
+
# Relationships
|
|
135
|
+
trading_account = relationship("TradingAccount", back_populates="portfolios")
|
|
136
|
+
positions = relationship("Position", back_populates="portfolio")
|
|
137
|
+
orders = relationship("TradingOrder", back_populates="portfolio")
|
|
138
|
+
performance_snapshots = relationship("PortfolioPerformanceSnapshot", back_populates="portfolio")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class Position(Base):
|
|
142
|
+
"""Individual position in a portfolio"""
|
|
143
|
+
__tablename__ = "positions"
|
|
144
|
+
|
|
145
|
+
id = Column(PostgresUUID(as_uuid=True), primary_key=True, default=uuid4)
|
|
146
|
+
portfolio_id = Column(PostgresUUID(as_uuid=True), ForeignKey("portfolios.id"), nullable=False)
|
|
147
|
+
symbol = Column(String(10), nullable=False, index=True)
|
|
148
|
+
|
|
149
|
+
# Position details
|
|
150
|
+
quantity = Column(Integer, nullable=False)
|
|
151
|
+
side = Column(SQLEnum(PositionSide), nullable=False)
|
|
152
|
+
average_price = Column(Numeric(10, 4), nullable=False)
|
|
153
|
+
current_price = Column(Numeric(10, 4), nullable=False)
|
|
154
|
+
|
|
155
|
+
# Financial metrics
|
|
156
|
+
market_value = Column(Numeric(15, 2), nullable=False)
|
|
157
|
+
cost_basis = Column(Numeric(15, 2), nullable=False)
|
|
158
|
+
unrealized_pnl = Column(Numeric(15, 2), nullable=False, default=0.0)
|
|
159
|
+
unrealized_pnl_pct = Column(Float, nullable=False, default=0.0)
|
|
160
|
+
realized_pnl = Column(Numeric(15, 2), nullable=False, default=0.0)
|
|
161
|
+
|
|
162
|
+
# Position sizing
|
|
163
|
+
position_size_pct = Column(Float, nullable=False, default=0.0) # % of portfolio
|
|
164
|
+
weight = Column(Float, nullable=False, default=0.0) # Portfolio weight
|
|
165
|
+
|
|
166
|
+
# Metadata
|
|
167
|
+
created_at = Column(DateTime, default=datetime.utcnow)
|
|
168
|
+
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
169
|
+
|
|
170
|
+
# Relationships
|
|
171
|
+
portfolio = relationship("Portfolio", back_populates="positions")
|
|
172
|
+
trades = relationship("TradingOrder", back_populates="position")
|
|
173
|
+
|
|
174
|
+
# Indexes
|
|
175
|
+
__table_args__ = (
|
|
176
|
+
Index('idx_position_portfolio_symbol', 'portfolio_id', 'symbol'),
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class TradingOrder(Base):
|
|
181
|
+
"""Trading order information"""
|
|
182
|
+
__tablename__ = "trading_orders"
|
|
183
|
+
|
|
184
|
+
id = Column(PostgresUUID(as_uuid=True), primary_key=True, default=uuid4)
|
|
185
|
+
trading_account_id = Column(PostgresUUID(as_uuid=True), ForeignKey("trading_accounts.id"), nullable=False)
|
|
186
|
+
portfolio_id = Column(PostgresUUID(as_uuid=True), ForeignKey("portfolios.id"), nullable=False)
|
|
187
|
+
position_id = Column(PostgresUUID(as_uuid=True), ForeignKey("positions.id"), nullable=True)
|
|
188
|
+
|
|
189
|
+
# Order details
|
|
190
|
+
symbol = Column(String(10), nullable=False, index=True)
|
|
191
|
+
side = Column(SQLEnum(OrderSide), nullable=False)
|
|
192
|
+
order_type = Column(SQLEnum(OrderType), nullable=False)
|
|
193
|
+
quantity = Column(Integer, nullable=False)
|
|
194
|
+
|
|
195
|
+
# Pricing
|
|
196
|
+
limit_price = Column(Numeric(10, 4), nullable=True)
|
|
197
|
+
stop_price = Column(Numeric(10, 4), nullable=True)
|
|
198
|
+
average_fill_price = Column(Numeric(10, 4), nullable=True)
|
|
199
|
+
|
|
200
|
+
# Status and execution
|
|
201
|
+
status = Column(SQLEnum(OrderStatus), nullable=False, default=OrderStatus.PENDING)
|
|
202
|
+
filled_quantity = Column(Integer, nullable=False, default=0)
|
|
203
|
+
remaining_quantity = Column(Integer, nullable=False)
|
|
204
|
+
|
|
205
|
+
# Timestamps
|
|
206
|
+
created_at = Column(DateTime, default=datetime.utcnow)
|
|
207
|
+
submitted_at = Column(DateTime, nullable=True)
|
|
208
|
+
filled_at = Column(DateTime, nullable=True)
|
|
209
|
+
cancelled_at = Column(DateTime, nullable=True)
|
|
210
|
+
|
|
211
|
+
# Additional info
|
|
212
|
+
time_in_force = Column(String(20), default="day")
|
|
213
|
+
extended_hours = Column(Boolean, default=False)
|
|
214
|
+
client_order_id = Column(String(100), nullable=True)
|
|
215
|
+
alpaca_order_id = Column(String(100), nullable=True, index=True)
|
|
216
|
+
|
|
217
|
+
# Relationships
|
|
218
|
+
trading_account = relationship("TradingAccount", back_populates="orders")
|
|
219
|
+
portfolio = relationship("Portfolio", back_populates="orders")
|
|
220
|
+
position = relationship("Position", back_populates="trades")
|
|
221
|
+
|
|
222
|
+
# Indexes
|
|
223
|
+
__table_args__ = (
|
|
224
|
+
Index('idx_order_portfolio_status', 'portfolio_id', 'status'),
|
|
225
|
+
Index('idx_order_symbol_status', 'symbol', 'status'),
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class PortfolioPerformanceSnapshot(Base):
|
|
230
|
+
"""Daily portfolio performance snapshots"""
|
|
231
|
+
__tablename__ = "portfolio_performance_snapshots"
|
|
232
|
+
|
|
233
|
+
id = Column(PostgresUUID(as_uuid=True), primary_key=True, default=uuid4)
|
|
234
|
+
portfolio_id = Column(PostgresUUID(as_uuid=True), ForeignKey("portfolios.id"), nullable=False)
|
|
235
|
+
|
|
236
|
+
# Snapshot data
|
|
237
|
+
snapshot_date = Column(DateTime, nullable=False, index=True)
|
|
238
|
+
portfolio_value = Column(Numeric(15, 2), nullable=False)
|
|
239
|
+
cash_balance = Column(Numeric(15, 2), nullable=False)
|
|
240
|
+
|
|
241
|
+
# Daily performance
|
|
242
|
+
daily_return = Column(Numeric(15, 2), nullable=False, default=0.0)
|
|
243
|
+
daily_return_pct = Column(Float, nullable=False, default=0.0)
|
|
244
|
+
|
|
245
|
+
# Cumulative performance
|
|
246
|
+
total_return = Column(Numeric(15, 2), nullable=False, default=0.0)
|
|
247
|
+
total_return_pct = Column(Float, nullable=False, default=0.0)
|
|
248
|
+
|
|
249
|
+
# Risk metrics
|
|
250
|
+
volatility = Column(Float, nullable=False, default=0.0)
|
|
251
|
+
sharpe_ratio = Column(Float, nullable=False, default=0.0)
|
|
252
|
+
max_drawdown = Column(Float, nullable=False, default=0.0)
|
|
253
|
+
|
|
254
|
+
# Position data (JSON)
|
|
255
|
+
positions_data = Column(JSON, nullable=True)
|
|
256
|
+
|
|
257
|
+
# Metadata
|
|
258
|
+
created_at = Column(DateTime, default=datetime.utcnow)
|
|
259
|
+
|
|
260
|
+
# Relationships
|
|
261
|
+
portfolio = relationship("Portfolio", back_populates="performance_snapshots")
|
|
262
|
+
|
|
263
|
+
# Indexes
|
|
264
|
+
__table_args__ = (
|
|
265
|
+
Index('idx_snapshot_portfolio_date', 'portfolio_id', 'snapshot_date'),
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class TradingSignal(Base):
|
|
270
|
+
"""Trading signals generated by ML models"""
|
|
271
|
+
__tablename__ = "trading_signals"
|
|
272
|
+
|
|
273
|
+
id = Column(PostgresUUID(as_uuid=True), primary_key=True, default=uuid4)
|
|
274
|
+
portfolio_id = Column(PostgresUUID(as_uuid=True), ForeignKey("portfolios.id"), nullable=False)
|
|
275
|
+
|
|
276
|
+
# Signal details
|
|
277
|
+
symbol = Column(String(10), nullable=False, index=True)
|
|
278
|
+
signal_type = Column(String(20), nullable=False) # "buy", "sell", "hold"
|
|
279
|
+
confidence = Column(Float, nullable=False)
|
|
280
|
+
strength = Column(Float, nullable=False) # Signal strength 0-1
|
|
281
|
+
|
|
282
|
+
# ML model info
|
|
283
|
+
model_id = Column(String(100), nullable=True)
|
|
284
|
+
model_version = Column(String(50), nullable=True)
|
|
285
|
+
prediction_id = Column(PostgresUUID(as_uuid=True), nullable=True)
|
|
286
|
+
|
|
287
|
+
# Signal parameters
|
|
288
|
+
target_price = Column(Numeric(10, 4), nullable=True)
|
|
289
|
+
stop_loss = Column(Numeric(10, 4), nullable=True)
|
|
290
|
+
take_profit = Column(Numeric(10, 4), nullable=True)
|
|
291
|
+
position_size = Column(Float, nullable=True) # Suggested position size as % of portfolio
|
|
292
|
+
|
|
293
|
+
# Metadata
|
|
294
|
+
created_at = Column(DateTime, default=datetime.utcnow)
|
|
295
|
+
expires_at = Column(DateTime, nullable=True)
|
|
296
|
+
is_active = Column(Boolean, default=True)
|
|
297
|
+
|
|
298
|
+
# Relationships
|
|
299
|
+
portfolio = relationship("Portfolio")
|
|
300
|
+
|
|
301
|
+
# Indexes
|
|
302
|
+
__table_args__ = (
|
|
303
|
+
Index('idx_signal_portfolio_symbol', 'portfolio_id', 'symbol'),
|
|
304
|
+
Index('idx_signal_created_active', 'created_at', 'is_active'),
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
# Pydantic Models for API
|
|
309
|
+
class TradingAccountCreate(BaseModel):
|
|
310
|
+
"""Create trading account request"""
|
|
311
|
+
account_name: str = Field(..., min_length=1, max_length=100)
|
|
312
|
+
account_type: PortfolioType = Field(default=PortfolioType.TEST)
|
|
313
|
+
alpaca_api_key: Optional[str] = None
|
|
314
|
+
alpaca_secret_key: Optional[str] = None
|
|
315
|
+
paper_trading: bool = Field(default=True)
|
|
316
|
+
risk_level: RiskLevel = Field(default=RiskLevel.MODERATE)
|
|
317
|
+
max_position_size: float = Field(default=0.1, ge=0.01, le=1.0)
|
|
318
|
+
max_portfolio_risk: float = Field(default=0.2, ge=0.01, le=1.0)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
class PortfolioCreate(BaseModel):
|
|
322
|
+
"""Create portfolio request"""
|
|
323
|
+
name: str = Field(..., min_length=1, max_length=100)
|
|
324
|
+
description: Optional[str] = None
|
|
325
|
+
initial_capital: float = Field(default=100000.0, gt=0)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
class OrderCreate(BaseModel):
|
|
329
|
+
"""Create order request"""
|
|
330
|
+
symbol: str = Field(..., min_length=1, max_length=10)
|
|
331
|
+
side: OrderSide
|
|
332
|
+
order_type: OrderType
|
|
333
|
+
quantity: int = Field(..., gt=0)
|
|
334
|
+
limit_price: Optional[float] = Field(None, gt=0)
|
|
335
|
+
stop_price: Optional[float] = Field(None, gt=0)
|
|
336
|
+
time_in_force: str = Field(default="day")
|
|
337
|
+
extended_hours: bool = Field(default=False)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
class PositionResponse(BaseModel):
|
|
341
|
+
"""Position response model"""
|
|
342
|
+
id: UUID
|
|
343
|
+
symbol: str
|
|
344
|
+
quantity: int
|
|
345
|
+
side: PositionSide
|
|
346
|
+
average_price: float
|
|
347
|
+
current_price: float
|
|
348
|
+
market_value: float
|
|
349
|
+
cost_basis: float
|
|
350
|
+
unrealized_pnl: float
|
|
351
|
+
unrealized_pnl_pct: float
|
|
352
|
+
realized_pnl: float
|
|
353
|
+
position_size_pct: float
|
|
354
|
+
weight: float
|
|
355
|
+
created_at: datetime
|
|
356
|
+
updated_at: datetime
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
class OrderResponse(BaseModel):
|
|
360
|
+
"""Order response model"""
|
|
361
|
+
id: UUID
|
|
362
|
+
symbol: str
|
|
363
|
+
side: OrderSide
|
|
364
|
+
order_type: OrderType
|
|
365
|
+
quantity: int
|
|
366
|
+
limit_price: Optional[float]
|
|
367
|
+
stop_price: Optional[float]
|
|
368
|
+
average_fill_price: Optional[float]
|
|
369
|
+
status: OrderStatus
|
|
370
|
+
filled_quantity: int
|
|
371
|
+
remaining_quantity: int
|
|
372
|
+
created_at: datetime
|
|
373
|
+
submitted_at: Optional[datetime]
|
|
374
|
+
filled_at: Optional[datetime]
|
|
375
|
+
cancelled_at: Optional[datetime]
|
|
376
|
+
time_in_force: str
|
|
377
|
+
extended_hours: bool
|
|
378
|
+
alpaca_order_id: Optional[str]
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class PortfolioResponse(BaseModel):
|
|
382
|
+
"""Portfolio response model"""
|
|
383
|
+
id: UUID
|
|
384
|
+
name: str
|
|
385
|
+
description: Optional[str]
|
|
386
|
+
initial_capital: float
|
|
387
|
+
current_value: float
|
|
388
|
+
cash_balance: float
|
|
389
|
+
total_return: float
|
|
390
|
+
total_return_pct: float
|
|
391
|
+
daily_return: float
|
|
392
|
+
daily_return_pct: float
|
|
393
|
+
sharpe_ratio: float
|
|
394
|
+
sortino_ratio: float
|
|
395
|
+
max_drawdown: float
|
|
396
|
+
volatility: float
|
|
397
|
+
is_active: bool
|
|
398
|
+
created_at: datetime
|
|
399
|
+
updated_at: datetime
|
|
400
|
+
positions: List[PositionResponse] = []
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
class TradingSignalResponse(BaseModel):
|
|
404
|
+
"""Trading signal response model"""
|
|
405
|
+
id: UUID
|
|
406
|
+
symbol: str
|
|
407
|
+
signal_type: str
|
|
408
|
+
confidence: float
|
|
409
|
+
strength: float
|
|
410
|
+
model_id: Optional[str]
|
|
411
|
+
model_version: Optional[str]
|
|
412
|
+
target_price: Optional[float]
|
|
413
|
+
stop_loss: Optional[float]
|
|
414
|
+
take_profit: Optional[float]
|
|
415
|
+
position_size: Optional[float]
|
|
416
|
+
created_at: datetime
|
|
417
|
+
expires_at: Optional[datetime]
|
|
418
|
+
is_active: bool
|