fin-infra 0.1.55__py3-none-any.whl → 0.1.57__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.
@@ -4,6 +4,9 @@ This module provides functionality to record and retrieve historical account bal
4
4
  snapshots over time. This enables balance trend analysis, sparklines, and time-series
5
5
  visualizations in fintech dashboards.
6
6
 
7
+ ⚠️ WARNING: This module uses IN-MEMORY storage by default. All data is LOST on restart.
8
+ For production use, integrate with svc-infra SQL database or set FIN_INFRA_STORAGE_BACKEND.
9
+
7
10
  Features:
8
11
  - Record daily balance snapshots for accounts
9
12
  - Store snapshots in time-series optimized format
@@ -36,6 +39,8 @@ Integration with svc-infra:
36
39
 
37
40
  from __future__ import annotations
38
41
 
42
+ import logging
43
+ import os
39
44
  from datetime import date, datetime, timedelta
40
45
  from typing import List, Optional
41
46
  from pydantic import BaseModel, Field, ConfigDict
@@ -50,8 +55,31 @@ __all__ = [
50
55
  ]
51
56
 
52
57
 
58
+ _logger = logging.getLogger(__name__)
59
+
53
60
  # In-memory storage for testing (will be replaced with SQL database in production)
61
+ # ⚠️ WARNING: All data is LOST on restart when using in-memory storage!
54
62
  _balance_snapshots: List[BalanceSnapshot] = []
63
+ _production_warning_logged = False
64
+
65
+
66
+ def _check_in_memory_warning() -> None:
67
+ """Log a warning if using in-memory storage in production."""
68
+ global _production_warning_logged
69
+ if _production_warning_logged:
70
+ return
71
+
72
+ env = os.getenv("ENV", "development").lower()
73
+ storage_backend = os.getenv("FIN_INFRA_STORAGE_BACKEND", "memory").lower()
74
+
75
+ if env in ("production", "staging") and storage_backend == "memory":
76
+ _logger.warning(
77
+ "⚠️ CRITICAL: Balance history using IN-MEMORY storage in %s environment! "
78
+ "All balance snapshots will be LOST on restart. "
79
+ "Set FIN_INFRA_STORAGE_BACKEND=sql for production persistence.",
80
+ env,
81
+ )
82
+ _production_warning_logged = True
55
83
 
56
84
 
57
85
  class BalanceSnapshot(BaseModel):
@@ -87,6 +115,8 @@ def record_balance_snapshot(
87
115
  This function stores a point-in-time balance record for trend analysis.
88
116
  In production, this would write to a SQL database via svc-infra.
89
117
 
118
+ ⚠️ WARNING: Uses in-memory storage by default. Data is LOST on restart!
119
+
90
120
  Args:
91
121
  account_id: Account identifier
92
122
  balance: Account balance at the snapshot time
@@ -103,6 +133,9 @@ def record_balance_snapshot(
103
133
  - In production, use unique constraint on (account_id, date) in SQL
104
134
  - Consider using svc-infra jobs for automatic daily snapshots
105
135
  """
136
+ # Check if in-memory storage is being used in production
137
+ _check_in_memory_warning()
138
+
106
139
  snapshot = BalanceSnapshot(
107
140
  account_id=account_id,
108
141
  balance=balance,
@@ -20,7 +20,6 @@ from ..models import (
20
20
  InvestmentAccount,
21
21
  InvestmentTransaction,
22
22
  Security,
23
- SecurityType,
24
23
  TransactionType,
25
24
  )
26
25
  from .base import InvestmentProvider
@@ -96,6 +95,25 @@ class SnapTradeInvestmentProvider(InvestmentProvider):
96
95
  timeout=30.0,
97
96
  )
98
97
 
98
+ def _auth_headers(self, user_id: str, user_secret: str) -> Dict[str, str]:
99
+ """Build authentication headers for SnapTrade API requests.
100
+
101
+ SECURITY: User secrets are passed in headers, NOT URL params.
102
+ URL params are logged in access logs, browser history, and proxy logs.
103
+ Headers are not logged by default in most web servers.
104
+
105
+ Args:
106
+ user_id: SnapTrade user ID
107
+ user_secret: SnapTrade user secret (sensitive!)
108
+
109
+ Returns:
110
+ Dict with authentication headers
111
+ """
112
+ return {
113
+ "userId": user_id,
114
+ "userSecret": user_secret,
115
+ }
116
+
99
117
  async def get_holdings(
100
118
  self,
101
119
  access_token: str,
@@ -123,12 +141,12 @@ class SnapTradeInvestmentProvider(InvestmentProvider):
123
141
  ... print(f"{holding.security.ticker_symbol}: P&L ${pnl}")
124
142
  """
125
143
  user_id, user_secret = self._parse_access_token(access_token)
144
+ auth_headers = self._auth_headers(user_id, user_secret)
126
145
 
127
146
  try:
128
147
  # Get all accounts
129
148
  accounts_url = f"{self.base_url}/accounts"
130
- params = {"userId": user_id, "userSecret": user_secret}
131
- response = await self.client.get(accounts_url, params=params)
149
+ response = await self.client.get(accounts_url, headers=auth_headers)
132
150
  response.raise_for_status()
133
151
  accounts = await response.json()
134
152
 
@@ -143,8 +161,7 @@ class SnapTradeInvestmentProvider(InvestmentProvider):
143
161
  positions_url = (
144
162
  f"{self.base_url}/accounts/{account_id}/positions"
145
163
  )
146
- pos_params = {"userId": user_id, "userSecret": user_secret}
147
- pos_response = await self.client.get(positions_url, params=pos_params)
164
+ pos_response = await self.client.get(positions_url, headers=auth_headers)
148
165
  pos_response.raise_for_status()
149
166
  positions = await pos_response.json()
150
167
 
@@ -192,12 +209,12 @@ class SnapTradeInvestmentProvider(InvestmentProvider):
192
209
  raise ValueError("start_date must be before end_date")
193
210
 
194
211
  user_id, user_secret = self._parse_access_token(access_token)
212
+ auth_headers = self._auth_headers(user_id, user_secret)
195
213
 
196
214
  try:
197
215
  # Get all accounts
198
216
  accounts_url = f"{self.base_url}/accounts"
199
- params = {"userId": user_id, "userSecret": user_secret}
200
- response = await self.client.get(accounts_url, params=params)
217
+ response = await self.client.get(accounts_url, headers=auth_headers)
201
218
  response.raise_for_status()
202
219
  accounts = await response.json()
203
220
 
@@ -212,13 +229,12 @@ class SnapTradeInvestmentProvider(InvestmentProvider):
212
229
  transactions_url = (
213
230
  f"{self.base_url}/accounts/{account_id}/transactions"
214
231
  )
232
+ # Date params are non-sensitive, only auth goes in headers
215
233
  tx_params = {
216
- "userId": user_id,
217
- "userSecret": user_secret,
218
234
  "startDate": start_date.isoformat(),
219
235
  "endDate": end_date.isoformat(),
220
236
  }
221
- tx_response = await self.client.get(transactions_url, params=tx_params)
237
+ tx_response = await self.client.get(transactions_url, params=tx_params, headers=auth_headers)
222
238
  tx_response.raise_for_status()
223
239
  transactions = await tx_response.json()
224
240
 
@@ -297,12 +313,12 @@ class SnapTradeInvestmentProvider(InvestmentProvider):
297
313
  ... print(f" P&L: {account.total_unrealized_gain_loss_percent:.2f}%")
298
314
  """
299
315
  user_id, user_secret = self._parse_access_token(access_token)
316
+ auth_headers = self._auth_headers(user_id, user_secret)
300
317
 
301
318
  try:
302
319
  # Get all accounts
303
320
  accounts_url = f"{self.base_url}/accounts"
304
- params = {"userId": user_id, "userSecret": user_secret}
305
- response = await self.client.get(accounts_url, params=params)
321
+ response = await self.client.get(accounts_url, headers=auth_headers)
306
322
  response.raise_for_status()
307
323
  accounts = await response.json()
308
324
 
@@ -315,8 +331,7 @@ class SnapTradeInvestmentProvider(InvestmentProvider):
315
331
  positions_url = (
316
332
  f"{self.base_url}/accounts/{account_id}/positions"
317
333
  )
318
- pos_params = {"userId": user_id, "userSecret": user_secret}
319
- pos_response = await self.client.get(positions_url, params=pos_params)
334
+ pos_response = await self.client.get(positions_url, headers=auth_headers)
320
335
  pos_response.raise_for_status()
321
336
  positions = await pos_response.json()
322
337
 
@@ -328,8 +343,7 @@ class SnapTradeInvestmentProvider(InvestmentProvider):
328
343
 
329
344
  # Get account balances
330
345
  balances_url = f"{self.base_url}/accounts/{account_id}/balances"
331
- bal_params = {"userId": user_id, "userSecret": user_secret}
332
- bal_response = await self.client.get(balances_url, params=bal_params)
346
+ bal_response = await self.client.get(balances_url, headers=auth_headers)
333
347
  bal_response.raise_for_status()
334
348
  balances = await bal_response.json()
335
349
 
@@ -373,11 +387,11 @@ class SnapTradeInvestmentProvider(InvestmentProvider):
373
387
  ... print(f"Connected: {conn['brokerage_name']}")
374
388
  """
375
389
  user_id, user_secret = self._parse_access_token(access_token)
390
+ auth_headers = self._auth_headers(user_id, user_secret)
376
391
 
377
392
  try:
378
393
  url = f"{self.base_url}/connections"
379
- params = {"userId": user_id, "userSecret": user_secret}
380
- response = await self.client.get(url, params=params)
394
+ response = await self.client.get(url, headers=auth_headers)
381
395
  response.raise_for_status()
382
396
  return await response.json()
383
397
 
@@ -1,9 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from decimal import Decimal
3
4
  from enum import Enum
4
5
  from typing import Optional
5
6
 
6
- from pydantic import BaseModel
7
+ from pydantic import BaseModel, field_validator
7
8
 
8
9
 
9
10
  class AccountType(str, Enum):
@@ -16,11 +17,26 @@ class AccountType(str, Enum):
16
17
 
17
18
 
18
19
  class Account(BaseModel):
20
+ """Financial account model.
21
+
22
+ Uses Decimal for balance fields to prevent floating-point precision errors
23
+ in financial calculations (e.g., $0.01 + $0.02 != $0.03 with float).
24
+ """
19
25
  id: str
20
26
  name: str
21
27
  type: AccountType
22
28
  mask: Optional[str] = None
23
29
  currency: str = "USD"
24
30
  institution: Optional[str] = None
25
- balance_available: Optional[float] = None
26
- balance_current: Optional[float] = None
31
+ balance_available: Optional[Decimal] = None
32
+ balance_current: Optional[Decimal] = None
33
+
34
+ @field_validator("balance_available", "balance_current", mode="before")
35
+ @classmethod
36
+ def _coerce_balance_to_decimal(cls, v):
37
+ """Coerce float/int to Decimal for backwards compatibility."""
38
+ if v is None:
39
+ return v
40
+ if isinstance(v, (int, float)):
41
+ return Decimal(str(v))
42
+ return v
@@ -1,16 +1,30 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from datetime import date
4
+ from decimal import Decimal
4
5
  from typing import Optional
5
6
 
6
- from pydantic import BaseModel
7
+ from pydantic import BaseModel, field_validator
7
8
 
8
9
 
9
10
  class Transaction(BaseModel):
11
+ """Financial transaction model.
12
+
13
+ Uses Decimal for amount to prevent floating-point precision errors
14
+ in financial calculations (e.g., $0.01 + $0.02 != $0.03 with float).
15
+ """
10
16
  id: str
11
17
  account_id: str
12
18
  date: date
13
- amount: float
19
+ amount: Decimal
14
20
  currency: str = "USD"
15
21
  description: Optional[str] = None
16
22
  category: Optional[str] = None
23
+
24
+ @field_validator("amount", mode="before")
25
+ @classmethod
26
+ def _coerce_amount_to_decimal(cls, v):
27
+ """Coerce float/int to Decimal for backwards compatibility."""
28
+ if isinstance(v, (int, float)):
29
+ return Decimal(str(v))
30
+ return v
@@ -105,6 +105,9 @@ class AlpacaBrokerage(BrokerageProvider):
105
105
  ) -> dict:
106
106
  """Submit an order to Alpaca.
107
107
 
108
+ IMPORTANT: client_order_id is auto-generated if not provided to ensure
109
+ idempotency. Network retries without idempotency can cause DOUBLE ORDERS.
110
+
108
111
  Args:
109
112
  symbol: Trading symbol (e.g., "AAPL")
110
113
  qty: Order quantity
@@ -113,14 +116,20 @@ class AlpacaBrokerage(BrokerageProvider):
113
116
  time_in_force: "day", "gtc", "ioc", or "fok" (default: "day")
114
117
  limit_price: Limit price (required for limit/stop_limit orders)
115
118
  stop_price: Stop price (required for stop/stop_limit orders)
116
- client_order_id: Optional client order ID
119
+ client_order_id: Client order ID for idempotency. Auto-generated if not provided.
117
120
 
118
121
  Returns:
119
- Order dict with id, status, filled_qty, etc.
122
+ Order dict with id, status, filled_qty, client_order_id, etc.
120
123
 
121
124
  Raises:
122
125
  Exception: If order submission fails
123
126
  """
127
+ # CRITICAL: Auto-generate client_order_id for idempotency if not provided.
128
+ # Without this, network retries can cause duplicate order execution = MONEY LOSS.
129
+ if client_order_id is None:
130
+ import uuid
131
+ client_order_id = str(uuid.uuid4())
132
+
124
133
  order = self.client.submit_order(
125
134
  symbol=symbol,
126
135
  qty=qty,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: fin-infra
3
- Version: 0.1.55
3
+ Version: 0.1.57
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
@@ -176,7 +176,7 @@ print(f"Monthly payment: ${monthly:,.2f}")
176
176
 
177
177
  ## FastAPI Integration
178
178
 
179
- Use with [svc-infra](https://github.com/nfraxio/svc-infra) for a complete backend:
179
+ Use with [svc-infra](https://github.com/nfraxlab/svc-infra) for a complete backend:
180
180
 
181
181
  ```python
182
182
  from fastapi import FastAPI
@@ -306,7 +306,7 @@ This separation keeps financial logic clean and portable.
306
306
  ## Running Examples
307
307
 
308
308
  ```bash
309
- git clone https://github.com/nfraxio/fin-infra.git
309
+ git clone https://github.com/nfraxlab/fin-infra.git
310
310
  cd fin-infra
311
311
  poetry install
312
312
 
@@ -327,9 +327,9 @@ fin-infra is part of the **nfrax** infrastructure suite:
327
327
 
328
328
  | Package | Purpose |
329
329
  |---------|---------|
330
- | **[fin-infra](https://github.com/nfraxio/fin-infra)** | Financial infrastructure (banking, portfolio, insights) |
331
- | **[svc-infra](https://github.com/nfraxio/svc-infra)** | Backend infrastructure (auth, billing, jobs, webhooks) |
332
- | **[ai-infra](https://github.com/nfraxio/ai-infra)** | AI/LLM infrastructure (agents, tools, RAG, MCP) |
330
+ | **[fin-infra](https://github.com/nfraxlab/fin-infra)** | Financial infrastructure (banking, portfolio, insights) |
331
+ | **[svc-infra](https://github.com/nfraxlab/svc-infra)** | Backend infrastructure (auth, billing, jobs, webhooks) |
332
+ | **[ai-infra](https://github.com/nfraxlab/ai-infra)** | AI/LLM infrastructure (agents, tools, RAG, MCP) |
333
333
 
334
334
  ## License
335
335
 
@@ -339,9 +339,9 @@ MIT License - use it for anything.
339
339
 
340
340
  <div align="center">
341
341
 
342
- **Built by [nfraxio](https://github.com/nfraxio)**
342
+ **Built by [nfraxlab](https://github.com/nfraxlab)**
343
343
 
344
- [Star us on GitHub](https://github.com/nfraxio/fin-infra) | [View on PyPI](https://pypi.org/project/fin-infra/)
344
+ [Star us on GitHub](https://github.com/nfraxlab/fin-infra) | [View on PyPI](https://pypi.org/project/fin-infra/)
345
345
 
346
346
  </div>
347
347
 
@@ -12,7 +12,7 @@ fin_infra/analytics/savings.py,sha256=tavIRZtu9FjCm-DeWg5f060GcsdgD-cl-vgKOnieOU
12
12
  fin_infra/analytics/scenarios.py,sha256=LE_dZVkbxxAx5sxitGhiOhZfWTlYtVbIvS9pEXkijLc,12246
13
13
  fin_infra/analytics/spending.py,sha256=ypgL52JOsneTsFa2_aFB9fVuu9QWQsImQYChtECeA4Y,25833
14
14
  fin_infra/banking/__init__.py,sha256=wva1SEyrH2po79YycQ_00ZyC2tVeuO3uYcyvudOW484,22267
15
- fin_infra/banking/history.py,sha256=NvPwcP_phNbyA3bFejasocm1UjoX5pSbgdpEbbhni4g,9360
15
+ fin_infra/banking/history.py,sha256=1ufAwkTnXr-QJetFzJl4xA2e3dqd1-TkT8pf46MNfho,10630
16
16
  fin_infra/banking/utils.py,sha256=B2ebnTeUz-56l8XMBWnf2txFOr0bXIo3cKPio7_bhc4,15711
17
17
  fin_infra/brokerage/__init__.py,sha256=RB0wbVlxM9PCbWUezzjrOf19JucVDpCvNlT62LoMzho,17023
18
18
  fin_infra/budgets/__init__.py,sha256=3VTYU_OdqblYiP5fjHHiw3m-FSj5trPz7XVTb3f3rBc,4106
@@ -85,7 +85,7 @@ fin_infra/investments/models.py,sha256=NHnkvtMa1QYp_TpuuqT4u3cWEJi3OhW-1e-orMuR4
85
85
  fin_infra/investments/providers/__init__.py,sha256=V1eIzz6EnGJ-pq-9L3S2-evmcExF-YdZfd5P6JMyDtc,383
86
86
  fin_infra/investments/providers/base.py,sha256=cONlsu_z4eSpgzv5ky0-kjWarrC-KYd4tgaghSnarYg,9826
87
87
  fin_infra/investments/providers/plaid.py,sha256=OKzLNPAcBiZlqBRCOeaBogB5fvHdvlRpPuGHKvRGS2E,18069
88
- fin_infra/investments/providers/snaptrade.py,sha256=No7KgJ31r831Z1QoHLuKwRcfiGEiN9mkQsWvkMfqkZo,23047
88
+ fin_infra/investments/providers/snaptrade.py,sha256=IUgXoI7UCBXOAxn2J62cOiQSW02sujqEk47nb638264,23508
89
89
  fin_infra/investments/scaffold_templates/README.md,sha256=PhgxfMLrro2Jz83b7XEnBi7lexiWKqlMrd2UU2Rbs8A,12149
90
90
  fin_infra/investments/scaffold_templates/__init__.py,sha256=iR0oiAzXFYXHBnVJjaEnAzk6omncYOLg0TKMJ7xomBc,82
91
91
  fin_infra/investments/scaffold_templates/models.py.tmpl,sha256=5inP5-jw-qEfPYxSN71tn4AojZ9PesOIeuHTw181N-c,5849
@@ -93,14 +93,14 @@ fin_infra/investments/scaffold_templates/repository.py.tmpl,sha256=XwOEpQZfuXut1
93
93
  fin_infra/investments/scaffold_templates/schemas.py.tmpl,sha256=knWmn-Kyr7AdgPD4ZPMb6T49ZuPXeuOMqmjYNyA0CA0,5451
94
94
  fin_infra/markets/__init__.py,sha256=mStcYiA4dq2yHEyStZyOLd-KkW-Jf657l8NSLLa_MU8,9512
95
95
  fin_infra/models/__init__.py,sha256=q3SkGzDGFkoAMxwqJw8i4cHWt5NGU5ypjOgntxDGVKo,860
96
- fin_infra/models/accounts.py,sha256=M5lgX3r1_PbN-UVNY78P6UhPtxp8Hd3uO2rr9EO7v1Q,551
96
+ fin_infra/models/accounts.py,sha256=PeobjGg6WM70OvOTe0JIo0zo7tBM0PDAcyClQT-Jo4o,1141
97
97
  fin_infra/models/brokerage.py,sha256=z6Zyf0N5zmmXtrN2y_4fNmtIP5wNq40H8lrHLBwY7rc,8311
98
98
  fin_infra/models/candle.py,sha256=7vrDxR1JFZodMUG8OGB0ft1_oaGW16gZtawjZ_2OwhA,535
99
99
  fin_infra/models/credit.py,sha256=rSdSURsMe9_i2gxmwPTDwNQWOuM2zutL-OhvHsnbtmw,12144
100
100
  fin_infra/models/money.py,sha256=5BX8IQZkrNtjjnGIQAK2tyKnVim0R-yc1F_EBxUhcr0,400
101
101
  fin_infra/models/quotes.py,sha256=_2cDJS8_RLo4tLpJlqWd32J8uFNP0bbf1V_0u3NuLwo,543
102
102
  fin_infra/models/tax.py,sha256=lhNVIW650CdtpfgmSyMMJdojV7QnpHOUFQKiwMLTT4A,15656
103
- fin_infra/models/transactions.py,sha256=uBwxdlZh7tyIkqfQZggPZuMDpDRLhI8SeXwKZZgg4LU,318
103
+ fin_infra/models/transactions.py,sha256=zlu7Ath91ZmqQWKn8Bd_iV_XreWTDXFWJyfTTZ3J-ss,828
104
104
  fin_infra/net_worth/__init__.py,sha256=EjEuHNg8gEfFwbfko1-o5j-gSUZ2FcO9h7l05C-zAJM,3101
105
105
  fin_infra/net_worth/add.py,sha256=5xYy2L5hEEPiQNF79i-ArWVztLXk2XM97DoZYNWGAz8,23100
106
106
  fin_infra/net_worth/aggregator.py,sha256=grif-N8qk77L_JQ4IlcOJaKKP1qpxel0lIV_ll3HgjI,12646
@@ -128,7 +128,7 @@ fin_infra/providers/banking/base.py,sha256=KeNU4ur3zLKHVsBF1LQifcs2AKX06IEE-Rx_S
128
128
  fin_infra/providers/banking/plaid_client.py,sha256=q0JuU5ULYc2V-RoZGiJQSAprymcsSlJMGmildH0aHDg,6302
129
129
  fin_infra/providers/banking/teller_client.py,sha256=r4apwTNt8FJ2Rn2bC97orzaVYFkE0yMXXl--H5rtph0,9800
130
130
  fin_infra/providers/base.py,sha256=icMIQ6kmIvN15bkpDCR6C4DC2GaO-oMiFsaUsoWsRag,3303
131
- fin_infra/providers/brokerage/alpaca.py,sha256=f3mnsNxJ_daxuaHVmIhv8PZNnehf_LTulw1qoS4OEnc,9361
131
+ fin_infra/providers/brokerage/alpaca.py,sha256=eOzdRp45_VeD1r_2k0IudxFn0IRhGogn-htF5IJVKnk,9862
132
132
  fin_infra/providers/brokerage/base.py,sha256=JJFH0Cqca4Rg4rmxfiwcQt-peRoBf4JpG3g6jx8DVks,106
133
133
  fin_infra/providers/credit/experian.py,sha256=hNEVqmCaPT72NHV3Nw3sKOYPX0kIsl819ucqUc-7z2k,341
134
134
  fin_infra/providers/identity/stripe_identity.py,sha256=JQGJRuQdWP5dWDcROgtz1RrmpkytRv95H6Fn-x1kifU,501
@@ -173,8 +173,8 @@ fin_infra/utils/http.py,sha256=wgXo5amXyzAX49v_lRUvp4Xxq8nodX32CMJyWl6u89I,568
173
173
  fin_infra/utils/retry.py,sha256=VxT4ssP4r8Krl3KThvI-opPMhGCpZUCH4rUyit1LEUk,967
174
174
  fin_infra/utils.py,sha256=VxT4ssP4r8Krl3KThvI-opPMhGCpZUCH4rUyit1LEUk,967
175
175
  fin_infra/version.py,sha256=4t_crzhrLum--oyowUMxtjBTzUtWp7oRTF22ewEvJG4,49
176
- fin_infra-0.1.55.dist-info/LICENSE,sha256=wK-Ya7Ylxa38dSIZRhvNj1ZVLIrHC-BAI8v38PNADiA,1061
177
- fin_infra-0.1.55.dist-info/METADATA,sha256=DlpY7YlmpzWyadfFXqLxilIk2UNQTmTpKRCcZDAiOyI,10174
178
- fin_infra-0.1.55.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
179
- fin_infra-0.1.55.dist-info/entry_points.txt,sha256=Sr1uikvALZMeKm-DIkeKG4L9c4SNqysXGO_IRF8_9eU,53
180
- fin_infra-0.1.55.dist-info/RECORD,,
176
+ fin_infra-0.1.57.dist-info/LICENSE,sha256=wK-Ya7Ylxa38dSIZRhvNj1ZVLIrHC-BAI8v38PNADiA,1061
177
+ fin_infra-0.1.57.dist-info/METADATA,sha256=QJ0u6cERtWUuLhAOPspw1HfUdW_bhPw4_wbawiYIaRs,10182
178
+ fin_infra-0.1.57.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
179
+ fin_infra-0.1.57.dist-info/entry_points.txt,sha256=Sr1uikvALZMeKm-DIkeKG4L9c4SNqysXGO_IRF8_9eU,53
180
+ fin_infra-0.1.57.dist-info/RECORD,,