fmp-data 0.0.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.
Files changed (84) hide show
  1. fmp_data/__init__.py +138 -0
  2. fmp_data/alternative/__init__.py +34 -0
  3. fmp_data/alternative/client.py +150 -0
  4. fmp_data/alternative/endpoints.py +533 -0
  5. fmp_data/alternative/mapping.py +737 -0
  6. fmp_data/alternative/models.py +296 -0
  7. fmp_data/alternative/schema.py +165 -0
  8. fmp_data/base.py +316 -0
  9. fmp_data/client.py +269 -0
  10. fmp_data/company/__init__.py +40 -0
  11. fmp_data/company/client.py +238 -0
  12. fmp_data/company/endpoints.py +711 -0
  13. fmp_data/company/hints.py +77 -0
  14. fmp_data/company/mapping.py +1361 -0
  15. fmp_data/company/models.py +543 -0
  16. fmp_data/company/schema.py +132 -0
  17. fmp_data/config.py +207 -0
  18. fmp_data/economics/__init__.py +16 -0
  19. fmp_data/economics/client.py +52 -0
  20. fmp_data/economics/endpoints.py +164 -0
  21. fmp_data/economics/mapping.py +641 -0
  22. fmp_data/economics/models.py +75 -0
  23. fmp_data/economics/schema.py +91 -0
  24. fmp_data/exceptions.py +54 -0
  25. fmp_data/fundamental/__init__.py +40 -0
  26. fmp_data/fundamental/client.py +87 -0
  27. fmp_data/fundamental/endpoints.py +403 -0
  28. fmp_data/fundamental/mapping.py +867 -0
  29. fmp_data/fundamental/models.py +913 -0
  30. fmp_data/fundamental/schema.py +40 -0
  31. fmp_data/institutional/__init__.py +26 -0
  32. fmp_data/institutional/client.py +141 -0
  33. fmp_data/institutional/endpoints.py +321 -0
  34. fmp_data/institutional/mapping.py +749 -0
  35. fmp_data/institutional/models.py +301 -0
  36. fmp_data/institutional/schema.py +99 -0
  37. fmp_data/intelligence/__init__.py +58 -0
  38. fmp_data/intelligence/client.py +331 -0
  39. fmp_data/intelligence/endpoints.py +788 -0
  40. fmp_data/intelligence/mapping.py +1677 -0
  41. fmp_data/intelligence/models.py +707 -0
  42. fmp_data/intelligence/schema.py +57 -0
  43. fmp_data/investment/__init__.py +24 -0
  44. fmp_data/investment/client.py +104 -0
  45. fmp_data/investment/endpoints.py +241 -0
  46. fmp_data/investment/mapping.py +658 -0
  47. fmp_data/investment/models.py +220 -0
  48. fmp_data/investment/schema.py +106 -0
  49. fmp_data/lc/__init__.py +256 -0
  50. fmp_data/lc/config.py +88 -0
  51. fmp_data/lc/embedding.py +140 -0
  52. fmp_data/lc/hints.py +66 -0
  53. fmp_data/lc/mapping.py +107 -0
  54. fmp_data/lc/models.py +98 -0
  55. fmp_data/lc/registry.py +693 -0
  56. fmp_data/lc/utils.py +35 -0
  57. fmp_data/lc/validation.py +267 -0
  58. fmp_data/lc/vector_store.py +592 -0
  59. fmp_data/logger.py +358 -0
  60. fmp_data/market/__init__.py +18 -0
  61. fmp_data/market/client.py +106 -0
  62. fmp_data/market/endpoints.py +358 -0
  63. fmp_data/market/hints.py +22 -0
  64. fmp_data/market/mapping.py +854 -0
  65. fmp_data/market/models.py +310 -0
  66. fmp_data/market/schema.py +186 -0
  67. fmp_data/mcp/__init__.py +0 -0
  68. fmp_data/mcp/server.py +101 -0
  69. fmp_data/mcp/tool_loader.py +74 -0
  70. fmp_data/mcp/tools_manifest.py +17 -0
  71. fmp_data/models.py +265 -0
  72. fmp_data/rate_limit.py +136 -0
  73. fmp_data/schema.py +158 -0
  74. fmp_data/technical/__init__.py +28 -0
  75. fmp_data/technical/client.py +214 -0
  76. fmp_data/technical/endpoints.py +102 -0
  77. fmp_data/technical/mapping.py +452 -0
  78. fmp_data/technical/models.py +87 -0
  79. fmp_data/technical/schema.py +261 -0
  80. fmp_data-0.0.0.dist-info/METADATA +732 -0
  81. fmp_data-0.0.0.dist-info/RECORD +84 -0
  82. fmp_data-0.0.0.dist-info/WHEEL +4 -0
  83. fmp_data-0.0.0.dist-info/entry_points.txt +10 -0
  84. fmp_data-0.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,296 @@
1
+ # fmp_data/alternative/models.py
2
+
3
+ import datetime
4
+ import warnings
5
+ from datetime import date
6
+ from typing import Any
7
+ from zoneinfo import ZoneInfo
8
+
9
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
10
+ from pydantic.alias_generators import to_camel
11
+
12
+ UTC = ZoneInfo("UTC")
13
+
14
+ default_model_config = ConfigDict(
15
+ populate_by_name=True,
16
+ validate_assignment=True,
17
+ str_strip_whitespace=True,
18
+ extra="allow",
19
+ alias_generator=to_camel,
20
+ )
21
+
22
+
23
+ # Base Models
24
+ class PriceQuote(BaseModel):
25
+ """Base model for price quotes"""
26
+
27
+ model_config = ConfigDict(populate_by_name=True)
28
+
29
+ # Required fields
30
+ symbol: str = Field(description="Trading symbol")
31
+ price: float | None = Field(None, description="Current price")
32
+ change: float | None = Field(None, description="Price change")
33
+ change_percent: float | None = Field(
34
+ alias="changesPercentage", description="Percent change"
35
+ )
36
+
37
+ # Optional fields
38
+ name: str | None = Field(None, description="Symbol name or pair name")
39
+
40
+ # Day range
41
+ day_low: float | None = Field(None, alias="dayLow", description="Day low price")
42
+ day_high: float | None = Field(None, alias="dayHigh", description="Day high price")
43
+
44
+ # Year range
45
+ year_high: float | None = Field(None, alias="yearHigh", description="52-week high")
46
+ year_low: float | None = Field(None, alias="yearLow", description="52-week low")
47
+
48
+ # Moving averages
49
+ price_avg_50: float | None = Field(
50
+ None, alias="priceAvg50", description="50-day average"
51
+ )
52
+ price_avg_200: float | None = Field(
53
+ None, alias="priceAvg200", description="200-day average"
54
+ )
55
+
56
+ # Volume
57
+ volume: float | None = Field(None, description="Trading volume")
58
+ avg_volume: float | None = Field(
59
+ None, alias="avgVolume", description="Average volume"
60
+ )
61
+
62
+ # Other price points
63
+ open: float | None = Field(None, description="Opening price")
64
+ previous_close: float | None = Field(
65
+ None, alias="previousClose", description="Previous close"
66
+ )
67
+
68
+ # Market data
69
+ market_cap: float | None = Field(
70
+ None, alias="marketCap", description="Market capitalization"
71
+ )
72
+ eps: float | None = Field(None, description="Earnings per share")
73
+ pe: float | None = Field(None, description="Price to earnings ratio")
74
+ shares_outstanding: float | None = Field(
75
+ None, alias="sharesOutstanding", description="Shares outstanding"
76
+ )
77
+ earnings_announcement: datetime.datetime | None = Field(
78
+ None, alias="earningsAnnouncement", description="Next earnings date"
79
+ )
80
+ exchange: str | None = Field(None, description="Exchange name")
81
+
82
+ timestamp: datetime.datetime | None = Field(
83
+ None,
84
+ description="Quote timestamp",
85
+ json_schema_extra={"format": "unix-timestamp"},
86
+ )
87
+
88
+ @field_validator("timestamp", mode="before")
89
+ def parse_timestamp(cls, value: Any) -> datetime.datetime | None:
90
+ if value is None:
91
+ return None
92
+
93
+ try:
94
+ if isinstance(value, str | int | float):
95
+ timestamp = float(value)
96
+ else:
97
+ raise ValueError(f"Unexpected type for timestamp: {type(value)}")
98
+
99
+ return datetime.datetime.fromtimestamp(timestamp, tz=UTC)
100
+ except Exception as e:
101
+ warnings.warn(f"Failed to parse timestamp {value}: {e}", stacklevel=2)
102
+ return None
103
+
104
+
105
+ class HistoricalPrice(BaseModel):
106
+ """Base model for historical price data"""
107
+
108
+ price_date: date = Field(
109
+ description="The date of the historical record", alias="date"
110
+ )
111
+ open: float = Field(description="Opening price")
112
+ high: float = Field(description="Highest price of the day")
113
+ low: float = Field(description="Lowest price of the day")
114
+ close: float = Field(description="Closing price")
115
+ adj_close: float = Field(alias="adjClose", description="Adjusted closing price")
116
+ volume: int = Field(description="Volume traded")
117
+ unadjusted_volume: int = Field(
118
+ alias="unadjustedVolume", description="Unadjusted trading volume"
119
+ )
120
+ change: float = Field(description="Price change")
121
+ change_percent: float | None = Field(
122
+ None, alias="changePercent", description="Percentage change in price"
123
+ )
124
+ vwap: float | None = Field(None, description="Volume-weighted average price")
125
+ label: str | None = Field(None, description="Formatted label for the date")
126
+ change_over_time: float | None = Field(
127
+ None, alias="changeOverTime", description="Change over time as a percentage"
128
+ )
129
+
130
+
131
+ class IntradayPrice(BaseModel):
132
+ """Base model for intraday prices"""
133
+
134
+ model_config = ConfigDict(populate_by_name=True)
135
+
136
+ date: datetime.datetime = Field(description="Price date and time")
137
+ open: float = Field(description="Opening price")
138
+ high: float = Field(description="High price")
139
+ low: float = Field(description="Low price")
140
+ close: float = Field(description="Closing price")
141
+ volume: float | None = Field(None, description="Trading volume") # Changed to float
142
+
143
+
144
+ # Cryptocurrency Models
145
+ class CryptoPair(BaseModel):
146
+ """Cryptocurrency trading pair information"""
147
+
148
+ model_config = ConfigDict(populate_by_name=True)
149
+
150
+ symbol: str = Field(description="Trading symbol")
151
+ name: str = Field(description="Cryptocurrency name")
152
+ currency: str = Field(description="Quote currency")
153
+ stock_exchange: str = Field(
154
+ alias="stockExchange", description="Full name of the stock exchange"
155
+ )
156
+ exchange_short_name: str = Field(
157
+ alias="exchangeShortName", description="Short name of the exchange"
158
+ )
159
+
160
+
161
+ class CryptoQuote(PriceQuote):
162
+ """Cryptocurrency price quote"""
163
+
164
+ model_config = ConfigDict(
165
+ populate_by_name=True,
166
+ validate_assignment=True,
167
+ extra="ignore", # Allow extra fields from API
168
+ )
169
+
170
+ # Override the base class field to make it optional
171
+ change_percent: float | None = Field(
172
+ None, alias="changesPercentage", description="Percent change"
173
+ )
174
+
175
+ @field_validator("timestamp", mode="before")
176
+ def parse_timestamp(cls, value: Any) -> datetime.datetime | None:
177
+ """Parse Unix timestamp (int, float, str) into a TZ-aware UTC datetime."""
178
+ if value is None:
179
+ return None
180
+ try:
181
+ ts_float = float(value) # handles int, float, str transparently
182
+ return datetime.datetime.fromtimestamp(ts_float, tz=UTC)
183
+ except (ValueError, TypeError) as exc:
184
+ warnings.warn(f"Failed to parse timestamp {value!r}: {exc}", stacklevel=2)
185
+ return None
186
+
187
+
188
+ class CryptoHistoricalPrice(HistoricalPrice):
189
+ """Cryptocurrency historical price"""
190
+
191
+ pass
192
+
193
+
194
+ class CryptoHistoricalData(BaseModel):
195
+ """Historical price data wrapper"""
196
+
197
+ model_config = ConfigDict(populate_by_name=True)
198
+
199
+ symbol: str = Field(description="Trading symbol")
200
+ historical: list[CryptoHistoricalPrice] = Field(
201
+ description="Historical price records"
202
+ )
203
+
204
+
205
+ class CryptoIntradayPrice(IntradayPrice):
206
+ """Cryptocurrency intraday price"""
207
+
208
+ pass
209
+
210
+
211
+ # Forex Models
212
+ class ForexPair(BaseModel):
213
+ """Forex trading pair information"""
214
+
215
+ model_config = ConfigDict(populate_by_name=True)
216
+
217
+ symbol: str = Field(description="Trading symbol")
218
+ name: str = Field(description="Pair name")
219
+ currency: str = Field(description="Quote currency")
220
+ stock_exchange: str = Field(
221
+ alias="stockExchange", description="Stock exchange code"
222
+ )
223
+ exchange_short_name: str = Field(
224
+ alias="exchangeShortName", description="Exchange short name"
225
+ )
226
+
227
+
228
+ class ForexQuote(PriceQuote):
229
+ """Forex price quote"""
230
+
231
+ pass
232
+
233
+
234
+ class ForexHistoricalPrice(HistoricalPrice):
235
+ """Forex historical price"""
236
+
237
+ pass
238
+
239
+
240
+ class ForexPriceHistory(BaseModel):
241
+ """Full forex price history"""
242
+
243
+ symbol: str = Field(description="Symbol for the currency pair")
244
+ historical: list[ForexHistoricalPrice] = Field(
245
+ description="List of historical price data for the forex pair"
246
+ )
247
+
248
+
249
+ class ForexIntradayPrice(IntradayPrice):
250
+ """Forex intraday price"""
251
+
252
+ pass
253
+
254
+
255
+ # Commodities Models
256
+ class Commodity(BaseModel):
257
+ """Commodity information"""
258
+
259
+ model_config = ConfigDict(populate_by_name=True)
260
+
261
+ symbol: str = Field(description="Trading symbol")
262
+ name: str = Field(description="Commodity name")
263
+ currency: str = Field(description="Trading currency")
264
+ stock_exchange: str = Field(
265
+ alias="stockExchange", description="Full name of the stock exchange"
266
+ )
267
+ exchange_short_name: str = Field(
268
+ alias="exchangeShortName", description="Short name of the exchange category"
269
+ )
270
+
271
+
272
+ class CommodityQuote(PriceQuote):
273
+ """Commodity price quote"""
274
+
275
+ pass
276
+
277
+
278
+ class CommodityHistoricalPrice(HistoricalPrice):
279
+ """Commodity historical price"""
280
+
281
+ pass
282
+
283
+
284
+ class CommodityPriceHistory(BaseModel):
285
+ """Full commodity price history"""
286
+
287
+ symbol: str = Field(description="Symbol for the commodity")
288
+ historical: list[CommodityHistoricalPrice] = Field(
289
+ description="List of historical price data"
290
+ )
291
+
292
+
293
+ class CommodityIntradayPrice(IntradayPrice):
294
+ """Commodity intraday price"""
295
+
296
+ pass
@@ -0,0 +1,165 @@
1
+ # fmp_data/alternative/schema.py
2
+ from datetime import date
3
+ from typing import Literal
4
+
5
+ from pydantic import BaseModel, Field, field_validator
6
+
7
+ # Constants
8
+ VALID_INTERVALS = Literal["1min", "5min", "15min", "30min", "1hour", "4hour"]
9
+
10
+
11
+ class BaseListArgs(BaseModel):
12
+ """Base class for list endpoints that take no arguments"""
13
+
14
+ pass
15
+
16
+
17
+ class BaseQuoteArgs(BaseModel):
18
+ """Base class for quote endpoints"""
19
+
20
+ symbol: str = Field(description="Trading symbol for the instrument")
21
+
22
+
23
+ class BaseHistoricalArgs(BaseQuoteArgs):
24
+ """Base class for historical data endpoints"""
25
+
26
+ start_date: date | None = Field( # Changed from from_date
27
+ None,
28
+ description="Start date for historical data (format: YYYY-MM-DD)",
29
+ )
30
+ end_date: date | None = Field( # Changed from to_date
31
+ None,
32
+ description="End date for historical data (format: YYYY-MM-DD)",
33
+ )
34
+
35
+
36
+ class BaseIntradayArgs(BaseQuoteArgs):
37
+ """Base class for intraday data endpoints"""
38
+
39
+ interval: VALID_INTERVALS = Field(
40
+ description="Time interval between price points",
41
+ )
42
+
43
+ @field_validator("interval")
44
+ def validate_interval(cls, v: str) -> str:
45
+ valid_intervals = ["1min", "5min", "15min", "30min", "1hour", "4hour"]
46
+ if v not in valid_intervals:
47
+ raise ValueError(f"Interval must be one of: {valid_intervals}")
48
+ return v
49
+
50
+
51
+ # Crypto Arguments
52
+ class CryptoListArgs(BaseListArgs):
53
+ """Arguments for listing available cryptocurrencies"""
54
+
55
+ pass
56
+
57
+
58
+ class CryptoQuotesArgs(BaseListArgs):
59
+ """Arguments for getting cryptocurrency quotes"""
60
+
61
+ pass
62
+
63
+
64
+ class CryptoQuoteArgs(BaseQuoteArgs):
65
+ """Arguments for getting a specific cryptocurrency quote"""
66
+
67
+ symbol: str = Field(
68
+ description=(
69
+ "Trading symbol for the cryptocurrency" " (e.g., 'BTCUSD' for Bitcoin/USD)"
70
+ ),
71
+ pattern=r"^[A-Z]{3,4}USD$",
72
+ )
73
+
74
+
75
+ class CryptoHistoricalArgs(BaseHistoricalArgs):
76
+ """Arguments for getting historical cryptocurrency prices"""
77
+
78
+ symbol: str = Field(
79
+ description="Trading symbol for the cryptocurrency (e.g., 'BTCUSD')",
80
+ pattern=r"^[A-Z]{3,4}USD$",
81
+ )
82
+
83
+
84
+ class CryptoIntradayArgs(BaseIntradayArgs):
85
+ """Arguments for getting intraday cryptocurrency prices"""
86
+
87
+ symbol: str = Field(
88
+ description="Trading symbol for the cryptocurrency", pattern=r"^[A-Z]{3,4}USD$"
89
+ )
90
+
91
+
92
+ # Forex Arguments
93
+ class ForexListArgs(BaseListArgs):
94
+ """Arguments for listing available forex pairs"""
95
+
96
+ pass
97
+
98
+
99
+ class ForexQuotesArgs(BaseListArgs):
100
+ """Arguments for getting forex quotes"""
101
+
102
+ pass
103
+
104
+
105
+ class ForexQuoteArgs(BaseQuoteArgs):
106
+ """Arguments for getting a specific forex quote"""
107
+
108
+ symbol: str = Field(
109
+ description="Trading symbol for the forex pair (e.g., 'EURUSD')",
110
+ pattern=r"^[A-Z]{6}$",
111
+ )
112
+
113
+
114
+ class ForexHistoricalArgs(BaseHistoricalArgs):
115
+ """Arguments for getting historical forex prices"""
116
+
117
+ symbol: str = Field(
118
+ description="Trading symbol for the forex pair", pattern=r"^[A-Z]{6}$"
119
+ )
120
+
121
+
122
+ class ForexIntradayArgs(BaseIntradayArgs):
123
+ """Arguments for getting intraday forex prices"""
124
+
125
+ symbol: str = Field(
126
+ description="Trading symbol for the forex pair", pattern=r"^[A-Z]{6}$"
127
+ )
128
+
129
+
130
+ # Commodity Arguments
131
+ class CommoditiesListArgs(BaseListArgs):
132
+ """Arguments for listing available commodities"""
133
+
134
+ pass
135
+
136
+
137
+ class CommoditiesQuotesArgs(BaseListArgs):
138
+ """Arguments for getting commodities quotes"""
139
+
140
+ pass
141
+
142
+
143
+ class CommodityQuoteArgs(BaseQuoteArgs):
144
+ """Arguments for getting a specific commodity quote"""
145
+
146
+ symbol: str = Field(
147
+ description="Trading symbol for the commodity (e.g., 'GC' for Gold)",
148
+ pattern=r"^[A-Z]{2,3}$",
149
+ )
150
+
151
+
152
+ class CommodityHistoricalArgs(BaseHistoricalArgs):
153
+ """Arguments for getting historical commodity prices"""
154
+
155
+ symbol: str = Field(
156
+ description="Trading symbol for the commodity", pattern=r"^[A-Z]{2,3}$"
157
+ )
158
+
159
+
160
+ class CommodityIntradayArgs(BaseIntradayArgs):
161
+ """Arguments for getting intraday commodity prices"""
162
+
163
+ symbol: str = Field(
164
+ description="Trading symbol for the commodity", pattern=r"^[A-Z]{2,3}$"
165
+ )