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.
Files changed (38) hide show
  1. agentii_models-0.1.0/.gitignore +28 -0
  2. agentii_models-0.1.0/CHANGELOG.md +20 -0
  3. agentii_models-0.1.0/PKG-INFO +17 -0
  4. agentii_models-0.1.0/README.md +30 -0
  5. agentii_models-0.1.0/pyproject.toml +23 -0
  6. agentii_models-0.1.0/src/agentii_models/__init__.py +60 -0
  7. agentii_models-0.1.0/src/agentii_models/_base.py +39 -0
  8. agentii_models-0.1.0/src/agentii_models/analytics.py +87 -0
  9. agentii_models-0.1.0/src/agentii_models/biotech.py +59 -0
  10. agentii_models-0.1.0/src/agentii_models/enums.py +132 -0
  11. agentii_models-0.1.0/src/agentii_models/instruments.py +85 -0
  12. agentii_models-0.1.0/src/agentii_models/options.py +245 -0
  13. agentii_models-0.1.0/src/agentii_models/orders.py +57 -0
  14. agentii_models-0.1.0/src/agentii_models/portfolio.py +140 -0
  15. agentii_models-0.1.0/src/agentii_models/providers.py +90 -0
  16. agentii_models-0.1.0/src/agentii_models/py.typed +0 -0
  17. agentii_models-0.1.0/src/agentii_models/stocks.py +112 -0
  18. agentii_models-0.1.0/src/agentii_models/types.py +9 -0
  19. agentii_models-0.1.0/src/gis_models/__init__.py +60 -0
  20. agentii_models-0.1.0/src/gis_models/eia.py +59 -0
  21. agentii_models-0.1.0/src/gis_models/enums.py +104 -0
  22. agentii_models-0.1.0/src/gis_models/graph.py +39 -0
  23. agentii_models-0.1.0/src/gis_models/vessels.py +79 -0
  24. agentii_models-0.1.0/src/gis_models/weather.py +90 -0
  25. agentii_models-0.1.0/tests/__init__.py +0 -0
  26. agentii_models-0.1.0/tests/conftest.py +102 -0
  27. agentii_models-0.1.0/tests/test_analytics.py +146 -0
  28. agentii_models-0.1.0/tests/test_biotech.py +142 -0
  29. agentii_models-0.1.0/tests/test_edge_cases.py +292 -0
  30. agentii_models-0.1.0/tests/test_enums.py +162 -0
  31. agentii_models-0.1.0/tests/test_gis_models.py +371 -0
  32. agentii_models-0.1.0/tests/test_instruments.py +140 -0
  33. agentii_models-0.1.0/tests/test_options.py +291 -0
  34. agentii_models-0.1.0/tests/test_orders.py +130 -0
  35. agentii_models-0.1.0/tests/test_portfolio.py +204 -0
  36. agentii_models-0.1.0/tests/test_providers.py +124 -0
  37. agentii_models-0.1.0/tests/test_serialization.py +360 -0
  38. 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