agentii-models 0.1.0__tar.gz
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.
- agentii_models-0.1.0/.gitignore +28 -0
- agentii_models-0.1.0/CHANGELOG.md +20 -0
- agentii_models-0.1.0/PKG-INFO +17 -0
- agentii_models-0.1.0/README.md +30 -0
- agentii_models-0.1.0/pyproject.toml +23 -0
- agentii_models-0.1.0/src/agentii_models/__init__.py +60 -0
- agentii_models-0.1.0/src/agentii_models/_base.py +39 -0
- agentii_models-0.1.0/src/agentii_models/analytics.py +87 -0
- agentii_models-0.1.0/src/agentii_models/biotech.py +59 -0
- agentii_models-0.1.0/src/agentii_models/enums.py +132 -0
- agentii_models-0.1.0/src/agentii_models/instruments.py +85 -0
- agentii_models-0.1.0/src/agentii_models/options.py +245 -0
- agentii_models-0.1.0/src/agentii_models/orders.py +57 -0
- agentii_models-0.1.0/src/agentii_models/portfolio.py +140 -0
- agentii_models-0.1.0/src/agentii_models/providers.py +90 -0
- agentii_models-0.1.0/src/agentii_models/py.typed +0 -0
- agentii_models-0.1.0/src/agentii_models/stocks.py +112 -0
- agentii_models-0.1.0/src/agentii_models/types.py +9 -0
- agentii_models-0.1.0/src/gis_models/__init__.py +60 -0
- agentii_models-0.1.0/src/gis_models/eia.py +59 -0
- agentii_models-0.1.0/src/gis_models/enums.py +104 -0
- agentii_models-0.1.0/src/gis_models/graph.py +39 -0
- agentii_models-0.1.0/src/gis_models/vessels.py +79 -0
- agentii_models-0.1.0/src/gis_models/weather.py +90 -0
- agentii_models-0.1.0/tests/__init__.py +0 -0
- agentii_models-0.1.0/tests/conftest.py +102 -0
- agentii_models-0.1.0/tests/test_analytics.py +146 -0
- agentii_models-0.1.0/tests/test_biotech.py +142 -0
- agentii_models-0.1.0/tests/test_edge_cases.py +292 -0
- agentii_models-0.1.0/tests/test_enums.py +162 -0
- agentii_models-0.1.0/tests/test_gis_models.py +371 -0
- agentii_models-0.1.0/tests/test_instruments.py +140 -0
- agentii_models-0.1.0/tests/test_options.py +291 -0
- agentii_models-0.1.0/tests/test_orders.py +130 -0
- agentii_models-0.1.0/tests/test_portfolio.py +204 -0
- agentii_models-0.1.0/tests/test_providers.py +124 -0
- agentii_models-0.1.0/tests/test_serialization.py +360 -0
- agentii_models-0.1.0/tests/test_stocks.py +253 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
|
|
4
|
+
# Rust build artifacts
|
|
5
|
+
target/
|
|
6
|
+
|
|
7
|
+
# Environment files
|
|
8
|
+
.env*
|
|
9
|
+
!.env.example
|
|
10
|
+
|
|
11
|
+
# Internal agent data
|
|
12
|
+
.backend/.agentii/sessions/
|
|
13
|
+
backend/.agentii/
|
|
14
|
+
backend/crates/.clawd-agents/
|
|
15
|
+
.claw/
|
|
16
|
+
|
|
17
|
+
# IDE
|
|
18
|
+
.idea/
|
|
19
|
+
.vscode/
|
|
20
|
+
*.swp
|
|
21
|
+
*.swo
|
|
22
|
+
|
|
23
|
+
# OS
|
|
24
|
+
.DS_Store
|
|
25
|
+
Thumbs.db
|
|
26
|
+
|
|
27
|
+
# Tauri
|
|
28
|
+
frontend/src-tauri/target/
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `agentii-models` follow field-level semantic versioning.
|
|
4
|
+
|
|
5
|
+
- **MAJOR**: field removal, field rename, or field type change.
|
|
6
|
+
- **MINOR**: new Optional field added.
|
|
7
|
+
- **PATCH**: validator fixes, docstring updates, internal refactors.
|
|
8
|
+
|
|
9
|
+
## [0.1.0] — 2026-07-05
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Initial release: Pydantic v2 models for US stocks, options, biotech catalysts.
|
|
14
|
+
- Modules: `instruments`, `stocks`, `options`, `analytics`, `portfolio`, `orders`, `biotech`, `providers`, `enums`, `types`, `_base`.
|
|
15
|
+
- GIS models: `eia`, `enums`, `graph`, `vessels`, `weather`.
|
|
16
|
+
- Shared enums: `AssetClass`, `Exchange`, `OptionType`, `OptionStyle`, `BarTimeframe`, `OrderSide`, `OrderType`, `OrderStatus`, `TimeInForce`, `CatalystType`, `FDADecisionOutcome`, `PricingModel`, `MarketSession`, `DataFeed`, `Adjustment`.
|
|
17
|
+
- Provider abstraction: `MarketDataProvider` ABC, `DataRouter`.
|
|
18
|
+
- Apache-2.0 license.
|
|
19
|
+
|
|
20
|
+
[0.1.0]: https://pypi.org/project/agentii-models/0.1.0/
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agentii-models
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Shared Pydantic v2 data models for the Agentii financial IDE — US stocks, options, biotech catalysts, portfolio, and provider abstraction.
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Requires-Dist: pydantic>=2.6
|
|
8
|
+
Provides-Extra: all
|
|
9
|
+
Requires-Dist: orjson>=3.9; extra == 'all'
|
|
10
|
+
Requires-Dist: pyarrow>=14.0; extra == 'all'
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: pytest-cov>=4.1; extra == 'dev'
|
|
13
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
14
|
+
Provides-Extra: json
|
|
15
|
+
Requires-Dist: orjson>=3.9; extra == 'json'
|
|
16
|
+
Provides-Extra: parquet
|
|
17
|
+
Requires-Dist: pyarrow>=14.0; extra == 'parquet'
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# agentii-models
|
|
2
|
+
|
|
3
|
+
Shared Pydantic v2 data models for the [Agentii financial IDE](https://github.com/agentii-ai/agentii) — US stocks, options, biotech catalysts, portfolio positions, order execution, and provider abstraction.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install agentii-models
|
|
9
|
+
|
|
10
|
+
# With orjson for fast JSON serialization
|
|
11
|
+
pip install "agentii-models[json]"
|
|
12
|
+
|
|
13
|
+
# With pyarrow for Parquet support
|
|
14
|
+
pip install "agentii-models[parquet]"
|
|
15
|
+
|
|
16
|
+
# Development
|
|
17
|
+
pip install -e ".[dev]"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from agentii_models import StockBar, OptionQuote, Greeks, OptionsChain
|
|
24
|
+
from agentii_models.enums import AssetClass, BarTimeframe, OptionType
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Requirements
|
|
28
|
+
|
|
29
|
+
- Python 3.12+
|
|
30
|
+
- Pydantic v2.6+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "agentii-models"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Shared Pydantic v2 data models for the Agentii financial IDE — US stocks, options, biotech catalysts, portfolio, and provider abstraction."
|
|
9
|
+
requires-python = ">=3.12"
|
|
10
|
+
license = "Apache-2.0"
|
|
11
|
+
dependencies = ["pydantic>=2.6"]
|
|
12
|
+
|
|
13
|
+
[project.optional-dependencies]
|
|
14
|
+
json = ["orjson>=3.9"]
|
|
15
|
+
parquet = ["pyarrow>=14.0"]
|
|
16
|
+
dev = ["pytest>=8.0", "pytest-cov>=4.1"]
|
|
17
|
+
all = ["agentii-models[json,parquet]"]
|
|
18
|
+
|
|
19
|
+
[tool.hatch.build.targets.wheel]
|
|
20
|
+
packages = ["src/agentii_models", "src/gis_models"]
|
|
21
|
+
|
|
22
|
+
[tool.pytest.ini_options]
|
|
23
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""agentii-models: Shared Pydantic v2 data models for Agentii and Agenzym."""
|
|
2
|
+
|
|
3
|
+
# Instruments
|
|
4
|
+
from .instruments import Instrument, EquityInstrument, OptionInstrument
|
|
5
|
+
|
|
6
|
+
# Stocks
|
|
7
|
+
from .stocks import StockTick, StockQuote, StockBar, StockSnapshot
|
|
8
|
+
|
|
9
|
+
# Options
|
|
10
|
+
from .options import Greeks, OptionQuote, OptionBar, OptionsChain
|
|
11
|
+
|
|
12
|
+
# Analytics
|
|
13
|
+
from .analytics import IVPoint, VolatilitySurface, OptionPricingResult, PayoffDiagram
|
|
14
|
+
|
|
15
|
+
# Portfolio
|
|
16
|
+
from .portfolio import StockPosition, OptionPosition, Portfolio
|
|
17
|
+
|
|
18
|
+
# Orders
|
|
19
|
+
from .orders import Order, Trade
|
|
20
|
+
|
|
21
|
+
# Biotech
|
|
22
|
+
from .biotech import CatalystEvent, FDADecision
|
|
23
|
+
|
|
24
|
+
# Providers
|
|
25
|
+
from .providers import MarketDataProvider, DataRouter
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
# Instruments
|
|
29
|
+
"Instrument",
|
|
30
|
+
"EquityInstrument",
|
|
31
|
+
"OptionInstrument",
|
|
32
|
+
# Stocks
|
|
33
|
+
"StockTick",
|
|
34
|
+
"StockQuote",
|
|
35
|
+
"StockBar",
|
|
36
|
+
"StockSnapshot",
|
|
37
|
+
# Options
|
|
38
|
+
"Greeks",
|
|
39
|
+
"OptionQuote",
|
|
40
|
+
"OptionBar",
|
|
41
|
+
"OptionsChain",
|
|
42
|
+
# Analytics
|
|
43
|
+
"IVPoint",
|
|
44
|
+
"VolatilitySurface",
|
|
45
|
+
"OptionPricingResult",
|
|
46
|
+
"PayoffDiagram",
|
|
47
|
+
# Portfolio
|
|
48
|
+
"StockPosition",
|
|
49
|
+
"OptionPosition",
|
|
50
|
+
"Portfolio",
|
|
51
|
+
# Orders
|
|
52
|
+
"Order",
|
|
53
|
+
"Trade",
|
|
54
|
+
# Biotech
|
|
55
|
+
"CatalystEvent",
|
|
56
|
+
"FDADecision",
|
|
57
|
+
# Providers
|
|
58
|
+
"MarketDataProvider",
|
|
59
|
+
"DataRouter",
|
|
60
|
+
]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Base market data model shared by all market data types."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict, field_validator
|
|
4
|
+
|
|
5
|
+
from .types import ProviderName, Symbol, Timestamp
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BaseMarketData(BaseModel):
|
|
9
|
+
model_config = ConfigDict(
|
|
10
|
+
frozen=True,
|
|
11
|
+
populate_by_name=True,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
symbol: Symbol
|
|
15
|
+
provider: ProviderName
|
|
16
|
+
timestamp_ns: Timestamp | None = None
|
|
17
|
+
|
|
18
|
+
@field_validator("symbol", mode="before")
|
|
19
|
+
@classmethod
|
|
20
|
+
def _validate_symbol(cls, v: str) -> str:
|
|
21
|
+
v = str(v).strip().upper()
|
|
22
|
+
if not v:
|
|
23
|
+
raise ValueError("symbol must be non-empty")
|
|
24
|
+
return v
|
|
25
|
+
|
|
26
|
+
@field_validator("provider", mode="before")
|
|
27
|
+
@classmethod
|
|
28
|
+
def _validate_provider(cls, v: str) -> str:
|
|
29
|
+
v = str(v).strip()
|
|
30
|
+
if not v:
|
|
31
|
+
raise ValueError("provider must be non-empty")
|
|
32
|
+
return v
|
|
33
|
+
|
|
34
|
+
@field_validator("timestamp_ns")
|
|
35
|
+
@classmethod
|
|
36
|
+
def _validate_timestamp_ns(cls, v: int | None) -> int | None:
|
|
37
|
+
if v is not None and v < 0:
|
|
38
|
+
raise ValueError("timestamp_ns must be non-negative")
|
|
39
|
+
return v
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Analytics models: IVPoint, VolatilitySurface, OptionPricingResult, PayoffDiagram.
|
|
2
|
+
|
|
3
|
+
Structural reference: VeighNa vnpy_optionmaster/pricing/black_scholes.py
|
|
4
|
+
- calculate_greeks(s, k, r, t, v, cp) → (price, delta, gamma, theta, vega)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from datetime import date, datetime
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, ConfigDict
|
|
10
|
+
|
|
11
|
+
from .enums import OptionType, PricingModel
|
|
12
|
+
from .types import Price, ProviderName, Symbol
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class IVPoint(BaseModel):
|
|
16
|
+
model_config = ConfigDict(frozen=True)
|
|
17
|
+
|
|
18
|
+
underlying_symbol: Symbol
|
|
19
|
+
strike: Price
|
|
20
|
+
expiration: date
|
|
21
|
+
option_type: OptionType
|
|
22
|
+
implied_volatility: float
|
|
23
|
+
timestamp: datetime
|
|
24
|
+
provider: ProviderName
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class VolatilitySurface(BaseModel):
|
|
28
|
+
"""3D IV surface: strike × expiry × IV."""
|
|
29
|
+
|
|
30
|
+
model_config = ConfigDict(frozen=True)
|
|
31
|
+
|
|
32
|
+
underlying_symbol: Symbol
|
|
33
|
+
snapshot_time: datetime
|
|
34
|
+
provider: ProviderName
|
|
35
|
+
points: list[IVPoint]
|
|
36
|
+
|
|
37
|
+
def iv_at(self, strike: float, expiration: date) -> float | None:
|
|
38
|
+
for p in self.points:
|
|
39
|
+
if p.strike == strike and p.expiration == expiration:
|
|
40
|
+
return p.implied_volatility
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
def smile(self, expiration: date) -> list[IVPoint]:
|
|
44
|
+
return sorted(
|
|
45
|
+
[p for p in self.points if p.expiration == expiration],
|
|
46
|
+
key=lambda p: p.strike,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def term_structure(self, strike: float) -> list[IVPoint]:
|
|
50
|
+
return sorted(
|
|
51
|
+
[p for p in self.points if p.strike == strike],
|
|
52
|
+
key=lambda p: p.expiration,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class OptionPricingResult(BaseModel):
|
|
57
|
+
"""Output of a pricing model calculation."""
|
|
58
|
+
|
|
59
|
+
model_config = ConfigDict(frozen=True)
|
|
60
|
+
|
|
61
|
+
model: PricingModel
|
|
62
|
+
theoretical_price: Price
|
|
63
|
+
delta: float
|
|
64
|
+
gamma: float
|
|
65
|
+
theta: float
|
|
66
|
+
vega: float
|
|
67
|
+
rho: float
|
|
68
|
+
implied_volatility: float
|
|
69
|
+
underlying_price: Price
|
|
70
|
+
strike: Price
|
|
71
|
+
risk_free_rate: float
|
|
72
|
+
time_to_expiry: float
|
|
73
|
+
option_type: OptionType
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class PayoffDiagram(BaseModel):
|
|
77
|
+
"""Strategy payoff visualization data."""
|
|
78
|
+
|
|
79
|
+
model_config = ConfigDict(frozen=True)
|
|
80
|
+
|
|
81
|
+
strategy_name: str
|
|
82
|
+
underlying_symbol: Symbol
|
|
83
|
+
price_points: list[float]
|
|
84
|
+
payoff_values: list[float]
|
|
85
|
+
breakeven_points: list[float]
|
|
86
|
+
max_profit: float | None = None
|
|
87
|
+
max_loss: float | None = None
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Biotech-specific models: CatalystEvent, FDADecision."""
|
|
2
|
+
|
|
3
|
+
from datetime import date, datetime
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, computed_field, field_validator
|
|
6
|
+
|
|
7
|
+
from .enums import CatalystType, FDADecisionOutcome
|
|
8
|
+
from .types import Price, Symbol
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CatalystEvent(BaseModel):
|
|
12
|
+
event_id: str
|
|
13
|
+
symbol: Symbol
|
|
14
|
+
drug_name: str
|
|
15
|
+
indication: str
|
|
16
|
+
catalyst_type: CatalystType
|
|
17
|
+
event_date: date | None = None
|
|
18
|
+
date_is_estimated: bool = False
|
|
19
|
+
approval_probability: float | None = None
|
|
20
|
+
expected_move_pct: float | None = None
|
|
21
|
+
historical_precedent: str | None = None
|
|
22
|
+
source: str | None = None
|
|
23
|
+
notes: str | None = None
|
|
24
|
+
created_at: datetime
|
|
25
|
+
updated_at: datetime | None = None
|
|
26
|
+
|
|
27
|
+
@field_validator("approval_probability")
|
|
28
|
+
@classmethod
|
|
29
|
+
def _validate_probability(cls, v: float | None) -> float | None:
|
|
30
|
+
if v is not None and not (0.0 <= v <= 1.0):
|
|
31
|
+
raise ValueError("approval_probability must be between 0.0 and 1.0")
|
|
32
|
+
return v
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class FDADecision(BaseModel):
|
|
36
|
+
model_config = ConfigDict(frozen=True)
|
|
37
|
+
|
|
38
|
+
decision_id: str
|
|
39
|
+
symbol: Symbol
|
|
40
|
+
drug_name: str
|
|
41
|
+
indication: str
|
|
42
|
+
outcome: FDADecisionOutcome
|
|
43
|
+
decision_date: date
|
|
44
|
+
catalyst_event_id: str | None = None
|
|
45
|
+
price_before: Price | None = None
|
|
46
|
+
price_after: Price | None = None
|
|
47
|
+
review_type: str | None = None
|
|
48
|
+
label_expansion: bool = False
|
|
49
|
+
advisory_committee_vote: str | None = None
|
|
50
|
+
crl_reasons: list[str] | None = None
|
|
51
|
+
source_url: str | None = None
|
|
52
|
+
decision_letter_url: str | None = None
|
|
53
|
+
|
|
54
|
+
@computed_field # type: ignore[prop-decorator]
|
|
55
|
+
@property
|
|
56
|
+
def actual_move_pct(self) -> float | None:
|
|
57
|
+
if self.price_before is not None and self.price_after is not None and self.price_before != 0:
|
|
58
|
+
return (self.price_after - self.price_before) / self.price_before * 100
|
|
59
|
+
return None
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""Shared enums for agentii-models."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AssetClass(str, Enum):
|
|
7
|
+
EQUITY = "equity"
|
|
8
|
+
OPTION = "option"
|
|
9
|
+
ETF = "etf"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Exchange(str, Enum):
|
|
13
|
+
NYSE = "NYSE"
|
|
14
|
+
NASDAQ = "NASDAQ"
|
|
15
|
+
CBOE = "CBOE"
|
|
16
|
+
AMEX = "AMEX"
|
|
17
|
+
ARCA = "ARCA"
|
|
18
|
+
BATS = "BATS"
|
|
19
|
+
IEX = "IEX"
|
|
20
|
+
OTC = "OTC"
|
|
21
|
+
OPRA = "OPRA"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class OptionType(str, Enum):
|
|
25
|
+
CALL = "call"
|
|
26
|
+
PUT = "put"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class OptionStyle(str, Enum):
|
|
30
|
+
AMERICAN = "american"
|
|
31
|
+
EUROPEAN = "european"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class BarTimeframe(str, Enum):
|
|
35
|
+
TICK = "tick"
|
|
36
|
+
SEC_1 = "1s"
|
|
37
|
+
MIN_1 = "1min"
|
|
38
|
+
MIN_5 = "5min"
|
|
39
|
+
MIN_15 = "15min"
|
|
40
|
+
MIN_30 = "30min"
|
|
41
|
+
HOUR_1 = "1h"
|
|
42
|
+
HOUR_4 = "4h"
|
|
43
|
+
DAY_1 = "1d"
|
|
44
|
+
WEEK_1 = "1w"
|
|
45
|
+
MONTH_1 = "1mo"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class MarketSession(str, Enum):
|
|
49
|
+
PRE_MARKET = "pre"
|
|
50
|
+
REGULAR = "regular"
|
|
51
|
+
POST_MARKET = "post"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class OrderSide(str, Enum):
|
|
55
|
+
BUY = "buy"
|
|
56
|
+
SELL = "sell"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class OrderType(str, Enum):
|
|
60
|
+
MARKET = "market"
|
|
61
|
+
LIMIT = "limit"
|
|
62
|
+
STOP = "stop"
|
|
63
|
+
STOP_LIMIT = "stop_limit"
|
|
64
|
+
TRAILING_STOP = "trailing_stop"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class OrderStatus(str, Enum):
|
|
68
|
+
PENDING = "pending"
|
|
69
|
+
ACCEPTED = "accepted"
|
|
70
|
+
PARTIALLY_FILLED = "partially_filled"
|
|
71
|
+
FILLED = "filled"
|
|
72
|
+
CANCELLED = "cancelled"
|
|
73
|
+
REJECTED = "rejected"
|
|
74
|
+
EXPIRED = "expired"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class TimeInForce(str, Enum):
|
|
78
|
+
DAY = "day"
|
|
79
|
+
GTC = "gtc"
|
|
80
|
+
IOC = "ioc"
|
|
81
|
+
FOK = "fok"
|
|
82
|
+
OPG = "opg"
|
|
83
|
+
CLS = "cls"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class CatalystType(str, Enum):
|
|
87
|
+
PDUFA = "pdufa"
|
|
88
|
+
ADCOM = "adcom"
|
|
89
|
+
PHASE_1 = "phase_1"
|
|
90
|
+
PHASE_2 = "phase_2"
|
|
91
|
+
PHASE_3 = "phase_3"
|
|
92
|
+
NDA_FILING = "nda_filing"
|
|
93
|
+
BLA_FILING = "bla_filing"
|
|
94
|
+
CONFERENCE = "conference"
|
|
95
|
+
EARNINGS = "earnings"
|
|
96
|
+
DATA_READOUT = "data_readout"
|
|
97
|
+
PRIORITY_REVIEW = "priority_review"
|
|
98
|
+
BREAKTHROUGH = "breakthrough"
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class FDADecisionOutcome(str, Enum):
|
|
102
|
+
APPROVED = "approved"
|
|
103
|
+
CRL = "crl"
|
|
104
|
+
TENTATIVE_APPROVAL = "tentative_approval"
|
|
105
|
+
REFUSED_TO_FILE = "refused_to_file"
|
|
106
|
+
WITHDRAWN = "withdrawn"
|
|
107
|
+
PENDING = "pending"
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class DataFeed(str, Enum):
|
|
111
|
+
"""Alpaca data feed source. Affects data quality, latency, and cost."""
|
|
112
|
+
|
|
113
|
+
SIP = "sip"
|
|
114
|
+
IEX = "iex"
|
|
115
|
+
OTC = "otc"
|
|
116
|
+
BOATS = "boats"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class Adjustment(str, Enum):
|
|
120
|
+
"""Price adjustment type for historical bars.
|
|
121
|
+
Critical for 10-year historical data accuracy."""
|
|
122
|
+
|
|
123
|
+
RAW = "raw"
|
|
124
|
+
SPLIT = "split"
|
|
125
|
+
DIVIDEND = "dividend"
|
|
126
|
+
ALL = "all"
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class PricingModel(str, Enum):
|
|
130
|
+
BLACK_SCHOLES = "black_scholes"
|
|
131
|
+
BLACK_76 = "black_76"
|
|
132
|
+
BINOMIAL_TREE = "binomial_tree"
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Instrument hierarchy: Instrument, EquityInstrument, OptionInstrument."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from datetime import date
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, computed_field, field_validator
|
|
8
|
+
|
|
9
|
+
from .enums import AssetClass, Exchange, OptionType, OptionStyle
|
|
10
|
+
from .types import Symbol
|
|
11
|
+
|
|
12
|
+
# OCC symbol: 1-6 uppercase letters (right-padded with spaces to 6), 6-digit date YYMMDD, C or P, 8-digit strike
|
|
13
|
+
_OCC_PATTERN = re.compile(r"^[A-Z]{1,6}\s*\d{6}[CP]\d{8}$")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Instrument(BaseModel):
|
|
17
|
+
model_config = ConfigDict(frozen=True)
|
|
18
|
+
|
|
19
|
+
symbol: Symbol
|
|
20
|
+
asset_class: AssetClass
|
|
21
|
+
exchange: Exchange
|
|
22
|
+
name: str
|
|
23
|
+
currency: str = "USD"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class EquityInstrument(Instrument):
|
|
27
|
+
asset_class: Literal[AssetClass.EQUITY] = AssetClass.EQUITY
|
|
28
|
+
|
|
29
|
+
sector: str | None = None
|
|
30
|
+
industry: str | None = None
|
|
31
|
+
market_cap: float | None = None
|
|
32
|
+
has_fda_pipeline: bool = False
|
|
33
|
+
clinical_stage: str | None = None
|
|
34
|
+
is_biotech: bool = False
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class OptionInstrument(Instrument):
|
|
38
|
+
asset_class: Literal[AssetClass.OPTION] = AssetClass.OPTION
|
|
39
|
+
|
|
40
|
+
underlying_symbol: Symbol
|
|
41
|
+
strike_price: float
|
|
42
|
+
expiration_date: date
|
|
43
|
+
option_type: OptionType
|
|
44
|
+
option_style: OptionStyle = OptionStyle.AMERICAN
|
|
45
|
+
contract_size: int = 100
|
|
46
|
+
|
|
47
|
+
@field_validator("symbol")
|
|
48
|
+
@classmethod
|
|
49
|
+
def _validate_occ_symbol(cls, v: str) -> str:
|
|
50
|
+
if not _OCC_PATTERN.match(v):
|
|
51
|
+
raise ValueError(
|
|
52
|
+
f"Invalid OCC symbol format: '{v}'. "
|
|
53
|
+
"Expected: 1-6 uppercase letters + 6-digit date YYMMDD + C/P + 8-digit strike"
|
|
54
|
+
)
|
|
55
|
+
return v
|
|
56
|
+
|
|
57
|
+
@field_validator("strike_price")
|
|
58
|
+
@classmethod
|
|
59
|
+
def _validate_strike(cls, v: float) -> float:
|
|
60
|
+
if v <= 0:
|
|
61
|
+
raise ValueError("strike_price must be positive")
|
|
62
|
+
return v
|
|
63
|
+
|
|
64
|
+
@field_validator("contract_size")
|
|
65
|
+
@classmethod
|
|
66
|
+
def _validate_contract_size(cls, v: int) -> int:
|
|
67
|
+
if v <= 0:
|
|
68
|
+
raise ValueError("contract_size must be positive")
|
|
69
|
+
return v
|
|
70
|
+
|
|
71
|
+
@computed_field # type: ignore[prop-decorator]
|
|
72
|
+
@property
|
|
73
|
+
def days_to_expiry(self) -> int:
|
|
74
|
+
return (self.expiration_date - date.today()).days
|
|
75
|
+
|
|
76
|
+
@computed_field # type: ignore[prop-decorator]
|
|
77
|
+
@property
|
|
78
|
+
def is_expired(self) -> bool:
|
|
79
|
+
return self.expiration_date < date.today()
|
|
80
|
+
|
|
81
|
+
def moneyness(self, underlying_price: float) -> float:
|
|
82
|
+
"""Return strike / underlying price ratio."""
|
|
83
|
+
if underlying_price <= 0:
|
|
84
|
+
raise ValueError("underlying_price must be positive")
|
|
85
|
+
return self.strike_price / underlying_price
|