fin-infra 0.1.69__py3-none-any.whl → 0.4.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.
- fin_infra/__init__.py +53 -3
- fin_infra/analytics/__init__.py +13 -2
- fin_infra/analytics/add.py +24 -24
- fin_infra/analytics/cash_flow.py +3 -3
- fin_infra/analytics/ease.py +19 -20
- fin_infra/analytics/models.py +5 -5
- fin_infra/analytics/portfolio.py +18 -18
- fin_infra/analytics/projections.py +1 -3
- fin_infra/analytics/spending.py +4 -5
- fin_infra/banking/__init__.py +27 -28
- fin_infra/banking/history.py +12 -13
- fin_infra/banking/utils.py +27 -26
- fin_infra/brokerage/__init__.py +29 -31
- fin_infra/budgets/__init__.py +3 -3
- fin_infra/budgets/add.py +16 -17
- fin_infra/budgets/alerts.py +4 -4
- fin_infra/budgets/ease.py +1 -2
- fin_infra/budgets/models.py +1 -2
- fin_infra/budgets/templates.py +4 -4
- fin_infra/budgets/tracker.py +4 -4
- fin_infra/cashflows/__init__.py +3 -3
- fin_infra/cashflows/core.py +1 -1
- fin_infra/categorization/__init__.py +1 -1
- fin_infra/categorization/add.py +2 -3
- fin_infra/categorization/ease.py +3 -3
- fin_infra/categorization/engine.py +18 -15
- fin_infra/categorization/llm_layer.py +13 -10
- fin_infra/categorization/models.py +3 -4
- fin_infra/categorization/rules.py +2 -4
- fin_infra/categorization/taxonomy.py +2 -2
- fin_infra/chat/__init__.py +6 -6
- fin_infra/chat/planning.py +1 -2
- fin_infra/cli/cmds/scaffold_cmds.py +16 -17
- fin_infra/clients/__init__.py +23 -1
- fin_infra/clients/base.py +1 -1
- fin_infra/clients/plaid.py +2 -2
- fin_infra/compliance/__init__.py +5 -4
- fin_infra/credit/add.py +6 -7
- fin_infra/credit/experian/auth.py +2 -2
- fin_infra/credit/experian/client.py +1 -1
- fin_infra/credit/experian/parser.py +5 -5
- fin_infra/credit/experian/provider.py +4 -4
- fin_infra/crypto/__init__.py +9 -11
- fin_infra/crypto/insights.py +4 -3
- fin_infra/documents/add.py +6 -8
- fin_infra/documents/analysis.py +9 -9
- fin_infra/documents/ease.py +14 -14
- fin_infra/documents/models.py +5 -6
- fin_infra/documents/ocr.py +7 -7
- fin_infra/documents/storage.py +21 -13
- fin_infra/exceptions.py +0 -1
- fin_infra/goals/__init__.py +8 -8
- fin_infra/goals/add.py +36 -36
- fin_infra/goals/funding.py +4 -6
- fin_infra/goals/management.py +5 -6
- fin_infra/goals/milestones.py +7 -8
- fin_infra/goals/models.py +9 -13
- fin_infra/insights/__init__.py +6 -3
- fin_infra/insights/aggregator.py +1 -1
- fin_infra/investments/__init__.py +3 -3
- fin_infra/investments/add.py +23 -23
- fin_infra/investments/ease.py +2 -2
- fin_infra/investments/models.py +27 -29
- fin_infra/investments/providers/base.py +12 -13
- fin_infra/investments/providers/plaid.py +52 -26
- fin_infra/investments/providers/snaptrade.py +19 -19
- fin_infra/investments/scaffold_templates/README.md +17 -17
- fin_infra/markets/__init__.py +7 -5
- fin_infra/models/__init__.py +10 -10
- fin_infra/models/accounts.py +4 -5
- fin_infra/models/brokerage.py +2 -1
- fin_infra/models/candle.py +1 -0
- fin_infra/models/money.py +1 -0
- fin_infra/models/quotes.py +4 -3
- fin_infra/models/tax.py +2 -1
- fin_infra/models/transactions.py +4 -5
- fin_infra/net_worth/__init__.py +8 -1
- fin_infra/net_worth/aggregator.py +5 -3
- fin_infra/net_worth/calculator.py +1 -1
- fin_infra/net_worth/insights.py +7 -8
- fin_infra/normalization/__init__.py +4 -4
- fin_infra/normalization/currency_converter.py +7 -8
- fin_infra/normalization/models.py +9 -10
- fin_infra/normalization/providers/exchangerate.py +5 -5
- fin_infra/normalization/providers/static_mappings.py +1 -1
- fin_infra/normalization/symbol_resolver.py +3 -4
- fin_infra/obs/classifier.py +3 -3
- fin_infra/providers/banking/plaid_client.py +5 -5
- fin_infra/providers/banking/teller_client.py +7 -6
- fin_infra/providers/base.py +27 -2
- fin_infra/providers/brokerage/alpaca.py +4 -4
- fin_infra/providers/market/alphavantage.py +6 -11
- fin_infra/providers/market/ccxt_crypto.py +19 -3
- fin_infra/providers/market/coingecko.py +5 -6
- fin_infra/providers/market/yahoo.py +23 -8
- fin_infra/providers/tax/__init__.py +1 -1
- fin_infra/providers/tax/irs.py +1 -1
- fin_infra/providers/tax/mock.py +5 -5
- fin_infra/providers/tax/taxbit.py +1 -1
- fin_infra/recurring/__init__.py +6 -6
- fin_infra/recurring/add.py +6 -5
- fin_infra/recurring/detector.py +7 -7
- fin_infra/recurring/detectors_llm.py +10 -10
- fin_infra/recurring/ease.py +6 -8
- fin_infra/recurring/insights.py +25 -24
- fin_infra/recurring/normalizer.py +7 -7
- fin_infra/recurring/normalizers.py +31 -30
- fin_infra/recurring/summary.py +13 -15
- fin_infra/scaffold/budgets.py +9 -9
- fin_infra/scaffold/goals.py +9 -9
- fin_infra/security/__init__.py +8 -8
- fin_infra/security/add.py +1 -2
- fin_infra/security/audit.py +6 -7
- fin_infra/security/encryption.py +6 -6
- fin_infra/security/models.py +7 -7
- fin_infra/security/pii_filter.py +16 -16
- fin_infra/security/token_store.py +2 -3
- fin_infra/settings.py +2 -1
- fin_infra/tax/__init__.py +1 -1
- fin_infra/tax/add.py +5 -4
- fin_infra/tax/tlh.py +10 -10
- fin_infra/utils/__init__.py +15 -1
- fin_infra/utils/deprecation.py +161 -0
- fin_infra/utils/http.py +4 -3
- fin_infra/utils/retry.py +2 -1
- {fin_infra-0.1.69.dist-info → fin_infra-0.4.0.dist-info}/METADATA +30 -16
- fin_infra-0.4.0.dist-info/RECORD +181 -0
- fin_infra-0.1.69.dist-info/RECORD +0 -180
- {fin_infra-0.1.69.dist-info → fin_infra-0.4.0.dist-info}/LICENSE +0 -0
- {fin_infra-0.1.69.dist-info → fin_infra-0.4.0.dist-info}/WHEEL +0 -0
- {fin_infra-0.1.69.dist-info → fin_infra-0.4.0.dist-info}/entry_points.txt +0 -0
fin_infra/security/pii_filter.py
CHANGED
|
@@ -9,21 +9,21 @@ import re
|
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
11
11
|
from .pii_patterns import (
|
|
12
|
-
ACCOUNT_PATTERN,
|
|
13
12
|
ACCOUNT_CONTEXT,
|
|
13
|
+
ACCOUNT_PATTERN,
|
|
14
14
|
CARD_PATTERN,
|
|
15
|
-
CVV_PATTERN,
|
|
16
15
|
CVV_CONTEXT,
|
|
16
|
+
CVV_PATTERN,
|
|
17
17
|
EIN_PATTERN,
|
|
18
18
|
EMAIL_PATTERN,
|
|
19
19
|
PHONE_PATTERN,
|
|
20
|
-
ROUTING_PATTERN,
|
|
21
20
|
ROUTING_CONTEXT,
|
|
22
|
-
|
|
23
|
-
SSN_NO_DASH,
|
|
21
|
+
ROUTING_PATTERN,
|
|
24
22
|
SSN_CONTEXT,
|
|
25
|
-
|
|
23
|
+
SSN_NO_DASH,
|
|
24
|
+
SSN_PATTERN,
|
|
26
25
|
is_valid_routing_number,
|
|
26
|
+
luhn_checksum,
|
|
27
27
|
)
|
|
28
28
|
|
|
29
29
|
|
|
@@ -116,8 +116,8 @@ class FinancialPIIFilter(logging.Filter):
|
|
|
116
116
|
Mask Social Security Numbers.
|
|
117
117
|
|
|
118
118
|
Examples:
|
|
119
|
-
123-45-6789
|
|
120
|
-
123456789
|
|
119
|
+
123-45-6789 -> ***-**-6789
|
|
120
|
+
123456789 -> *****6789 (with context)
|
|
121
121
|
"""
|
|
122
122
|
# With dashes (high confidence)
|
|
123
123
|
text = SSN_PATTERN.sub(lambda m: f"***-**-{m.group()[-4:]}", text)
|
|
@@ -145,7 +145,7 @@ class FinancialPIIFilter(logging.Filter):
|
|
|
145
145
|
Mask Employer Identification Numbers.
|
|
146
146
|
|
|
147
147
|
Example:
|
|
148
|
-
12-3456789
|
|
148
|
+
12-3456789 -> **-****789
|
|
149
149
|
"""
|
|
150
150
|
return EIN_PATTERN.sub(lambda m: f"**-****{m.group()[-3:]}", text)
|
|
151
151
|
|
|
@@ -154,8 +154,8 @@ class FinancialPIIFilter(logging.Filter):
|
|
|
154
154
|
Mask credit card numbers using Luhn validation.
|
|
155
155
|
|
|
156
156
|
Examples:
|
|
157
|
-
4111 1111 1111 1111
|
|
158
|
-
4111111111111111
|
|
157
|
+
4111 1111 1111 1111 -> **** **** **** 1111
|
|
158
|
+
4111111111111111 -> ************1111
|
|
159
159
|
"""
|
|
160
160
|
|
|
161
161
|
def mask_card_match(match):
|
|
@@ -182,7 +182,7 @@ class FinancialPIIFilter(logging.Filter):
|
|
|
182
182
|
Mask ABA routing numbers with checksum validation.
|
|
183
183
|
|
|
184
184
|
Example:
|
|
185
|
-
021000021
|
|
185
|
+
021000021 -> ******021
|
|
186
186
|
"""
|
|
187
187
|
|
|
188
188
|
def mask_routing_match(match):
|
|
@@ -207,7 +207,7 @@ class FinancialPIIFilter(logging.Filter):
|
|
|
207
207
|
Mask bank account numbers.
|
|
208
208
|
|
|
209
209
|
Example:
|
|
210
|
-
1234567890
|
|
210
|
+
1234567890 -> ******7890
|
|
211
211
|
"""
|
|
212
212
|
|
|
213
213
|
def mask_account_match(match):
|
|
@@ -231,7 +231,7 @@ class FinancialPIIFilter(logging.Filter):
|
|
|
231
231
|
Mask CVV codes (context-dependent).
|
|
232
232
|
|
|
233
233
|
Example:
|
|
234
|
-
CVV: 123
|
|
234
|
+
CVV: 123 -> CVV: ***
|
|
235
235
|
"""
|
|
236
236
|
|
|
237
237
|
def mask_cvv_match(match):
|
|
@@ -255,7 +255,7 @@ class FinancialPIIFilter(logging.Filter):
|
|
|
255
255
|
Mask email addresses.
|
|
256
256
|
|
|
257
257
|
Example:
|
|
258
|
-
user@example.com
|
|
258
|
+
user@example.com -> u***@example.com
|
|
259
259
|
"""
|
|
260
260
|
|
|
261
261
|
def mask_email_match(match):
|
|
@@ -272,7 +272,7 @@ class FinancialPIIFilter(logging.Filter):
|
|
|
272
272
|
Mask phone numbers.
|
|
273
273
|
|
|
274
274
|
Example:
|
|
275
|
-
(555) 123-4567
|
|
275
|
+
(555) 123-4567 -> (***) ***-4567
|
|
276
276
|
"""
|
|
277
277
|
|
|
278
278
|
def mask_phone_match(match):
|
|
@@ -5,7 +5,6 @@ Database operations for encrypted provider tokens.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from datetime import datetime
|
|
8
|
-
from typing import Optional
|
|
9
8
|
|
|
10
9
|
from sqlalchemy import Column, DateTime, String, Text, select, update
|
|
11
10
|
from sqlalchemy.dialects.postgresql import UUID
|
|
@@ -48,8 +47,8 @@ async def store_provider_token(
|
|
|
48
47
|
provider: str,
|
|
49
48
|
token: str,
|
|
50
49
|
encryption: ProviderTokenEncryption,
|
|
51
|
-
expires_at:
|
|
52
|
-
key_id:
|
|
50
|
+
expires_at: datetime | None = None,
|
|
51
|
+
key_id: str | None = None,
|
|
53
52
|
) -> ProviderTokenMetadata:
|
|
54
53
|
"""
|
|
55
54
|
Store encrypted provider token in database.
|
fin_infra/settings.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from functools import lru_cache
|
|
4
|
+
|
|
4
5
|
from pydantic import Field
|
|
5
6
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
6
7
|
|
|
@@ -33,5 +34,5 @@ class Settings(BaseSettings):
|
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
@lru_cache
|
|
36
|
-
def get_settings() ->
|
|
37
|
+
def get_settings() -> Settings:
|
|
37
38
|
return Settings()
|
fin_infra/tax/__init__.py
CHANGED
|
@@ -33,7 +33,7 @@ Example:
|
|
|
33
33
|
import os
|
|
34
34
|
|
|
35
35
|
from fin_infra.providers.base import TaxProvider
|
|
36
|
-
from fin_infra.providers.tax import
|
|
36
|
+
from fin_infra.providers.tax import IRSProvider, MockTaxProvider, TaxBitProvider
|
|
37
37
|
from fin_infra.tax.add import add_tax_data
|
|
38
38
|
from fin_infra.tax.tlh import (
|
|
39
39
|
TLHOpportunity,
|
fin_infra/tax/add.py
CHANGED
|
@@ -17,7 +17,8 @@ Example:
|
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
19
|
from decimal import Decimal
|
|
20
|
-
|
|
20
|
+
|
|
21
|
+
from fastapi import Body, FastAPI, Query
|
|
21
22
|
from pydantic import BaseModel
|
|
22
23
|
|
|
23
24
|
from fin_infra.providers.base import TaxProvider
|
|
@@ -89,8 +90,8 @@ def add_tax_data(
|
|
|
89
90
|
>>> # POST /tax/tax-liability
|
|
90
91
|
"""
|
|
91
92
|
# Use svc-infra user_router for authentication (tax data is user-specific and sensitive)
|
|
92
|
-
from svc_infra.api.fastapi.dual.protected import user_router
|
|
93
93
|
from svc_infra.api.fastapi.docs.scoped import add_prefixed_docs
|
|
94
|
+
from svc_infra.api.fastapi.dual.protected import user_router
|
|
94
95
|
|
|
95
96
|
# Initialize provider
|
|
96
97
|
if provider is None:
|
|
@@ -278,7 +279,7 @@ def add_tax_data(
|
|
|
278
279
|
that can be sold to offset capital gains. Suggests replacement securities
|
|
279
280
|
to maintain market exposure without triggering wash sale rules.
|
|
280
281
|
|
|
281
|
-
|
|
282
|
+
[!] **DISCLAIMER**: Not a substitute for professional tax or financial advice.
|
|
282
283
|
Consult a certified tax professional before executing TLH trades.
|
|
283
284
|
|
|
284
285
|
Args:
|
|
@@ -343,7 +344,7 @@ def add_tax_data(
|
|
|
343
344
|
Projects the outcome of executing provided TLH opportunities, including
|
|
344
345
|
total tax savings, portfolio impact, and risk assessment.
|
|
345
346
|
|
|
346
|
-
|
|
347
|
+
[!] **DISCLAIMER**: Not a substitute for professional tax or financial advice.
|
|
347
348
|
Consult a certified tax professional before executing TLH trades.
|
|
348
349
|
|
|
349
350
|
Args:
|
fin_infra/tax/tlh.py
CHANGED
|
@@ -55,7 +55,7 @@ Cost Considerations:
|
|
|
55
55
|
|
|
56
56
|
from __future__ import annotations
|
|
57
57
|
|
|
58
|
-
from datetime import
|
|
58
|
+
from datetime import UTC, datetime
|
|
59
59
|
from decimal import Decimal
|
|
60
60
|
from typing import TYPE_CHECKING
|
|
61
61
|
|
|
@@ -412,9 +412,9 @@ def _assess_wash_sale_risk(symbol: str, last_purchase_date: datetime | None) ->
|
|
|
412
412
|
return "none"
|
|
413
413
|
|
|
414
414
|
# Calculate days since last purchase
|
|
415
|
-
now = datetime.now(
|
|
415
|
+
now = datetime.now(UTC)
|
|
416
416
|
if last_purchase_date.tzinfo is None:
|
|
417
|
-
last_purchase_date = last_purchase_date.replace(tzinfo=
|
|
417
|
+
last_purchase_date = last_purchase_date.replace(tzinfo=UTC)
|
|
418
418
|
|
|
419
419
|
days_since = (now - last_purchase_date).days
|
|
420
420
|
|
|
@@ -456,7 +456,7 @@ def _suggest_replacement(symbol: str, asset_class: str) -> str:
|
|
|
456
456
|
# Simple rule-based suggestions (v1)
|
|
457
457
|
# TODO: Replace with ai-infra LLM for intelligent suggestions
|
|
458
458
|
replacements = {
|
|
459
|
-
# Tech stocks
|
|
459
|
+
# Tech stocks -> sector ETFs
|
|
460
460
|
"AAPL": "VGT", # Tech ETF
|
|
461
461
|
"MSFT": "VGT",
|
|
462
462
|
"GOOGL": "VGT",
|
|
@@ -464,19 +464,19 @@ def _suggest_replacement(symbol: str, asset_class: str) -> str:
|
|
|
464
464
|
"META": "VGT",
|
|
465
465
|
"NVDA": "SOXX", # Semiconductor ETF
|
|
466
466
|
"AMD": "SOXX",
|
|
467
|
-
# Auto/EV
|
|
467
|
+
# Auto/EV -> sector alternatives
|
|
468
468
|
"TSLA": "ARKK", # Innovation ETF
|
|
469
469
|
"F": "XLI", # Industrials ETF
|
|
470
470
|
"GM": "XLI",
|
|
471
|
-
# Finance
|
|
471
|
+
# Finance -> sector ETF
|
|
472
472
|
"JPM": "XLF", # Financials ETF
|
|
473
473
|
"BAC": "XLF",
|
|
474
474
|
"GS": "XLF",
|
|
475
|
-
# Healthcare
|
|
475
|
+
# Healthcare -> sector ETF
|
|
476
476
|
"JNJ": "XLV", # Healthcare ETF
|
|
477
477
|
"PFE": "XLV",
|
|
478
478
|
"MRNA": "XBI", # Biotech ETF
|
|
479
|
-
# Crypto
|
|
479
|
+
# Crypto -> broad exposure
|
|
480
480
|
"BTC": "ETH", # Ethereum (different asset)
|
|
481
481
|
"ETH": "BTC", # Bitcoin (different asset)
|
|
482
482
|
}
|
|
@@ -506,8 +506,8 @@ def _generate_tlh_recommendations(
|
|
|
506
506
|
recommendations = []
|
|
507
507
|
|
|
508
508
|
# Timing recommendations
|
|
509
|
-
now = datetime.now(
|
|
510
|
-
days_until_year_end = (datetime(now.year, 12, 31, tzinfo=
|
|
509
|
+
now = datetime.now(UTC)
|
|
510
|
+
days_until_year_end = (datetime(now.year, 12, 31, tzinfo=UTC) - now).days
|
|
511
511
|
|
|
512
512
|
if days_until_year_end < 30:
|
|
513
513
|
recommendations.append(
|
fin_infra/utils/__init__.py
CHANGED
|
@@ -10,8 +10,22 @@ and should be imported from there:
|
|
|
10
10
|
|
|
11
11
|
For async retry with exponential backoff:
|
|
12
12
|
from fin_infra.utils.retry import retry_async, RetryError
|
|
13
|
+
|
|
14
|
+
For deprecation utilities:
|
|
15
|
+
from fin_infra.utils.deprecation import deprecated, deprecated_parameter
|
|
13
16
|
"""
|
|
14
17
|
|
|
18
|
+
from fin_infra.utils.deprecation import (
|
|
19
|
+
DeprecatedWarning,
|
|
20
|
+
deprecated,
|
|
21
|
+
deprecated_parameter,
|
|
22
|
+
)
|
|
15
23
|
from fin_infra.utils.retry import RetryError, retry_async
|
|
16
24
|
|
|
17
|
-
__all__ = [
|
|
25
|
+
__all__ = [
|
|
26
|
+
"RetryError",
|
|
27
|
+
"retry_async",
|
|
28
|
+
"deprecated",
|
|
29
|
+
"deprecated_parameter",
|
|
30
|
+
"DeprecatedWarning",
|
|
31
|
+
]
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""Deprecation utilities for fin-infra.
|
|
2
|
+
|
|
3
|
+
This module provides decorators and functions for marking features as deprecated,
|
|
4
|
+
following the deprecation policy defined in DEPRECATION.md.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
>>> from fin_infra.utils.deprecation import deprecated
|
|
8
|
+
>>>
|
|
9
|
+
>>> @deprecated(
|
|
10
|
+
... version="1.2.0",
|
|
11
|
+
... reason="Use new_function() instead",
|
|
12
|
+
... removal_version="1.4.0"
|
|
13
|
+
... )
|
|
14
|
+
... def old_function():
|
|
15
|
+
... pass
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import functools
|
|
21
|
+
import warnings
|
|
22
|
+
from collections.abc import Callable
|
|
23
|
+
from typing import Any, TypeVar
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"deprecated",
|
|
27
|
+
"deprecated_parameter",
|
|
28
|
+
"DeprecatedWarning",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class DeprecatedWarning(DeprecationWarning):
|
|
35
|
+
"""Custom deprecation warning for fin-infra.
|
|
36
|
+
|
|
37
|
+
This warning is used to distinguish fin-infra deprecations from
|
|
38
|
+
Python's built-in DeprecationWarning.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def deprecated(
|
|
45
|
+
version: str,
|
|
46
|
+
reason: str,
|
|
47
|
+
removal_version: str | None = None,
|
|
48
|
+
*,
|
|
49
|
+
stacklevel: int = 2,
|
|
50
|
+
) -> Callable[[F], F]:
|
|
51
|
+
"""Decorator to mark a function or class as deprecated.
|
|
52
|
+
|
|
53
|
+
The decorated function/class will emit a DeprecationWarning when called/instantiated.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
version: The version in which the feature was deprecated (e.g., "1.2.0").
|
|
57
|
+
reason: The reason for deprecation and recommended alternative.
|
|
58
|
+
removal_version: The version in which the feature will be removed (e.g., "1.4.0").
|
|
59
|
+
stacklevel: Stack level for the warning (default 2 for immediate caller).
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
A decorator that wraps the function/class with deprecation warning.
|
|
63
|
+
|
|
64
|
+
Example:
|
|
65
|
+
>>> @deprecated(
|
|
66
|
+
... version="1.2.0",
|
|
67
|
+
... reason="Use new_function() instead",
|
|
68
|
+
... removal_version="1.4.0"
|
|
69
|
+
... )
|
|
70
|
+
... def old_function():
|
|
71
|
+
... return "result"
|
|
72
|
+
>>>
|
|
73
|
+
>>> old_function() # Emits DeprecationWarning
|
|
74
|
+
'result'
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
def decorator(func: F) -> F:
|
|
78
|
+
# Build the warning message
|
|
79
|
+
name = getattr(func, "__qualname__", getattr(func, "__name__", str(func)))
|
|
80
|
+
message = f"{name} is deprecated since version {version}."
|
|
81
|
+
|
|
82
|
+
if removal_version:
|
|
83
|
+
message += f" It will be removed in version {removal_version}."
|
|
84
|
+
|
|
85
|
+
message += f" {reason}"
|
|
86
|
+
|
|
87
|
+
if isinstance(func, type):
|
|
88
|
+
# Handle class deprecation
|
|
89
|
+
original_init = func.__init__ # type: ignore[misc]
|
|
90
|
+
|
|
91
|
+
@functools.wraps(original_init)
|
|
92
|
+
def new_init(self: Any, *args: Any, **kwargs: Any) -> None:
|
|
93
|
+
warnings.warn(message, DeprecatedWarning, stacklevel=stacklevel)
|
|
94
|
+
original_init(self, *args, **kwargs)
|
|
95
|
+
|
|
96
|
+
func.__init__ = new_init # type: ignore[misc]
|
|
97
|
+
|
|
98
|
+
# Add deprecation info to docstring
|
|
99
|
+
if func.__doc__:
|
|
100
|
+
func.__doc__ = f".. deprecated:: {version}\n {reason}\n\n{func.__doc__}"
|
|
101
|
+
else:
|
|
102
|
+
func.__doc__ = f".. deprecated:: {version}\n {reason}"
|
|
103
|
+
|
|
104
|
+
return func # type: ignore[return-value]
|
|
105
|
+
else:
|
|
106
|
+
# Handle function deprecation
|
|
107
|
+
@functools.wraps(func)
|
|
108
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
109
|
+
warnings.warn(message, DeprecatedWarning, stacklevel=stacklevel)
|
|
110
|
+
return func(*args, **kwargs)
|
|
111
|
+
|
|
112
|
+
# Add deprecation info to docstring
|
|
113
|
+
if wrapper.__doc__:
|
|
114
|
+
wrapper.__doc__ = f".. deprecated:: {version}\n {reason}\n\n{wrapper.__doc__}"
|
|
115
|
+
else:
|
|
116
|
+
wrapper.__doc__ = f".. deprecated:: {version}\n {reason}"
|
|
117
|
+
|
|
118
|
+
return wrapper # type: ignore[return-value]
|
|
119
|
+
|
|
120
|
+
return decorator
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def deprecated_parameter(
|
|
124
|
+
name: str,
|
|
125
|
+
version: str,
|
|
126
|
+
reason: str,
|
|
127
|
+
removal_version: str | None = None,
|
|
128
|
+
*,
|
|
129
|
+
stacklevel: int = 2,
|
|
130
|
+
) -> None:
|
|
131
|
+
"""Emit a deprecation warning for a deprecated parameter.
|
|
132
|
+
|
|
133
|
+
Call this function when a deprecated parameter is used. This should be
|
|
134
|
+
called at the beginning of a function that has deprecated parameters.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
name: The name of the deprecated parameter.
|
|
138
|
+
version: The version in which the parameter was deprecated.
|
|
139
|
+
reason: The reason for deprecation and recommended alternative.
|
|
140
|
+
removal_version: The version in which the parameter will be removed.
|
|
141
|
+
stacklevel: Stack level for the warning (default 2 for immediate caller).
|
|
142
|
+
|
|
143
|
+
Example:
|
|
144
|
+
>>> def my_function(new_param: str, old_param: str | None = None):
|
|
145
|
+
... if old_param is not None:
|
|
146
|
+
... deprecated_parameter(
|
|
147
|
+
... name="old_param",
|
|
148
|
+
... version="1.2.0",
|
|
149
|
+
... reason="Use new_param instead"
|
|
150
|
+
... )
|
|
151
|
+
... new_param = old_param
|
|
152
|
+
... return new_param
|
|
153
|
+
"""
|
|
154
|
+
message = f"Parameter '{name}' is deprecated since version {version}."
|
|
155
|
+
|
|
156
|
+
if removal_version:
|
|
157
|
+
message += f" It will be removed in version {removal_version}."
|
|
158
|
+
|
|
159
|
+
message += f" {reason}"
|
|
160
|
+
|
|
161
|
+
warnings.warn(message, DeprecatedWarning, stacklevel=stacklevel + 1)
|
fin_infra/utils/http.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import httpx
|
|
4
3
|
from typing import Any, cast
|
|
5
|
-
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_exponential
|
|
6
7
|
|
|
7
8
|
_DEFAULT_TIMEOUT = httpx.Timeout(20.0)
|
|
8
9
|
|
|
@@ -17,4 +18,4 @@ async def aget_json(url: str, **kwargs) -> dict[Any, Any]:
|
|
|
17
18
|
async with httpx.AsyncClient(timeout=_DEFAULT_TIMEOUT) as client:
|
|
18
19
|
r = await client.get(url, **kwargs)
|
|
19
20
|
r.raise_for_status()
|
|
20
|
-
return cast(dict[Any, Any], r.json())
|
|
21
|
+
return cast("dict[Any, Any]", r.json())
|
fin_infra/utils/retry.py
CHANGED
|
@@ -2,7 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import random
|
|
5
|
-
from
|
|
5
|
+
from collections.abc import Awaitable, Callable, Iterable
|
|
6
|
+
from typing import TypeVar
|
|
6
7
|
|
|
7
8
|
from fin_infra.exceptions import RetryError
|
|
8
9
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: fin-infra
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Financial infrastructure toolkit: banking connections, market data, credit, cashflows, and brokerage integrations
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: finance,banking,plaid,brokerage,markets,credit,tax,cashflow,fintech,infra
|
|
@@ -19,41 +19,55 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
19
19
|
Classifier: Topic :: Office/Business :: Financial
|
|
20
20
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
21
|
Classifier: Typing :: Typed
|
|
22
|
+
Provides-Extra: all
|
|
23
|
+
Provides-Extra: banking
|
|
24
|
+
Provides-Extra: crypto
|
|
25
|
+
Provides-Extra: markets
|
|
26
|
+
Provides-Extra: plaid
|
|
27
|
+
Provides-Extra: yahoo
|
|
22
28
|
Requires-Dist: ai-infra (>=0.1.142)
|
|
23
29
|
Requires-Dist: cashews[redis] (>=7.0)
|
|
24
|
-
Requires-Dist: ccxt (>=4.0.0)
|
|
30
|
+
Requires-Dist: ccxt (>=4.0.0) ; extra == "markets" or extra == "crypto" or extra == "all"
|
|
31
|
+
Requires-Dist: fastapi-users (>=15.0.2,<16.0.0)
|
|
25
32
|
Requires-Dist: httpx (>=0.25.0)
|
|
33
|
+
Requires-Dist: langchain-core (>=1.2.5,<2.0.0)
|
|
26
34
|
Requires-Dist: loguru (>=0.7.0)
|
|
27
35
|
Requires-Dist: numpy (>=1.24.0)
|
|
28
36
|
Requires-Dist: numpy-financial (>=1.0.0)
|
|
29
|
-
Requires-Dist: plaid-python (>=25.0.0)
|
|
37
|
+
Requires-Dist: plaid-python (>=25.0.0) ; extra == "plaid" or extra == "banking" or extra == "all"
|
|
30
38
|
Requires-Dist: pydantic (>=2.0)
|
|
31
39
|
Requires-Dist: pydantic-settings (>=2.0)
|
|
32
40
|
Requires-Dist: python-dotenv (>=1.0.0)
|
|
33
41
|
Requires-Dist: tenacity (>=8.0.0)
|
|
34
42
|
Requires-Dist: typing-extensions (>=4.0)
|
|
35
|
-
Requires-Dist: yahooquery (>=2.3.0)
|
|
36
|
-
Project-URL: Documentation, https://
|
|
37
|
-
Project-URL: Homepage, https://github.com/
|
|
38
|
-
Project-URL: Issues, https://github.com/
|
|
39
|
-
Project-URL: Repository, https://github.com/
|
|
43
|
+
Requires-Dist: yahooquery (>=2.3.0) ; extra == "markets" or extra == "yahoo" or extra == "all"
|
|
44
|
+
Project-URL: Documentation, https://nfrax.com/fin-infra
|
|
45
|
+
Project-URL: Homepage, https://github.com/nfraxlab/fin-infra
|
|
46
|
+
Project-URL: Issues, https://github.com/nfraxlab/fin-infra/issues
|
|
47
|
+
Project-URL: Repository, https://github.com/nfraxlab/fin-infra
|
|
40
48
|
Description-Content-Type: text/markdown
|
|
41
49
|
|
|
42
|
-
<div align="center">
|
|
43
|
-
|
|
44
50
|
# fin-infra
|
|
45
51
|
|
|
46
|
-
|
|
52
|
+
**Financial data infrastructure for fintech apps.**
|
|
47
53
|
|
|
48
|
-
[](https://pypi.org/project/fin-infra/)
|
|
54
|
+
[](https://pypi.org/project/fin-infra/)
|
|
55
|
+
[](https://github.com/nfraxlab/fin-infra/actions/workflows/ci.yml)
|
|
56
|
+
[](https://pypi.org/project/fin-infra/)
|
|
57
|
+
[](LICENSE)
|
|
49
58
|
|
|
50
|
-
|
|
59
|
+
## Overview
|
|
51
60
|
|
|
52
|
-
|
|
61
|
+
Banking, investments, market data, credit scores, and financial calculations in one toolkit.
|
|
53
62
|
|
|
54
|
-
|
|
63
|
+
### Key Features
|
|
55
64
|
|
|
56
|
-
|
|
65
|
+
- **Banking** - Plaid/Teller integration, accounts, transactions
|
|
66
|
+
- **Investments** - Holdings, portfolio data, real P/L with cost basis
|
|
67
|
+
- **Market Data** - Stocks, crypto, forex quotes and history
|
|
68
|
+
- **Credit** - Credit scores and monitoring
|
|
69
|
+
- **Analytics** - Cash flow, savings rate, spending insights
|
|
70
|
+
- **Cashflows** - NPV, IRR, loan amortization calculations
|
|
57
71
|
|
|
58
72
|
## Why fin-infra?
|
|
59
73
|
|