sigma-terminal 2.0.2__py3-none-any.whl → 3.3.0__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.
sigma/core/models.py CHANGED
@@ -1,153 +1,544 @@
1
- """Data models for Sigma."""
2
-
3
- from datetime import datetime
4
- from enum import Enum
5
- from typing import Any, Optional
1
+ """Data models for Sigma Financial Intelligence Platform."""
6
2
 
3
+ from dataclasses import dataclass, field
4
+ from datetime import datetime, date
5
+ from enum import Enum, auto
6
+ from typing import Any, Dict, List, Optional, Union
7
7
  from pydantic import BaseModel, Field
8
+ import json
8
9
 
9
10
 
10
- class MessageRole(str, Enum):
11
- """Message roles."""
12
- SYSTEM = "system"
13
- USER = "user"
14
- ASSISTANT = "assistant"
15
- TOOL = "tool"
11
+ # ============================================================================
12
+ # ENUMS
13
+ # ============================================================================
16
14
 
15
+ class AssetClass(str, Enum):
16
+ """Asset class types."""
17
+ EQUITY = "equity"
18
+ ETF = "etf"
19
+ OPTION = "option"
20
+ FUTURE = "future"
21
+ FOREX = "forex"
22
+ CRYPTO = "crypto"
23
+ RATES = "rates"
24
+ COMMODITY = "commodity"
25
+ INDEX = "index"
26
+ FUND = "fund"
27
+ UNKNOWN = "unknown"
17
28
 
18
- class ToolCall(BaseModel):
19
- """Tool call."""
20
- id: str
21
- name: str
22
- arguments: dict[str, Any]
23
-
24
-
25
- class Message(BaseModel):
26
- """Chat message."""
27
- role: MessageRole
28
- content: str
29
- tool_calls: list[ToolCall] = Field(default_factory=list)
30
- tool_call_id: Optional[str] = None
31
- name: Optional[str] = None
32
- timestamp: datetime = Field(default_factory=datetime.now)
33
-
34
-
35
- class ToolResult(BaseModel):
36
- """Tool execution result."""
37
- tool_name: str
38
- tool_call_id: str
39
- success: bool
40
- result: Any
41
- error: Optional[str] = None
42
- duration_ms: float = 0.0
43
-
44
- @property
45
- def display_result(self) -> str:
46
- """Get result for display."""
47
- if self.error:
48
- return f"Error: {self.error}"
49
- if isinstance(self.result, dict):
50
- return str(self.result)
51
- return str(self.result) if self.result else "No result"
52
-
53
-
54
- class AgentStep(BaseModel):
55
- """Agent execution step."""
56
- step_number: int
57
- action: str
58
- tool_calls: list[ToolCall] = Field(default_factory=list)
59
- tool_results: list[ToolResult] = Field(default_factory=list)
60
- reasoning: Optional[str] = None
61
- timestamp: datetime = Field(default_factory=datetime.now)
62
-
63
-
64
- class FinancialMetric(BaseModel):
65
- """Financial metric."""
29
+
30
+ class TimeHorizon(str, Enum):
31
+ """Investment time horizons."""
32
+ INTRADAY = "intraday"
33
+ DAILY = "daily"
34
+ WEEKLY = "weekly"
35
+ MONTHLY = "monthly"
36
+ QUARTERLY = "quarterly"
37
+ YEARLY = "yearly"
38
+ MULTI_YEAR = "multi_year"
39
+
40
+
41
+ class RiskProfile(str, Enum):
42
+ """Risk tolerance levels."""
43
+ CONSERVATIVE = "conservative"
44
+ MODERATE = "moderate"
45
+ AGGRESSIVE = "aggressive"
46
+ VERY_AGGRESSIVE = "very_aggressive"
47
+
48
+
49
+ class DeliverableType(str, Enum):
50
+ """Types of research deliverables."""
51
+ QUICK_ANSWER = "quick_answer"
52
+ ANALYSIS = "analysis"
53
+ COMPARISON = "comparison"
54
+ BACKTEST = "backtest"
55
+ PORTFOLIO = "portfolio"
56
+ STRATEGY = "strategy"
57
+ REPORT = "report"
58
+ ALERT = "alert"
59
+ CHART = "chart"
60
+
61
+
62
+ class Regime(str, Enum):
63
+ """Market regime types."""
64
+ BULL = "bull"
65
+ BEAR = "bear"
66
+ SIDEWAYS = "sideways"
67
+ HIGH_VOL = "high_volatility"
68
+ LOW_VOL = "low_volatility"
69
+ CRISIS = "crisis"
70
+ RECOVERY = "recovery"
71
+
72
+
73
+ class SignalType(str, Enum):
74
+ """Signal types for strategies."""
75
+ TREND = "trend"
76
+ MOMENTUM = "momentum"
77
+ MEAN_REVERSION = "mean_reversion"
78
+ CARRY = "carry"
79
+ VALUE = "value"
80
+ QUALITY = "quality"
81
+ LOW_VOL = "low_volatility"
82
+ SIZE = "size"
83
+ DEFENSIVE = "defensive"
84
+ GROWTH = "growth"
85
+
86
+
87
+ class DataSource(str, Enum):
88
+ """Data source providers."""
89
+ YFINANCE = "yfinance"
90
+ ALPHA_VANTAGE = "alpha_vantage"
91
+ POLYGON = "polygon"
92
+ TIINGO = "tiingo"
93
+ FRED = "fred"
94
+ QUANDL = "quandl"
95
+ LEAN = "lean"
96
+ CUSTOM = "custom"
97
+
98
+
99
+ # ============================================================================
100
+ # RESEARCH PLAN MODELS
101
+ # ============================================================================
102
+
103
+ class Constraint(BaseModel):
104
+ """Investment constraint."""
66
105
  name: str
67
- value: float | str | None
68
- unit: Optional[str] = None
69
- period: Optional[str] = None
70
- change: Optional[float] = None
71
- change_pct: Optional[float] = None
106
+ type: str # max_weight, sector_cap, leverage_cap, turnover_cap, etc.
107
+ value: float
108
+ description: Optional[str] = None
109
+
110
+
111
+ class ResearchPlan(BaseModel):
112
+ """Structured research plan from user intent."""
113
+ goal: str = Field(..., description="Primary research goal")
114
+ assets: List[str] = Field(default_factory=list, description="Tickers/assets to analyze")
115
+ asset_classes: List[AssetClass] = Field(default_factory=list)
116
+ horizon: TimeHorizon = Field(default=TimeHorizon.DAILY)
117
+ benchmark: Optional[str] = Field(default="SPY", description="Benchmark for comparison")
118
+ risk_profile: RiskProfile = Field(default=RiskProfile.MODERATE)
119
+ constraints: List[Constraint] = Field(default_factory=list)
120
+ deliverable: DeliverableType = Field(default=DeliverableType.ANALYSIS)
121
+
122
+ # Account and tax settings
123
+ account_type: Optional[str] = Field(default=None) # taxable, ira, 401k
124
+ leverage_allowed: bool = Field(default=False)
125
+ max_leverage: float = Field(default=1.0)
126
+ tax_aware: bool = Field(default=False)
127
+
128
+ # Date ranges
129
+ start_date: Optional[date] = None
130
+ end_date: Optional[date] = None
131
+ lookback_period: Optional[str] = Field(default="2y")
132
+
133
+ # Additional context
134
+ context: Dict[str, Any] = Field(default_factory=dict)
135
+ clarifications_needed: List[str] = Field(default_factory=list)
136
+
137
+ class Config:
138
+ use_enum_values = True
139
+
140
+
141
+ # ============================================================================
142
+ # DATA MODELS
143
+ # ============================================================================
72
144
 
145
+ class DataQualityReport(BaseModel):
146
+ """Data quality assessment."""
147
+ total_records: int
148
+ missing_count: int
149
+ missing_pct: float
150
+ stale_ticks: int
151
+ outliers_detected: int
152
+ timezone_issues: int
153
+ date_range: tuple
154
+ gaps: List[tuple]
155
+ warnings: List[str]
156
+ passed: bool
73
157
 
74
- class StockSnapshot(BaseModel):
75
- """Stock market snapshot."""
158
+
159
+ class DataLineage(BaseModel):
160
+ """Track data provenance."""
161
+ source: DataSource
162
+ fetch_timestamp: datetime
163
+ symbols: List[str]
164
+ date_range: tuple
165
+ transformations: List[str] = Field(default_factory=list)
166
+ quality_report: Optional[DataQualityReport] = None
167
+ version: str = "1.0"
168
+ config: Dict[str, Any] = Field(default_factory=dict)
169
+
170
+
171
+ class CorporateAction(BaseModel):
172
+ """Corporate action event."""
173
+ date: date
174
+ symbol: str
175
+ action_type: str # split, dividend, merger, symbol_change, spinoff
176
+ details: Dict[str, Any]
177
+ adjustment_factor: Optional[float] = None
178
+
179
+
180
+ class PriceBar(BaseModel):
181
+ """OHLCV price bar."""
182
+ timestamp: datetime
183
+ open: float
184
+ high: float
185
+ low: float
186
+ close: float
187
+ volume: float
188
+ adjusted_close: Optional[float] = None
189
+ vwap: Optional[float] = None
190
+
191
+
192
+ class Fundamental(BaseModel):
193
+ """Point-in-time fundamental data."""
194
+ as_of_date: date # When data was known (avoids lookahead)
195
+ report_date: date # When the report period ends
76
196
  symbol: str
77
- company_name: str
78
- price: float
79
- change: float
80
- change_pct: float
81
- volume: int
82
- market_cap: Optional[float] = None
83
- pe_ratio: Optional[float] = None
84
- dividend_yield: Optional[float] = None
85
- week_52_high: Optional[float] = None
86
- week_52_low: Optional[float] = None
87
- avg_volume: Optional[int] = None
197
+ metrics: Dict[str, float] # revenue, earnings, etc.
198
+ source: str
199
+
200
+
201
+ # ============================================================================
202
+ # ANALYTICS MODELS
203
+ # ============================================================================
204
+
205
+ class PerformanceMetrics(BaseModel):
206
+ """Comprehensive performance metrics."""
207
+ # Returns
208
+ total_return: float
209
+ cagr: float
210
+ mtd_return: Optional[float] = None
211
+ ytd_return: Optional[float] = None
212
+
213
+ # Risk
214
+ volatility: float
215
+ downside_deviation: float
216
+ max_drawdown: float
217
+ drawdown_duration_days: Optional[int] = None
218
+ var_95: Optional[float] = None
219
+ cvar_95: Optional[float] = None
220
+
221
+ # Risk-adjusted
222
+ sharpe_ratio: float
223
+ sortino_ratio: float
224
+ calmar_ratio: float
225
+ information_ratio: Optional[float] = None
226
+ treynor_ratio: Optional[float] = None
227
+
228
+ # Other
88
229
  beta: Optional[float] = None
89
- eps: Optional[float] = None
90
- timestamp: datetime = Field(default_factory=datetime.now)
230
+ alpha: Optional[float] = None
231
+ r_squared: Optional[float] = None
232
+ tracking_error: Optional[float] = None
233
+ win_rate: Optional[float] = None
234
+ profit_factor: Optional[float] = None
91
235
 
92
236
 
93
- class FinancialStatement(BaseModel):
94
- """Financial statement data."""
237
+ class FactorExposure(BaseModel):
238
+ """Factor exposure analysis."""
95
239
  symbol: str
96
- statement_type: str # income, balance, cash_flow
97
- period: str # annual, quarterly
98
- date: str
99
- metrics: dict[str, float | None]
240
+ as_of_date: date
241
+ market_beta: float
242
+ size_exposure: Optional[float] = None
243
+ value_exposure: Optional[float] = None
244
+ momentum_exposure: Optional[float] = None
245
+ quality_exposure: Optional[float] = None
246
+ low_vol_exposure: Optional[float] = None
247
+ growth_exposure: Optional[float] = None
248
+ r_squared: float
249
+ residual_vol: float
100
250
 
101
251
 
102
- class EarningsData(BaseModel):
103
- """Earnings data."""
252
+ class RegimeAnalysis(BaseModel):
253
+ """Regime detection results."""
254
+ current_regime: Regime
255
+ regime_probability: float
256
+ regime_history: List[tuple] # (start_date, end_date, regime)
257
+ transition_matrix: Optional[Dict[str, Dict[str, float]]] = None
258
+ indicators: Dict[str, float]
259
+
260
+
261
+ class SeasonalityAnalysis(BaseModel):
262
+ """Seasonality patterns."""
104
263
  symbol: str
105
- date: str
106
- eps_estimate: Optional[float] = None
107
- eps_actual: Optional[float] = None
108
- revenue_estimate: Optional[float] = None
109
- revenue_actual: Optional[float] = None
110
- surprise_pct: Optional[float] = None
264
+ monthly_returns: Dict[int, float] # month -> avg return
265
+ day_of_week_returns: Dict[int, float] # day -> avg return
266
+ pre_earnings_drift: Optional[float] = None
267
+ post_earnings_drift: Optional[float] = None
268
+ strongest_month: int
269
+ weakest_month: int
270
+ statistical_significance: Dict[str, float]
111
271
 
112
272
 
113
- class InsiderTrade(BaseModel):
114
- """Insider trading data."""
273
+ class EventStudy(BaseModel):
274
+ """Event study results."""
275
+ event_type: str # earnings, fomc, cpi, etc.
115
276
  symbol: str
116
- insider_name: str
117
- title: str
118
- transaction_type: str
119
- shares: int
120
- price: Optional[float] = None
121
- value: Optional[float] = None
122
- date: str
277
+ events_analyzed: int
278
+ avg_move: float
279
+ std_move: float
280
+ positive_rate: float
281
+ pre_event_drift: float
282
+ post_event_drift: float
283
+ distribution: Dict[str, float] # percentiles
284
+ conditional_outcomes: Dict[str, float]
123
285
 
124
286
 
125
- class AnalystRating(BaseModel):
126
- """Analyst rating."""
287
+ # ============================================================================
288
+ # STRATEGY MODELS
289
+ # ============================================================================
290
+
291
+ class TradingRule(BaseModel):
292
+ """Explicit trading rule."""
293
+ name: str
294
+ entry_condition: str # Human-readable condition
295
+ exit_condition: str
296
+ position_size: str # Sizing rule description
297
+ stop_loss: Optional[str] = None
298
+ take_profit: Optional[str] = None
299
+ rebalance_frequency: str
300
+ assumptions: List[str] = Field(default_factory=list)
301
+ failure_modes: List[str] = Field(default_factory=list)
302
+
303
+
304
+ class StrategyHypothesis(BaseModel):
305
+ """Trading strategy hypothesis."""
306
+ name: str
307
+ description: str
308
+ signal_type: SignalType
309
+ asset_classes: List[AssetClass]
310
+ timeframe: TimeHorizon
311
+ expected_sharpe: float
312
+ expected_turnover: float
313
+ rules: List[TradingRule]
314
+ data_requirements: List[str]
315
+ assumptions: List[str]
316
+ risks: List[str]
317
+
318
+
319
+ class BacktestResult(BaseModel):
320
+ """Comprehensive backtest results."""
321
+ strategy_name: str
127
322
  symbol: str
128
- firm: str
129
- analyst: Optional[str] = None
130
- rating: str
131
- price_target: Optional[float] = None
132
- date: str
323
+ period: str
324
+
325
+ # Performance
326
+ metrics: PerformanceMetrics
327
+
328
+ # Equity curve data
329
+ equity_curve: List[float]
330
+ drawdown_curve: List[float]
331
+
332
+ # Trade analysis
333
+ total_trades: int
334
+ winning_trades: int
335
+ losing_trades: int
336
+ avg_win: float
337
+ avg_loss: float
338
+ largest_win: float
339
+ largest_loss: float
340
+ avg_holding_period: float
341
+
342
+ # Factor analysis
343
+ factor_exposures: Optional[FactorExposure] = None
344
+
345
+ # Diagnostics
346
+ warnings: List[str] = Field(default_factory=list)
347
+ issues_detected: List[str] = Field(default_factory=list)
348
+
349
+ # Reproducibility
350
+ parameters: Dict[str, Any] = Field(default_factory=dict)
351
+ data_lineage: Optional[DataLineage] = None
133
352
 
134
353
 
135
- class NewsItem(BaseModel):
136
- """News item."""
137
- title: str
138
- source: str
139
- url: str
140
- published: datetime
141
- summary: Optional[str] = None
142
- sentiment: Optional[str] = None
354
+ class ParameterSweepResult(BaseModel):
355
+ """Parameter optimization results."""
356
+ strategy_name: str
357
+ best_params: Dict[str, Any]
358
+ best_metric: float
359
+ optimization_metric: str
360
+ all_results: List[Dict[str, Any]]
361
+ stability_score: float
362
+ overfitting_warning: bool
363
+ train_sharpe: float
364
+ test_sharpe: float
365
+
366
+
367
+ # ============================================================================
368
+ # PORTFOLIO MODELS
369
+ # ============================================================================
370
+
371
+ class Position(BaseModel):
372
+ """Portfolio position."""
373
+ symbol: str
374
+ shares: float
375
+ entry_price: float
376
+ current_price: float
377
+ market_value: float
378
+ weight: float
379
+ pnl: float
380
+ pnl_pct: float
381
+
382
+
383
+ class PortfolioAllocation(BaseModel):
384
+ """Portfolio allocation."""
385
+ positions: Dict[str, float] # symbol -> weight
386
+ method: str # mean_variance, risk_parity, min_variance, etc.
387
+ expected_return: float
388
+ expected_volatility: float
389
+ expected_sharpe: float
390
+ constraints_applied: List[str]
391
+
392
+
393
+ class RiskBudget(BaseModel):
394
+ """Risk budget allocation."""
395
+ total_risk: float # Portfolio volatility target
396
+ position_risks: Dict[str, float] # symbol -> risk contribution
397
+ marginal_risks: Dict[str, float] # symbol -> marginal risk
398
+ diversification_ratio: float
399
+
143
400
 
401
+ class HedgeSuggestion(BaseModel):
402
+ """Hedge recommendation."""
403
+ hedge_type: str # index, sector, duration, volatility
404
+ instrument: str
405
+ hedge_ratio: float
406
+ expected_beta_reduction: float
407
+ cost_estimate: float
408
+ rationale: str
144
409
 
145
- class ResearchReport(BaseModel):
146
- """Research report."""
410
+
411
+ # ============================================================================
412
+ # REPORTING MODELS
413
+ # ============================================================================
414
+
415
+ class ResearchMemo(BaseModel):
416
+ """Research memo output."""
147
417
  title: str
148
- summary: str
149
- sections: list[dict[str, Any]]
150
- data_sources: list[str]
151
- symbols_analyzed: list[str]
152
- generated_at: datetime = Field(default_factory=datetime.now)
153
- confidence: float = 0.0
418
+ date: datetime
419
+ executive_summary: str
420
+ key_findings: List[str]
421
+ methodology: str
422
+ risks: List[str]
423
+ conclusion: str
424
+ what_would_change: List[str] # Factors that would change the conclusion
425
+ charts: List[str] # Paths to chart files
426
+ data_sources: List[DataLineage]
427
+ reproducibility_config: Dict[str, Any]
428
+
429
+
430
+ class Alert(BaseModel):
431
+ """Monitoring alert."""
432
+ timestamp: datetime
433
+ alert_type: str # signal, regime_shift, volume, drawdown, drift
434
+ symbol: str
435
+ message: str
436
+ severity: str # info, warning, critical
437
+ current_value: float
438
+ threshold: float
439
+ action_suggested: Optional[str] = None
440
+
441
+
442
+ # ============================================================================
443
+ # COMPARISON MODELS
444
+ # ============================================================================
445
+
446
+ class ComparisonResult(BaseModel):
447
+ """Multi-asset comparison result."""
448
+ assets: List[str]
449
+ period: str
450
+
451
+ # Performance comparison
452
+ performance_metrics: Dict[str, PerformanceMetrics]
453
+
454
+ # Risk comparison
455
+ correlation_matrix: Dict[str, Dict[str, float]]
456
+ beta_to_benchmark: Dict[str, float]
457
+
458
+ # Behavioral comparison
459
+ momentum_scores: Dict[str, float]
460
+ mean_reversion_scores: Dict[str, float]
461
+ regime_sensitivity: Dict[str, Dict[str, float]]
462
+
463
+ # Fundamental comparison (if applicable)
464
+ fundamental_comparison: Optional[Dict[str, Dict[str, float]]] = None
465
+
466
+ # Ranking
467
+ overall_ranking: List[str]
468
+ ranking_rationale: str
469
+ tradeoffs: List[str]
470
+
471
+
472
+ # ============================================================================
473
+ # UTILITY FUNCTIONS
474
+ # ============================================================================
475
+
476
+ def detect_asset_class(symbol: str) -> AssetClass:
477
+ """Auto-detect asset class from symbol."""
478
+ symbol = symbol.upper()
479
+
480
+ # Crypto patterns
481
+ if symbol.endswith(("USD", "USDT", "BTC", "ETH")) or symbol in ["BTC", "ETH", "SOL", "DOGE"]:
482
+ return AssetClass.CRYPTO
483
+
484
+ # Forex patterns
485
+ if len(symbol) == 6 and symbol[:3] in ["USD", "EUR", "GBP", "JPY", "CHF", "AUD", "CAD", "NZD"]:
486
+ return AssetClass.FOREX
487
+
488
+ # Futures patterns
489
+ if symbol.startswith(("/", "@")) or symbol.endswith(("F", "Z", "H", "M", "U")) and len(symbol) <= 5:
490
+ return AssetClass.FUTURE
491
+
492
+ # Options (simplified detection)
493
+ if len(symbol) > 10 and any(c.isdigit() for c in symbol):
494
+ return AssetClass.OPTION
495
+
496
+ # Index patterns
497
+ if symbol.startswith("^") or symbol in ["SPX", "NDX", "DJI", "VIX", "RUT"]:
498
+ return AssetClass.INDEX
499
+
500
+ # Common ETFs
501
+ etf_patterns = ["SPY", "QQQ", "IWM", "DIA", "VTI", "VOO", "XL", "IY", "VB", "VG"]
502
+ if any(symbol.startswith(p) for p in etf_patterns):
503
+ return AssetClass.ETF
504
+
505
+ # Default to equity
506
+ return AssetClass.EQUITY
507
+
508
+
509
+ def parse_time_period(period: str) -> tuple:
510
+ """Parse period string to start/end dates."""
511
+ from datetime import timedelta
512
+
513
+ today = date.today()
514
+ period = period.lower().strip()
515
+
516
+ period_map = {
517
+ "1d": timedelta(days=1),
518
+ "5d": timedelta(days=5),
519
+ "1w": timedelta(weeks=1),
520
+ "1mo": timedelta(days=30),
521
+ "3mo": timedelta(days=90),
522
+ "6mo": timedelta(days=180),
523
+ "1y": timedelta(days=365),
524
+ "2y": timedelta(days=730),
525
+ "5y": timedelta(days=1825),
526
+ "10y": timedelta(days=3650),
527
+ "ytd": None, # Special case
528
+ "mtd": None, # Special case
529
+ }
530
+
531
+ if period == "ytd":
532
+ return date(today.year, 1, 1), today
533
+ elif period == "mtd":
534
+ return date(today.year, today.month, 1), today
535
+ elif period in period_map:
536
+ return today - period_map[period], today
537
+ else:
538
+ # Try parsing as date range
539
+ if " to " in period:
540
+ parts = period.split(" to ")
541
+ return date.fromisoformat(parts[0]), date.fromisoformat(parts[1])
542
+
543
+ # Default to 2 years
544
+ return today - timedelta(days=730), today