fin-infra 0.1.83__py3-none-any.whl → 0.1.85__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/analytics/cash_flow.py +3 -3
- fin_infra/analytics/models.py +5 -5
- fin_infra/analytics/spending.py +4 -5
- fin_infra/budgets/alerts.py +2 -2
- 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 +3 -3
- fin_infra/categorization/ease.py +3 -3
- fin_infra/categorization/models.py +3 -4
- fin_infra/documents/models.py +2 -3
- fin_infra/investments/ease.py +2 -2
- fin_infra/investments/models.py +24 -26
- fin_infra/models/accounts.py +4 -5
- fin_infra/models/transactions.py +2 -2
- fin_infra/normalization/currency_converter.py +4 -5
- fin_infra/normalization/models.py +9 -10
- fin_infra/normalization/symbol_resolver.py +3 -4
- fin_infra/security/add.py +1 -2
- fin_infra/security/audit.py +6 -7
- fin_infra/security/token_store.py +2 -3
- {fin_infra-0.1.83.dist-info → fin_infra-0.1.85.dist-info}/METADATA +1 -1
- {fin_infra-0.1.83.dist-info → fin_infra-0.1.85.dist-info}/RECORD +26 -26
- {fin_infra-0.1.83.dist-info → fin_infra-0.1.85.dist-info}/LICENSE +0 -0
- {fin_infra-0.1.83.dist-info → fin_infra-0.1.85.dist-info}/WHEEL +0 -0
- {fin_infra-0.1.83.dist-info → fin_infra-0.1.85.dist-info}/entry_points.txt +0 -0
fin_infra/analytics/cash_flow.py
CHANGED
|
@@ -7,7 +7,7 @@ from __future__ import annotations
|
|
|
7
7
|
|
|
8
8
|
from datetime import datetime, timedelta
|
|
9
9
|
from decimal import Decimal
|
|
10
|
-
from typing import Any
|
|
10
|
+
from typing import Any
|
|
11
11
|
|
|
12
12
|
from ..models import Transaction
|
|
13
13
|
from .models import CashFlowAnalysis
|
|
@@ -17,7 +17,7 @@ async def calculate_cash_flow(
|
|
|
17
17
|
user_id: str,
|
|
18
18
|
start_date: str | datetime,
|
|
19
19
|
end_date: str | datetime,
|
|
20
|
-
accounts:
|
|
20
|
+
accounts: list[str] | None = None,
|
|
21
21
|
*,
|
|
22
22
|
banking_provider=None,
|
|
23
23
|
categorization_provider=None,
|
|
@@ -117,7 +117,7 @@ async def calculate_cash_flow(
|
|
|
117
117
|
async def forecast_cash_flow(
|
|
118
118
|
user_id: str,
|
|
119
119
|
months: int = 6,
|
|
120
|
-
assumptions:
|
|
120
|
+
assumptions: dict[str, Any] | None = None,
|
|
121
121
|
*,
|
|
122
122
|
recurring_provider=None,
|
|
123
123
|
) -> list[CashFlowAnalysis]:
|
fin_infra/analytics/models.py
CHANGED
|
@@ -7,7 +7,7 @@ from __future__ import annotations
|
|
|
7
7
|
|
|
8
8
|
from datetime import datetime
|
|
9
9
|
from enum import Enum
|
|
10
|
-
from typing import Any
|
|
10
|
+
from typing import Any
|
|
11
11
|
|
|
12
12
|
from pydantic import BaseModel, ConfigDict, Field
|
|
13
13
|
|
|
@@ -72,7 +72,7 @@ class SavingsRateData(BaseModel):
|
|
|
72
72
|
expenses: float = Field(..., description="Total expenses for period")
|
|
73
73
|
period: Period = Field(..., description="Period type")
|
|
74
74
|
definition: SavingsDefinition = Field(..., description="Calculation method used")
|
|
75
|
-
trend:
|
|
75
|
+
trend: TrendDirection | None = Field(None, description="Trend over time")
|
|
76
76
|
|
|
77
77
|
|
|
78
78
|
class SpendingAnomaly(BaseModel):
|
|
@@ -132,7 +132,7 @@ class PersonalizedSpendingAdvice(BaseModel):
|
|
|
132
132
|
alerts: list[str] = Field(
|
|
133
133
|
default_factory=list, description="Urgent spending issues requiring attention"
|
|
134
134
|
)
|
|
135
|
-
estimated_monthly_savings:
|
|
135
|
+
estimated_monthly_savings: float | None = Field(
|
|
136
136
|
None, description="Potential monthly savings if recommendations followed"
|
|
137
137
|
)
|
|
138
138
|
|
|
@@ -183,7 +183,7 @@ class BenchmarkComparison(BaseModel):
|
|
|
183
183
|
benchmark_return_percent: float = Field(..., description="Benchmark return percentage")
|
|
184
184
|
benchmark_symbol: str = Field(..., description="Benchmark ticker (e.g., SPY)")
|
|
185
185
|
alpha: float = Field(..., description="Portfolio alpha (excess return)")
|
|
186
|
-
beta:
|
|
186
|
+
beta: float | None = Field(None, description="Portfolio beta (volatility vs benchmark)")
|
|
187
187
|
period: str = Field(..., description="Comparison period (1y, 3y, 5y, etc.)")
|
|
188
188
|
|
|
189
189
|
|
|
@@ -213,6 +213,6 @@ class GrowthProjection(BaseModel):
|
|
|
213
213
|
assumptions: dict[str, Any] = Field(
|
|
214
214
|
default_factory=dict, description="Assumptions used (inflation, returns, etc.)"
|
|
215
215
|
)
|
|
216
|
-
confidence_intervals:
|
|
216
|
+
confidence_intervals: dict[str, tuple[float, float]] | None = Field(
|
|
217
217
|
None, description="95% confidence intervals by scenario"
|
|
218
218
|
)
|
fin_infra/analytics/spending.py
CHANGED
|
@@ -45,7 +45,6 @@ Examples:
|
|
|
45
45
|
from collections import defaultdict
|
|
46
46
|
from datetime import timedelta
|
|
47
47
|
from decimal import Decimal
|
|
48
|
-
from typing import Optional
|
|
49
48
|
|
|
50
49
|
from fin_infra.analytics.models import (
|
|
51
50
|
PersonalizedSpendingAdvice,
|
|
@@ -60,7 +59,7 @@ async def analyze_spending(
|
|
|
60
59
|
user_id: str,
|
|
61
60
|
*,
|
|
62
61
|
period: str = "30d",
|
|
63
|
-
categories:
|
|
62
|
+
categories: list[str] | None = None,
|
|
64
63
|
banking_provider=None,
|
|
65
64
|
categorization_provider=None,
|
|
66
65
|
) -> SpendingInsight:
|
|
@@ -426,7 +425,7 @@ def _generate_mock_transactions(days: int) -> list[Transaction]:
|
|
|
426
425
|
async def generate_spending_insights(
|
|
427
426
|
spending_insight: SpendingInsight,
|
|
428
427
|
*,
|
|
429
|
-
user_context:
|
|
428
|
+
user_context: dict | None = None,
|
|
430
429
|
llm_provider=None,
|
|
431
430
|
) -> "PersonalizedSpendingAdvice":
|
|
432
431
|
"""Generate personalized spending insights using LLM.
|
|
@@ -521,7 +520,7 @@ async def generate_spending_insights(
|
|
|
521
520
|
|
|
522
521
|
def _build_spending_insights_prompt(
|
|
523
522
|
spending_insight: SpendingInsight,
|
|
524
|
-
user_context:
|
|
523
|
+
user_context: dict | None = None,
|
|
525
524
|
) -> str:
|
|
526
525
|
"""Build LLM prompt with financial context.
|
|
527
526
|
|
|
@@ -621,7 +620,7 @@ Be specific, encouraging, and actionable. Focus on realistic savings, not extrem
|
|
|
621
620
|
|
|
622
621
|
def _generate_rule_based_insights(
|
|
623
622
|
spending_insight: SpendingInsight,
|
|
624
|
-
user_context:
|
|
623
|
+
user_context: dict | None = None,
|
|
625
624
|
) -> "PersonalizedSpendingAdvice":
|
|
626
625
|
"""Generate rule-based insights when LLM is unavailable.
|
|
627
626
|
|
fin_infra/budgets/alerts.py
CHANGED
|
@@ -35,7 +35,7 @@ Example:
|
|
|
35
35
|
from __future__ import annotations
|
|
36
36
|
|
|
37
37
|
from datetime import datetime
|
|
38
|
-
from typing import TYPE_CHECKING
|
|
38
|
+
from typing import TYPE_CHECKING
|
|
39
39
|
|
|
40
40
|
from fin_infra.budgets.models import (
|
|
41
41
|
AlertSeverity,
|
|
@@ -51,7 +51,7 @@ if TYPE_CHECKING:
|
|
|
51
51
|
async def check_budget_alerts(
|
|
52
52
|
budget_id: str,
|
|
53
53
|
tracker: BudgetTracker,
|
|
54
|
-
thresholds:
|
|
54
|
+
thresholds: dict[str, float] | None = None,
|
|
55
55
|
) -> list[BudgetAlert]:
|
|
56
56
|
"""
|
|
57
57
|
Check budget for alerts (overspending, approaching limits, unusual patterns).
|
fin_infra/budgets/ease.py
CHANGED
|
@@ -12,7 +12,6 @@ Generic Design:
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
14
|
import os
|
|
15
|
-
from typing import Optional
|
|
16
15
|
|
|
17
16
|
from sqlalchemy.ext.asyncio import create_async_engine
|
|
18
17
|
|
|
@@ -20,7 +19,7 @@ from fin_infra.budgets.tracker import BudgetTracker
|
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
def easy_budgets(
|
|
23
|
-
db_url:
|
|
22
|
+
db_url: str | None = None,
|
|
24
23
|
pool_size: int = 5,
|
|
25
24
|
max_overflow: int = 10,
|
|
26
25
|
pool_pre_ping: bool = True,
|
fin_infra/budgets/models.py
CHANGED
|
@@ -8,7 +8,6 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
from datetime import datetime
|
|
10
10
|
from enum import Enum
|
|
11
|
-
from typing import Optional
|
|
12
11
|
|
|
13
12
|
from pydantic import BaseModel, ConfigDict, Field
|
|
14
13
|
|
|
@@ -296,7 +295,7 @@ class BudgetAlert(BaseModel):
|
|
|
296
295
|
"""
|
|
297
296
|
|
|
298
297
|
budget_id: str = Field(..., description="Budget identifier")
|
|
299
|
-
category:
|
|
298
|
+
category: str | None = Field(None, description="Category triggering alert")
|
|
300
299
|
alert_type: AlertType = Field(..., description="Type of alert")
|
|
301
300
|
threshold: float = Field(..., description="Threshold that triggered alert")
|
|
302
301
|
message: str = Field(..., description="Human-readable alert message")
|
fin_infra/budgets/templates.py
CHANGED
|
@@ -16,7 +16,7 @@ Generic Design:
|
|
|
16
16
|
from __future__ import annotations
|
|
17
17
|
|
|
18
18
|
from datetime import datetime
|
|
19
|
-
from typing import TYPE_CHECKING
|
|
19
|
+
from typing import TYPE_CHECKING
|
|
20
20
|
|
|
21
21
|
from fin_infra.budgets.models import Budget, BudgetPeriod, BudgetType
|
|
22
22
|
|
|
@@ -177,9 +177,9 @@ async def apply_template(
|
|
|
177
177
|
template_name: str,
|
|
178
178
|
total_income: float,
|
|
179
179
|
tracker: BudgetTracker,
|
|
180
|
-
budget_name:
|
|
181
|
-
start_date:
|
|
182
|
-
custom_template:
|
|
180
|
+
budget_name: str | None = None,
|
|
181
|
+
start_date: datetime | None = None,
|
|
182
|
+
custom_template: BudgetTemplate | None = None,
|
|
183
183
|
) -> Budget:
|
|
184
184
|
"""Apply a budget template to create a new budget.
|
|
185
185
|
|
fin_infra/budgets/tracker.py
CHANGED
|
@@ -36,7 +36,7 @@ from __future__ import annotations
|
|
|
36
36
|
|
|
37
37
|
import uuid
|
|
38
38
|
from datetime import datetime, timedelta
|
|
39
|
-
from typing import TYPE_CHECKING
|
|
39
|
+
from typing import TYPE_CHECKING
|
|
40
40
|
|
|
41
41
|
from sqlalchemy.ext.asyncio import async_sessionmaker
|
|
42
42
|
|
|
@@ -116,7 +116,7 @@ class BudgetTracker:
|
|
|
116
116
|
type: str, # BudgetType value
|
|
117
117
|
period: str, # BudgetPeriod value
|
|
118
118
|
categories: dict[str, float],
|
|
119
|
-
start_date:
|
|
119
|
+
start_date: datetime | None = None,
|
|
120
120
|
rollover_enabled: bool = False,
|
|
121
121
|
) -> Budget:
|
|
122
122
|
"""
|
|
@@ -205,7 +205,7 @@ class BudgetTracker:
|
|
|
205
205
|
async def get_budgets(
|
|
206
206
|
self,
|
|
207
207
|
user_id: str,
|
|
208
|
-
type:
|
|
208
|
+
type: str | None = None,
|
|
209
209
|
) -> list[Budget]:
|
|
210
210
|
"""
|
|
211
211
|
Get all budgets for a user.
|
fin_infra/categorization/ease.py
CHANGED
|
@@ -5,7 +5,7 @@ Provides one-line setup with sensible defaults.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Literal
|
|
8
|
+
from typing import Literal
|
|
9
9
|
|
|
10
10
|
from .engine import CategorizationEngine
|
|
11
11
|
|
|
@@ -21,10 +21,10 @@ def easy_categorization(
|
|
|
21
21
|
taxonomy: str = "mx",
|
|
22
22
|
enable_ml: bool = False,
|
|
23
23
|
confidence_threshold: float = 0.6,
|
|
24
|
-
model_path:
|
|
24
|
+
model_path: Path | None = None,
|
|
25
25
|
# LLM-specific parameters (V2)
|
|
26
26
|
llm_provider: Literal["google", "openai", "anthropic", "none"] = "google",
|
|
27
|
-
llm_model:
|
|
27
|
+
llm_model: str | None = None,
|
|
28
28
|
llm_confidence_threshold: float = 0.6,
|
|
29
29
|
llm_cache_ttl: int = 86400, # 24 hours
|
|
30
30
|
llm_max_cost_per_day: float = 0.10, # $0.10/day
|
|
@@ -4,7 +4,6 @@ Pydantic models for transaction categorization.
|
|
|
4
4
|
|
|
5
5
|
from datetime import datetime
|
|
6
6
|
from enum import Enum
|
|
7
|
-
from typing import Optional
|
|
8
7
|
|
|
9
8
|
from pydantic import BaseModel, ConfigDict, Field
|
|
10
9
|
|
|
@@ -34,7 +33,7 @@ class CategoryPrediction(BaseModel):
|
|
|
34
33
|
default_factory=list,
|
|
35
34
|
description="Alternative predictions (category, confidence)",
|
|
36
35
|
)
|
|
37
|
-
reasoning:
|
|
36
|
+
reasoning: str | None = Field(None, description="Explanation of prediction (for LLM)")
|
|
38
37
|
|
|
39
38
|
model_config = ConfigDict(
|
|
40
39
|
json_schema_extra={
|
|
@@ -100,7 +99,7 @@ class CategorizationRequest(BaseModel):
|
|
|
100
99
|
"""Request to categorize a merchant."""
|
|
101
100
|
|
|
102
101
|
merchant_name: str = Field(..., description="Merchant name to categorize")
|
|
103
|
-
user_id:
|
|
102
|
+
user_id: str | None = Field(None, description="User ID for personalized overrides")
|
|
104
103
|
include_alternatives: bool = Field(default=False, description="Include alternative predictions")
|
|
105
104
|
min_confidence: float = Field(
|
|
106
105
|
default=0.0,
|
|
@@ -152,7 +151,7 @@ class CategoryStats(BaseModel):
|
|
|
152
151
|
total_categories: int = Field(..., description="Total number of categories")
|
|
153
152
|
categories_by_group: dict[str, int] = Field(..., description="Category counts by group")
|
|
154
153
|
total_rules: int = Field(..., description="Total number of rules")
|
|
155
|
-
cache_hit_rate:
|
|
154
|
+
cache_hit_rate: float | None = Field(None, description="Cache hit rate (0-1)")
|
|
156
155
|
|
|
157
156
|
model_config = ConfigDict(
|
|
158
157
|
json_schema_extra={
|
fin_infra/documents/models.py
CHANGED
|
@@ -31,7 +31,6 @@ from __future__ import annotations
|
|
|
31
31
|
|
|
32
32
|
from datetime import datetime
|
|
33
33
|
from enum import Enum
|
|
34
|
-
from typing import Optional
|
|
35
34
|
|
|
36
35
|
from pydantic import BaseModel, ConfigDict, Field
|
|
37
36
|
from svc_infra.documents import Document as BaseDocument
|
|
@@ -111,8 +110,8 @@ class FinancialDocument(BaseDocument):
|
|
|
111
110
|
|
|
112
111
|
# Financial-specific fields
|
|
113
112
|
type: DocumentType = Field(..., description="Document type category (financial-specific)")
|
|
114
|
-
tax_year:
|
|
115
|
-
form_type:
|
|
113
|
+
tax_year: int | None = Field(None, description="Tax year for tax documents (e.g., 2024)")
|
|
114
|
+
form_type: str | None = Field(None, description="Tax form type (W-2, 1099-INT, 1040, etc.)")
|
|
116
115
|
|
|
117
116
|
|
|
118
117
|
# Backward compatibility: alias for existing code
|
fin_infra/investments/ease.py
CHANGED
|
@@ -8,13 +8,13 @@ and SnapTrade (retail brokerages) for maximum coverage.
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import os
|
|
11
|
-
from typing import Any, Literal
|
|
11
|
+
from typing import Any, Literal
|
|
12
12
|
|
|
13
13
|
from .providers.base import InvestmentProvider
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def easy_investments(
|
|
17
|
-
provider:
|
|
17
|
+
provider: Literal["plaid", "snaptrade"] | None = None,
|
|
18
18
|
**config: Any,
|
|
19
19
|
) -> InvestmentProvider:
|
|
20
20
|
"""Create investment provider with auto-configuration.
|
fin_infra/investments/models.py
CHANGED
|
@@ -20,7 +20,7 @@ from __future__ import annotations
|
|
|
20
20
|
from datetime import date
|
|
21
21
|
from decimal import Decimal
|
|
22
22
|
from enum import Enum
|
|
23
|
-
from typing import TYPE_CHECKING
|
|
23
|
+
from typing import TYPE_CHECKING
|
|
24
24
|
|
|
25
25
|
from pydantic import BaseModel, ConfigDict, Field, computed_field
|
|
26
26
|
|
|
@@ -121,22 +121,20 @@ class Security(BaseModel):
|
|
|
121
121
|
|
|
122
122
|
# Identifiers (at least one required for matching)
|
|
123
123
|
security_id: str = Field(..., description="Provider-specific security ID")
|
|
124
|
-
cusip:
|
|
125
|
-
isin:
|
|
126
|
-
sedol:
|
|
127
|
-
ticker_symbol:
|
|
124
|
+
cusip: str | None = Field(None, description="CUSIP identifier (US securities)")
|
|
125
|
+
isin: str | None = Field(None, description="ISIN identifier (international)")
|
|
126
|
+
sedol: str | None = Field(None, description="SEDOL identifier (UK securities)")
|
|
127
|
+
ticker_symbol: str | None = Field(None, description="Trading symbol (AAPL, GOOGL)")
|
|
128
128
|
|
|
129
129
|
# Basic info
|
|
130
130
|
name: str = Field(..., description="Security name")
|
|
131
131
|
type: SecurityType = Field(..., description="Security type (equity, etf, bond, etc.)")
|
|
132
|
-
sector:
|
|
133
|
-
None, description="Sector classification (Technology, Healthcare)"
|
|
134
|
-
)
|
|
132
|
+
sector: str | None = Field(None, description="Sector classification (Technology, Healthcare)")
|
|
135
133
|
|
|
136
134
|
# Market data
|
|
137
|
-
close_price:
|
|
138
|
-
close_price_as_of:
|
|
139
|
-
exchange:
|
|
135
|
+
close_price: Decimal | None = Field(None, ge=0, description="Latest closing price")
|
|
136
|
+
close_price_as_of: date | None = Field(None, description="Date of close_price")
|
|
137
|
+
exchange: str | None = Field(None, description="Exchange (NASDAQ, NYSE, etc.)")
|
|
140
138
|
currency: str = Field("USD", description="Currency code (USD, EUR, etc.)")
|
|
141
139
|
|
|
142
140
|
|
|
@@ -198,26 +196,26 @@ class Holding(BaseModel):
|
|
|
198
196
|
institution_value: Decimal = Field(
|
|
199
197
|
..., ge=0, description="Current market value (quantity × price)"
|
|
200
198
|
)
|
|
201
|
-
cost_basis:
|
|
199
|
+
cost_basis: Decimal | None = Field(
|
|
202
200
|
None, ge=0, description="Total cost basis (original purchase price)"
|
|
203
201
|
)
|
|
204
202
|
|
|
205
203
|
# Additional data
|
|
206
204
|
currency: str = Field("USD", description="Currency code")
|
|
207
|
-
unofficial_currency_code:
|
|
208
|
-
as_of_date:
|
|
205
|
+
unofficial_currency_code: str | None = Field(None, description="For crypto/alt currencies")
|
|
206
|
+
as_of_date: date | None = Field(None, description="Date of pricing data")
|
|
209
207
|
|
|
210
208
|
if TYPE_CHECKING:
|
|
211
209
|
|
|
212
210
|
@property
|
|
213
|
-
def unrealized_gain_loss(self) ->
|
|
211
|
+
def unrealized_gain_loss(self) -> Decimal | None:
|
|
214
212
|
"""Calculate unrealized gain/loss (current value - cost basis)."""
|
|
215
213
|
if self.cost_basis is None:
|
|
216
214
|
return None
|
|
217
215
|
return self.institution_value - self.cost_basis
|
|
218
216
|
|
|
219
217
|
@property
|
|
220
|
-
def unrealized_gain_loss_percent(self) ->
|
|
218
|
+
def unrealized_gain_loss_percent(self) -> Decimal | None:
|
|
221
219
|
"""Calculate unrealized gain/loss percentage."""
|
|
222
220
|
if self.cost_basis is None or self.cost_basis == 0:
|
|
223
221
|
return None
|
|
@@ -228,7 +226,7 @@ class Holding(BaseModel):
|
|
|
228
226
|
|
|
229
227
|
@computed_field
|
|
230
228
|
@property
|
|
231
|
-
def unrealized_gain_loss(self) ->
|
|
229
|
+
def unrealized_gain_loss(self) -> Decimal | None:
|
|
232
230
|
"""Calculate unrealized gain/loss (current value - cost basis)."""
|
|
233
231
|
if self.cost_basis is None:
|
|
234
232
|
return None
|
|
@@ -236,7 +234,7 @@ class Holding(BaseModel):
|
|
|
236
234
|
|
|
237
235
|
@computed_field
|
|
238
236
|
@property
|
|
239
|
-
def unrealized_gain_loss_percent(self) ->
|
|
237
|
+
def unrealized_gain_loss_percent(self) -> Decimal | None:
|
|
240
238
|
"""Calculate unrealized gain/loss percentage."""
|
|
241
239
|
if self.cost_basis is None or self.cost_basis == 0:
|
|
242
240
|
return None
|
|
@@ -304,17 +302,17 @@ class InvestmentTransaction(BaseModel):
|
|
|
304
302
|
transaction_type: TransactionType = Field(
|
|
305
303
|
..., alias="type", description="Transaction type (buy, sell, dividend)"
|
|
306
304
|
)
|
|
307
|
-
subtype:
|
|
305
|
+
subtype: str | None = Field(None, description="Provider-specific subtype")
|
|
308
306
|
|
|
309
307
|
# Amounts
|
|
310
308
|
quantity: Decimal = Field(..., description="Number of shares (0 for fees/dividends)")
|
|
311
309
|
amount: Decimal = Field(..., description="Transaction amount (negative for purchases)")
|
|
312
|
-
price:
|
|
313
|
-
fees:
|
|
310
|
+
price: Decimal | None = Field(None, ge=0, description="Price per share")
|
|
311
|
+
fees: Decimal | None = Field(None, ge=0, description="Transaction fees")
|
|
314
312
|
|
|
315
313
|
# Additional data
|
|
316
314
|
currency: str = Field("USD", description="Currency code")
|
|
317
|
-
unofficial_currency_code:
|
|
315
|
+
unofficial_currency_code: str | None = Field(None, description="For crypto/alt currencies")
|
|
318
316
|
|
|
319
317
|
|
|
320
318
|
class InvestmentAccount(BaseModel):
|
|
@@ -371,10 +369,10 @@ class InvestmentAccount(BaseModel):
|
|
|
371
369
|
account_id: str = Field(..., description="Account identifier")
|
|
372
370
|
name: str = Field(..., description="Account name (Fidelity 401k)")
|
|
373
371
|
type: str = Field(..., description="Account type (investment)")
|
|
374
|
-
subtype:
|
|
372
|
+
subtype: str | None = Field(None, description="Account subtype (401k, ira, brokerage)")
|
|
375
373
|
|
|
376
374
|
# Balances
|
|
377
|
-
balances: dict[str,
|
|
375
|
+
balances: dict[str, Decimal | None] = Field(
|
|
378
376
|
..., description="Current, available, and limit balances"
|
|
379
377
|
)
|
|
380
378
|
|
|
@@ -405,7 +403,7 @@ class InvestmentAccount(BaseModel):
|
|
|
405
403
|
return holdings_value - self.total_cost_basis
|
|
406
404
|
|
|
407
405
|
@property
|
|
408
|
-
def total_unrealized_gain_loss_percent(self) ->
|
|
406
|
+
def total_unrealized_gain_loss_percent(self) -> Decimal | None:
|
|
409
407
|
"""Calculate total unrealized P&L percentage."""
|
|
410
408
|
if self.total_cost_basis == 0:
|
|
411
409
|
return None
|
|
@@ -439,7 +437,7 @@ class InvestmentAccount(BaseModel):
|
|
|
439
437
|
|
|
440
438
|
@computed_field
|
|
441
439
|
@property
|
|
442
|
-
def total_unrealized_gain_loss_percent(self) ->
|
|
440
|
+
def total_unrealized_gain_loss_percent(self) -> Decimal | None:
|
|
443
441
|
"""Calculate total unrealized P&L percentage."""
|
|
444
442
|
if self.total_cost_basis == 0:
|
|
445
443
|
return None
|
fin_infra/models/accounts.py
CHANGED
|
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from decimal import Decimal
|
|
4
4
|
from enum import Enum
|
|
5
|
-
from typing import Optional
|
|
6
5
|
|
|
7
6
|
from pydantic import BaseModel, field_validator
|
|
8
7
|
|
|
@@ -26,11 +25,11 @@ class Account(BaseModel):
|
|
|
26
25
|
id: str
|
|
27
26
|
name: str
|
|
28
27
|
type: AccountType
|
|
29
|
-
mask:
|
|
28
|
+
mask: str | None = None
|
|
30
29
|
currency: str = "USD"
|
|
31
|
-
institution:
|
|
32
|
-
balance_available:
|
|
33
|
-
balance_current:
|
|
30
|
+
institution: str | None = None
|
|
31
|
+
balance_available: Decimal | None = None
|
|
32
|
+
balance_current: Decimal | None = None
|
|
34
33
|
|
|
35
34
|
@field_validator("balance_available", "balance_current", mode="before")
|
|
36
35
|
@classmethod
|
fin_infra/models/transactions.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import datetime
|
|
4
4
|
from decimal import Decimal
|
|
5
5
|
|
|
6
6
|
from pydantic import BaseModel, field_validator
|
|
@@ -15,7 +15,7 @@ class Transaction(BaseModel):
|
|
|
15
15
|
|
|
16
16
|
id: str
|
|
17
17
|
account_id: str
|
|
18
|
-
date: date
|
|
18
|
+
date: datetime.date
|
|
19
19
|
amount: Decimal
|
|
20
20
|
currency: str = "USD"
|
|
21
21
|
description: str | None = None
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
from datetime import date as DateType
|
|
5
|
-
from typing import Optional
|
|
6
5
|
|
|
7
6
|
from fin_infra.exceptions import CurrencyNotSupportedError, ExchangeRateAPIError
|
|
8
7
|
from fin_infra.normalization.models import CurrencyConversionResult
|
|
@@ -25,7 +24,7 @@ class CurrencyConverter:
|
|
|
25
24
|
Supports 160+ currencies including crypto (BTC, ETH).
|
|
26
25
|
"""
|
|
27
26
|
|
|
28
|
-
def __init__(self, api_key:
|
|
27
|
+
def __init__(self, api_key: str | None = None):
|
|
29
28
|
"""
|
|
30
29
|
Initialize currency converter.
|
|
31
30
|
|
|
@@ -39,7 +38,7 @@ class CurrencyConverter:
|
|
|
39
38
|
amount: float,
|
|
40
39
|
from_currency: str,
|
|
41
40
|
to_currency: str,
|
|
42
|
-
date:
|
|
41
|
+
date: DateType | None = None,
|
|
43
42
|
) -> float:
|
|
44
43
|
"""
|
|
45
44
|
Convert amount from one currency to another.
|
|
@@ -79,7 +78,7 @@ class CurrencyConverter:
|
|
|
79
78
|
self,
|
|
80
79
|
from_currency: str,
|
|
81
80
|
to_currency: str,
|
|
82
|
-
date:
|
|
81
|
+
date: DateType | None = None,
|
|
83
82
|
) -> float:
|
|
84
83
|
"""
|
|
85
84
|
Get exchange rate between two currencies.
|
|
@@ -142,7 +141,7 @@ class CurrencyConverter:
|
|
|
142
141
|
amount: float,
|
|
143
142
|
from_currency: str,
|
|
144
143
|
to_currency: str,
|
|
145
|
-
date:
|
|
144
|
+
date: DateType | None = None,
|
|
146
145
|
) -> CurrencyConversionResult:
|
|
147
146
|
"""
|
|
148
147
|
Convert amount with detailed result information.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Data models for normalization module."""
|
|
2
2
|
|
|
3
3
|
from datetime import date as DateType
|
|
4
|
-
from typing import Optional
|
|
5
4
|
|
|
6
5
|
from pydantic import BaseModel, Field
|
|
7
6
|
|
|
@@ -11,12 +10,12 @@ class SymbolMetadata(BaseModel):
|
|
|
11
10
|
|
|
12
11
|
ticker: str = Field(..., description="Standard ticker symbol")
|
|
13
12
|
name: str = Field(..., description="Company or asset name")
|
|
14
|
-
exchange:
|
|
15
|
-
cusip:
|
|
16
|
-
isin:
|
|
17
|
-
sector:
|
|
18
|
-
industry:
|
|
19
|
-
market_cap:
|
|
13
|
+
exchange: str | None = Field(None, description="Primary exchange (e.g., NASDAQ, NYSE)")
|
|
14
|
+
cusip: str | None = Field(None, description="CUSIP identifier")
|
|
15
|
+
isin: str | None = Field(None, description="ISIN identifier")
|
|
16
|
+
sector: str | None = Field(None, description="Business sector")
|
|
17
|
+
industry: str | None = Field(None, description="Industry classification")
|
|
18
|
+
market_cap: float | None = Field(None, description="Market capitalization in USD")
|
|
20
19
|
asset_type: str = Field(default="stock", description="Asset type: stock, etf, crypto, forex")
|
|
21
20
|
|
|
22
21
|
|
|
@@ -26,8 +25,8 @@ class ExchangeRate(BaseModel):
|
|
|
26
25
|
from_currency: str = Field(..., description="Source currency code (e.g., USD)")
|
|
27
26
|
to_currency: str = Field(..., description="Target currency code (e.g., EUR)")
|
|
28
27
|
rate: float = Field(..., description="Exchange rate (1 from_currency = rate to_currency)")
|
|
29
|
-
date:
|
|
30
|
-
timestamp:
|
|
28
|
+
date: DateType | None = Field(None, description="Rate date (None = current)")
|
|
29
|
+
timestamp: int | None = Field(None, description="Unix timestamp of rate")
|
|
31
30
|
|
|
32
31
|
|
|
33
32
|
class CurrencyConversionResult(BaseModel):
|
|
@@ -38,4 +37,4 @@ class CurrencyConversionResult(BaseModel):
|
|
|
38
37
|
to_currency: str = Field(..., description="Target currency")
|
|
39
38
|
converted: float = Field(..., description="Converted amount")
|
|
40
39
|
rate: float = Field(..., description="Exchange rate used")
|
|
41
|
-
date:
|
|
40
|
+
date: DateType | None = Field(None, description="Rate date")
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Symbol resolver for converting between ticker formats."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from typing import Optional
|
|
5
4
|
|
|
6
5
|
from fin_infra.exceptions import SymbolNotFoundError
|
|
7
6
|
from fin_infra.normalization.models import SymbolMetadata
|
|
@@ -231,9 +230,9 @@ class SymbolResolver:
|
|
|
231
230
|
def add_mapping(
|
|
232
231
|
self,
|
|
233
232
|
ticker: str,
|
|
234
|
-
cusip:
|
|
235
|
-
isin:
|
|
236
|
-
metadata:
|
|
233
|
+
cusip: str | None = None,
|
|
234
|
+
isin: str | None = None,
|
|
235
|
+
metadata: dict | None = None,
|
|
237
236
|
):
|
|
238
237
|
"""
|
|
239
238
|
Add or override a symbol mapping (useful for custom symbols).
|
fin_infra/security/add.py
CHANGED
|
@@ -5,7 +5,6 @@ Easy integration of financial PII masking and token encryption.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import logging
|
|
8
|
-
from typing import Optional
|
|
9
8
|
|
|
10
9
|
from fastapi import FastAPI
|
|
11
10
|
|
|
@@ -15,7 +14,7 @@ from .pii_filter import FinancialPIIFilter
|
|
|
15
14
|
|
|
16
15
|
def add_financial_security(
|
|
17
16
|
app: FastAPI,
|
|
18
|
-
encryption_key:
|
|
17
|
+
encryption_key: bytes | None = None,
|
|
19
18
|
enable_pii_filter: bool = True,
|
|
20
19
|
enable_audit_log: bool = True,
|
|
21
20
|
mask_emails: bool = False,
|
fin_infra/security/audit.py
CHANGED
|
@@ -6,7 +6,6 @@ Track access to sensitive financial data for compliance.
|
|
|
6
6
|
|
|
7
7
|
import logging
|
|
8
8
|
from datetime import datetime
|
|
9
|
-
from typing import Optional
|
|
10
9
|
|
|
11
10
|
from .models import PIIAccessLog
|
|
12
11
|
|
|
@@ -23,10 +22,10 @@ async def log_pii_access(
|
|
|
23
22
|
pii_type: str,
|
|
24
23
|
action: str,
|
|
25
24
|
resource: str,
|
|
26
|
-
ip_address:
|
|
27
|
-
user_agent:
|
|
25
|
+
ip_address: str | None = None,
|
|
26
|
+
user_agent: str | None = None,
|
|
28
27
|
success: bool = True,
|
|
29
|
-
error_message:
|
|
28
|
+
error_message: str | None = None,
|
|
30
29
|
) -> PIIAccessLog:
|
|
31
30
|
"""
|
|
32
31
|
Log PII access for audit trail.
|
|
@@ -86,9 +85,9 @@ async def log_pii_access(
|
|
|
86
85
|
|
|
87
86
|
|
|
88
87
|
def get_audit_logs(
|
|
89
|
-
user_id:
|
|
90
|
-
pii_type:
|
|
91
|
-
action:
|
|
88
|
+
user_id: str | None = None,
|
|
89
|
+
pii_type: str | None = None,
|
|
90
|
+
action: str | None = None,
|
|
92
91
|
limit: int = 100,
|
|
93
92
|
) -> list[PIIAccessLog]:
|
|
94
93
|
"""
|
|
@@ -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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: fin-infra
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.85
|
|
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
|
|
@@ -2,39 +2,39 @@ fin_infra/__init__.py,sha256=7oL-CCsALNifBODAn9LriicaIrzgJkmVPvE-9duP0mw,1574
|
|
|
2
2
|
fin_infra/__main__.py,sha256=1qNP7j0ffw0wFs1dBwDcJ9TNXlC6FcYuulzoV87pMi8,262
|
|
3
3
|
fin_infra/analytics/__init__.py,sha256=zjAOFkI9xjg76ozZStYBXEPNwIsioj5fUBltja5JLyc,2327
|
|
4
4
|
fin_infra/analytics/add.py,sha256=-iAqKdvf9t0gQjjo3uOO56qA_ei4omKYDOynxb4SZPI,12654
|
|
5
|
-
fin_infra/analytics/cash_flow.py,sha256=
|
|
5
|
+
fin_infra/analytics/cash_flow.py,sha256=VSsQHwTi6r6VFoScH9nu9Fj8XHC4O4B9TkAI8Alobm4,10284
|
|
6
6
|
fin_infra/analytics/ease.py,sha256=aTAPc-Lmh6XP7REPVlAom38MtKCqeS-auNW-rpXRZJs,14282
|
|
7
|
-
fin_infra/analytics/models.py,sha256=
|
|
7
|
+
fin_infra/analytics/models.py,sha256=XFHeVZoPE0PcSFj59dNsdxISFVwdrTLU4sDCtExumrg,8193
|
|
8
8
|
fin_infra/analytics/portfolio.py,sha256=d39sZGa3TQVX1xN2nplPHuxhsE-rahuiyzd612DeZ_Y,26417
|
|
9
9
|
fin_infra/analytics/projections.py,sha256=T7LLG0Pe5f-WwgfDITdTMk-l8cCLZTM9cEC_Vxf6mkc,9154
|
|
10
10
|
fin_infra/analytics/rebalancing.py,sha256=VM8MgoJofmrCXPK1rbmVqWGB4FauNmHCL5HOEbrZR2g,14610
|
|
11
11
|
fin_infra/analytics/savings.py,sha256=n3rGNFP8TU5mW-uz9kOuqX_mDiVnDyAeDN06Q7Abotw,7570
|
|
12
12
|
fin_infra/analytics/scenarios.py,sha256=LE_dZVkbxxAx5sxitGhiOhZfWTlYtVbIvS9pEXkijLc,12246
|
|
13
|
-
fin_infra/analytics/spending.py,sha256=
|
|
13
|
+
fin_infra/analytics/spending.py,sha256=ogLcfF5ZOLMkBIj02RISnA3hiY_PsLWZ2_AAzA7FenY,26209
|
|
14
14
|
fin_infra/banking/__init__.py,sha256=6wwGNITzyehC9MBQc5jy0ewSTuNetyQu2AdND51O55w,22450
|
|
15
15
|
fin_infra/banking/history.py,sha256=BYvKLI_F198TBrtP7vMCCcBcszzFxZkmD-ClCezSVW8,10575
|
|
16
16
|
fin_infra/banking/utils.py,sha256=HhxZbeaA8zqVttgMiJGnShTo_r_0DaD7T3IMq8n8340,15252
|
|
17
17
|
fin_infra/brokerage/__init__.py,sha256=wJpgdOMWNxKt01eUIbu2oSmcFlL189nlWR0-PhLLVFc,17154
|
|
18
18
|
fin_infra/budgets/__init__.py,sha256=GgfujfgdVld6_yUf8bhkW-bsIXBLb4THlJMocVpA4mM,3986
|
|
19
19
|
fin_infra/budgets/add.py,sha256=i7Av--Z14sYQDJHETiF9VL71ouWBZcs3H3VZ62gqFEo,13508
|
|
20
|
-
fin_infra/budgets/alerts.py,sha256=
|
|
21
|
-
fin_infra/budgets/ease.py,sha256=
|
|
22
|
-
fin_infra/budgets/models.py,sha256=
|
|
20
|
+
fin_infra/budgets/alerts.py,sha256=XDSUPpRR1LNgSQU0Szlht0bLWI7RRBh1ElbcwGexbPs,9701
|
|
21
|
+
fin_infra/budgets/ease.py,sha256=SB0hEuKw7avb32Ax1jY471hh7Y4Nhc4YtNOL7z1BOs0,8172
|
|
22
|
+
fin_infra/budgets/models.py,sha256=q_6aqzC3fT-h5lJfTy2YjwBJgHAJha9e18KkuyxG2Dc,14304
|
|
23
23
|
fin_infra/budgets/scaffold_templates/README.md,sha256=FBtRSrQSkg7Xp8SPiuSmzGR94I2zzjOckb0_vKtcksY,14084
|
|
24
24
|
fin_infra/budgets/scaffold_templates/__init__.py,sha256=zQXi_vY4QS5a4Ddi2mVXMLM--rP021h5D94ohIC1dkU,172
|
|
25
25
|
fin_infra/budgets/scaffold_templates/models.py.tmpl,sha256=rpKhXwnx1gQjV_GGVqs8CvkFNHctrYcagC9swE14pVU,7553
|
|
26
26
|
fin_infra/budgets/scaffold_templates/repository.py.tmpl,sha256=khFgnQnVNnOo8DWYDmYz58MvdeSJpoS9QvTXcGhsa8g,10023
|
|
27
27
|
fin_infra/budgets/scaffold_templates/schemas.py.tmpl,sha256=x5gSQ7Kiuq08tum5joKmeY0ib2r3ekLKk09dFOl0PS0,5658
|
|
28
|
-
fin_infra/budgets/templates.py,sha256=
|
|
29
|
-
fin_infra/budgets/tracker.py,sha256=
|
|
28
|
+
fin_infra/budgets/templates.py,sha256=V1Hfh0P_6iMdaSLtnFm9iEvXDUyFb7yqBObat98hDjU,12093
|
|
29
|
+
fin_infra/budgets/tracker.py,sha256=RDPfYhOLSAp-RFlTWFTpYaB5mysu3QfQhhcqJzmVWUU,16444
|
|
30
30
|
fin_infra/cashflows/__init__.py,sha256=N9ZGKwIrQs-eiZZxqCow2aLBhW1MKcJoasQjGtyV6l4,8517
|
|
31
31
|
fin_infra/cashflows/core.py,sha256=YWvF0DVOfBkvO_MuDODjE-V_g52H2ixFRH_TjMXExDE,541
|
|
32
32
|
fin_infra/categorization/__init__.py,sha256=efLje12AW-ec9Vs5ynb41r4XCIWx5a-Z9WoGb3kQdIE,2030
|
|
33
33
|
fin_infra/categorization/add.py,sha256=-I-V7R8vWj2-B8yjtFn_pNryQV5uascFR18a1Ltcqm0,6247
|
|
34
|
-
fin_infra/categorization/ease.py,sha256=
|
|
34
|
+
fin_infra/categorization/ease.py,sha256=gvlVb_TL7kwm5oM_gWdrhZt4cJOSvYEgGipNJKK8CpM,5820
|
|
35
35
|
fin_infra/categorization/engine.py,sha256=u1ZYDmuB0gZeJHn-CKvvwd7Z1HFiB-S91YuV_ZrK_Rc,12117
|
|
36
36
|
fin_infra/categorization/llm_layer.py,sha256=CSe6q-cKQgnKUKSItdDQwzMd8K1dsp-Mwua7SWZ9Ux0,12670
|
|
37
|
-
fin_infra/categorization/models.py,sha256
|
|
37
|
+
fin_infra/categorization/models.py,sha256=A8m3hAIbsUUV3eNjXpTiPWD1sYGjSdXEByTS3ZZASEA,5894
|
|
38
38
|
fin_infra/categorization/rules.py,sha256=IpDnHBeuykRdu5vs3Lph4Y9-3RseIjjleQ5hZphQvNk,12849
|
|
39
39
|
fin_infra/categorization/taxonomy.py,sha256=p-tSOwJ0O-rFZ1LIlHSYdaYdSc65084j0fdMI_6LW84,13251
|
|
40
40
|
fin_infra/chat/__init__.py,sha256=-gufQz-rOZVXkcTUcCcXdG2oGL8csJBxwSuOMN_5aVc,6324
|
|
@@ -61,7 +61,7 @@ fin_infra/documents/__init__.py,sha256=Ub1hbX3PTrBSsBdcbL8PFf6oq8jSH4pYxW45-qOYP
|
|
|
61
61
|
fin_infra/documents/add.py,sha256=2RSf_i39-JUT6c_jVsEQwW3FfQSlH4H9Z0t_H0vz86U,8091
|
|
62
62
|
fin_infra/documents/analysis.py,sha256=UWKTR_qXZoumItXUwHFMH3LAG2vFVlPITbIC8Y_r0s8,13970
|
|
63
63
|
fin_infra/documents/ease.py,sha256=z8hLaYhO1znBbcA9F10uNVa1BnH4rm3Jm5pexQwyiJY,9574
|
|
64
|
-
fin_infra/documents/models.py,sha256=
|
|
64
|
+
fin_infra/documents/models.py,sha256=VEkJynM2REAf37ZachIz7_MfTevpzmG4Cmpku388YwE,6831
|
|
65
65
|
fin_infra/documents/ocr.py,sha256=8I0XhiEdDhew55jUxOB5whoO5OcSXD71xiv8HhnyYno,9565
|
|
66
66
|
fin_infra/documents/storage.py,sha256=HXfubl-4PspvA-U8czB-4TgZKI1kOlw440jYHRDzjoY,10300
|
|
67
67
|
fin_infra/exceptions.py,sha256=k7L3vfXcONM2-AqmqNhQvOHobVBrLPTYp44cMUxuP3o,16951
|
|
@@ -81,8 +81,8 @@ fin_infra/insights/aggregator.py,sha256=HtaJipSA-O_HVComBcyQdkGs6guoW81sYwRYHXGd
|
|
|
81
81
|
fin_infra/insights/models.py,sha256=xov_YV8oBLJt3YdyVjbryRfcXqmGeGiPvZsZHSbvtl8,3202
|
|
82
82
|
fin_infra/investments/__init__.py,sha256=DQJa32LdXspbtxydric-AfT3o5iDkqIByAsSriSfGak,6729
|
|
83
83
|
fin_infra/investments/add.py,sha256=UV2_99z1p8cUiifVFJXOF0lGd0ucMgZ5s9N7IFyE_NY,17193
|
|
84
|
-
fin_infra/investments/ease.py,sha256=
|
|
85
|
-
fin_infra/investments/models.py,sha256=
|
|
84
|
+
fin_infra/investments/ease.py,sha256=s716nYv5kzkJ-vGbVeIumqAN0-TEl1ai-If84uP9h68,9474
|
|
85
|
+
fin_infra/investments/models.py,sha256=Es1GdrB640k50gtdzTtfh_66nxvYxORq0xbtXryoRGo,18319
|
|
86
86
|
fin_infra/investments/providers/__init__.py,sha256=V1eIzz6EnGJ-pq-9L3S2-evmcExF-YdZfd5P6JMyDtc,383
|
|
87
87
|
fin_infra/investments/providers/base.py,sha256=1plM-o_fV6jQyyPnpZT5q8ir96oyxvh0zwA3qpiVIFY,9810
|
|
88
88
|
fin_infra/investments/providers/plaid.py,sha256=X42cMpsHNcOPcYvl73jrP8x7nQ399n2_it3Ko9UIkI0,18751
|
|
@@ -94,14 +94,14 @@ fin_infra/investments/scaffold_templates/repository.py.tmpl,sha256=XwOEpQZfuXut1
|
|
|
94
94
|
fin_infra/investments/scaffold_templates/schemas.py.tmpl,sha256=knWmn-Kyr7AdgPD4ZPMb6T49ZuPXeuOMqmjYNyA0CA0,5451
|
|
95
95
|
fin_infra/markets/__init__.py,sha256=Ba9iUTfDkkla8GwPo3gULTJYUDQh5gSgGN15BxHINpo,9865
|
|
96
96
|
fin_infra/models/__init__.py,sha256=y94RJ_1-bzgNUCxqE76X56WIOk3-El_Jueqy7uB0rb8,860
|
|
97
|
-
fin_infra/models/accounts.py,sha256=
|
|
97
|
+
fin_infra/models/accounts.py,sha256=m_HdYHOe_m0GLnc_f5njo9n-zscWu-C0rJB6SAd5-aY,1098
|
|
98
98
|
fin_infra/models/brokerage.py,sha256=TV5KMe78e-ttjcUbZIfdGo3x0NisAQ3puwv_ehtgSHc,8312
|
|
99
99
|
fin_infra/models/candle.py,sha256=5JeqUvqlFfYE61uViBeLKZCh2wKdJZH7Pvi-TLzN4FI,536
|
|
100
100
|
fin_infra/models/credit.py,sha256=rSdSURsMe9_i2gxmwPTDwNQWOuM2zutL-OhvHsnbtmw,12144
|
|
101
101
|
fin_infra/models/money.py,sha256=63pdGD1WBMHicJ1w7pbU1g5fqt4gIzPuqQQ2-NSlBuc,401
|
|
102
102
|
fin_infra/models/quotes.py,sha256=-YBzgnjjCihRAprUdaPRtfwKrgliDIGgqAnaM69VbDU,521
|
|
103
103
|
fin_infra/models/tax.py,sha256=as40J9h24BB7LmeaIfay509UaYEctmk5CPW9CfcjWZc,15657
|
|
104
|
-
fin_infra/models/transactions.py,sha256=
|
|
104
|
+
fin_infra/models/transactions.py,sha256=gzc6hLKKU6VUWGshqw2WChU_x0qyXhdvOGwmj9H_H3I,790
|
|
105
105
|
fin_infra/net_worth/__init__.py,sha256=4LzVM2Y0OkBlZoD5FUAbLwtORp1okYcsYc4zj0P8C7k,3452
|
|
106
106
|
fin_infra/net_worth/add.py,sha256=QWfHIHJs2CV99WRBqjQ2OteiOrn5cR9nurmxTF9v5rg,23191
|
|
107
107
|
fin_infra/net_worth/aggregator.py,sha256=9Kx2vUR71QwqYZdGaCfmYrJ1hNxzd1EEuAdWJoNjqTI,12780
|
|
@@ -116,12 +116,12 @@ fin_infra/net_worth/scaffold_templates/models.py.tmpl,sha256=9BKsoD08RZbSdOm0wFT
|
|
|
116
116
|
fin_infra/net_worth/scaffold_templates/repository.py.tmpl,sha256=DSErnNxeAe4pWeefARRK3bU0hHltqdIFffENfVwdd7c,12798
|
|
117
117
|
fin_infra/net_worth/scaffold_templates/schemas.py.tmpl,sha256=VkFsxyZx4DFDhXDhn-7KT0IgrXCvgaS5ZdWbjyezWj0,4709
|
|
118
118
|
fin_infra/normalization/__init__.py,sha256=O-8YvPbKWgfdC6E2qzXuCFLJ6cM-CS8GmBO8TDcJ9X0,6246
|
|
119
|
-
fin_infra/normalization/currency_converter.py,sha256=
|
|
120
|
-
fin_infra/normalization/models.py,sha256=
|
|
119
|
+
fin_infra/normalization/currency_converter.py,sha256=dhiSAYD7onQgCiT17TtGzL4czeX_qJ9_yW2zsvvBErg,7018
|
|
120
|
+
fin_infra/normalization/models.py,sha256=h8zC2n642WwVWC1yvx_VCILDOgUwPDrZwjb0QIbA8cE,1873
|
|
121
121
|
fin_infra/normalization/providers/__init__.py,sha256=LFU1tB2hVO42Yrkw-IDpPexD4mIlxob9lRrJEeGYqpE,559
|
|
122
122
|
fin_infra/normalization/providers/exchangerate.py,sha256=I_2TK_LLOcYCy3HcN9Nut3UwqTWEOX26GktmX3cczvY,6340
|
|
123
123
|
fin_infra/normalization/providers/static_mappings.py,sha256=m14VHmTZipbqrgyE0ABToabVx-pDcyB577LNWrACEUM,6809
|
|
124
|
-
fin_infra/normalization/symbol_resolver.py,sha256=
|
|
124
|
+
fin_infra/normalization/symbol_resolver.py,sha256=UAfhxmiINpQhE8tPLh-GMCfDZGmK5wf2DwtYZP3sSUo,8093
|
|
125
125
|
fin_infra/obs/__init__.py,sha256=kMMVl0fdwtJtZeKiusTuw0iO61Jo9-HNXsLmn3ffLRE,631
|
|
126
126
|
fin_infra/obs/classifier.py,sha256=JasWCqSkYjllJNZ5Gwbrd53ZhLwhYNZ0i2nbTcklEog,5155
|
|
127
127
|
fin_infra/providers/__init__.py,sha256=jxhQm79T6DVXf7Wpy7luL-p50cE_IMUbjt4o3apzJQU,768
|
|
@@ -158,13 +158,13 @@ fin_infra/scaffold/__init__.py,sha256=IfL_CHHMpQB1efqY37BlIu07356tLaeVI2Mv3C0qYD
|
|
|
158
158
|
fin_infra/scaffold/budgets.py,sha256=qLnyPh-a-ZrslstaxRtR__Bpwlz0EncSdVnCM7fDBI4,9481
|
|
159
159
|
fin_infra/scaffold/goals.py,sha256=C1mexWRbWV3aRdmiT65LpKMMWzhTWTX_upeCcsjTcWw,9684
|
|
160
160
|
fin_infra/security/__init__.py,sha256=wM341EZioKtfTawpFCciaFh0FlpHHkoEoviW9pGYqpI,1363
|
|
161
|
-
fin_infra/security/add.py,sha256=
|
|
162
|
-
fin_infra/security/audit.py,sha256=
|
|
161
|
+
fin_infra/security/add.py,sha256=SIMBlSQ4FWCNiBfzgbzeVpRoo2JS3dqW-51uvq3hQU0,2715
|
|
162
|
+
fin_infra/security/audit.py,sha256=nb4H2Jq7XwalHLOx6LqleUtSXkBRz6u65LJ7lyuQNDo,3276
|
|
163
163
|
fin_infra/security/encryption.py,sha256=Cr_9xhBiUvYx1tDjCiLTzyY9NRTlqljWGgswqXT499Y,6139
|
|
164
164
|
fin_infra/security/models.py,sha256=s8dsvpxP-7DThUZl3Fvr3S6KVAfL5r3TKcd4uj_hCTE,1689
|
|
165
165
|
fin_infra/security/pii_filter.py,sha256=nVqzbG_-Z6m8fzzS1KBy9yFwaZ8eRrU00Q72fSkVyU8,8307
|
|
166
166
|
fin_infra/security/pii_patterns.py,sha256=SM-o7cL6NdgkOmtBedsN2nJZ5QPbeYehZdYmAujk8Y8,3070
|
|
167
|
-
fin_infra/security/token_store.py,sha256=
|
|
167
|
+
fin_infra/security/token_store.py,sha256=FsfoAkIMQ2NRfkBuyG1eH30nnfO3w_V5tDPAZwUj9Os,6041
|
|
168
168
|
fin_infra/settings.py,sha256=11JgIhjGwWnwixV-hveEWpoWd_JC0ixLnOQoLWCiwNo,1387
|
|
169
169
|
fin_infra/tax/__init__.py,sha256=U0EUKQwbDqnAYwU8WRx6AD07TaB-ELdKGISOQ-904lw,6103
|
|
170
170
|
fin_infra/tax/add.py,sha256=dYigcgQFEP38NLoNuhh7vpSoy07sWX6Z_DTp1tWFJXw,14553
|
|
@@ -173,8 +173,8 @@ fin_infra/utils/__init__.py,sha256=gKacLSWMAis--pasd8AuVN7ap0e9Z1TjRGur0J23EDo,6
|
|
|
173
173
|
fin_infra/utils/http.py,sha256=rDEgYsEBrEe75ml5RA-iSs3xeU5W-3j-czJlT7WbrM4,632
|
|
174
174
|
fin_infra/utils/retry.py,sha256=YiyTgy26eJ1ah7fE2_-ZPa4hv4bIT4OzjYolkNWb5j0,1057
|
|
175
175
|
fin_infra/version.py,sha256=4t_crzhrLum--oyowUMxtjBTzUtWp7oRTF22ewEvJG4,49
|
|
176
|
-
fin_infra-0.1.
|
|
177
|
-
fin_infra-0.1.
|
|
178
|
-
fin_infra-0.1.
|
|
179
|
-
fin_infra-0.1.
|
|
180
|
-
fin_infra-0.1.
|
|
176
|
+
fin_infra-0.1.85.dist-info/LICENSE,sha256=wK-Ya7Ylxa38dSIZRhvNj1ZVLIrHC-BAI8v38PNADiA,1061
|
|
177
|
+
fin_infra-0.1.85.dist-info/METADATA,sha256=y13UC--sOtnmoLfYI4s1Z_THKBcpLzH3JaYaXOH41y8,10479
|
|
178
|
+
fin_infra-0.1.85.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
|
179
|
+
fin_infra-0.1.85.dist-info/entry_points.txt,sha256=Sr1uikvALZMeKm-DIkeKG4L9c4SNqysXGO_IRF8_9eU,53
|
|
180
|
+
fin_infra-0.1.85.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|