fin-infra 0.2.0__tar.gz → 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. {fin_infra-0.2.0 → fin_infra-0.2.1}/PKG-INFO +3 -1
  2. {fin_infra-0.2.0 → fin_infra-0.2.1}/pyproject.toml +7 -1
  3. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/analytics/__init__.py +2 -2
  4. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/banking/history.py +4 -4
  5. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/brokerage/__init__.py +5 -5
  6. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/chat/planning.py +1 -1
  7. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/cli/cmds/scaffold_cmds.py +6 -6
  8. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/goals/management.py +3 -3
  9. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/goals/milestones.py +6 -6
  10. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/investments/scaffold_templates/README.md +17 -17
  11. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/net_worth/__init__.py +5 -5
  12. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/net_worth/insights.py +4 -4
  13. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/brokerage/alpaca.py +1 -1
  14. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/tax/add.py +2 -2
  15. {fin_infra-0.2.0 → fin_infra-0.2.1}/LICENSE +0 -0
  16. {fin_infra-0.2.0 → fin_infra-0.2.1}/README.md +0 -0
  17. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/__init__.py +0 -0
  18. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/__main__.py +0 -0
  19. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/analytics/add.py +0 -0
  20. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/analytics/cash_flow.py +0 -0
  21. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/analytics/ease.py +0 -0
  22. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/analytics/models.py +0 -0
  23. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/analytics/portfolio.py +0 -0
  24. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/analytics/projections.py +0 -0
  25. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/analytics/rebalancing.py +0 -0
  26. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/analytics/savings.py +0 -0
  27. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/analytics/scenarios.py +0 -0
  28. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/analytics/spending.py +0 -0
  29. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/banking/__init__.py +0 -0
  30. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/banking/utils.py +0 -0
  31. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/budgets/__init__.py +0 -0
  32. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/budgets/add.py +0 -0
  33. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/budgets/alerts.py +0 -0
  34. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/budgets/ease.py +0 -0
  35. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/budgets/models.py +0 -0
  36. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/budgets/scaffold_templates/README.md +0 -0
  37. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/budgets/scaffold_templates/__init__.py +0 -0
  38. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/budgets/scaffold_templates/models.py.tmpl +0 -0
  39. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/budgets/scaffold_templates/repository.py.tmpl +0 -0
  40. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/budgets/scaffold_templates/schemas.py.tmpl +0 -0
  41. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/budgets/templates.py +0 -0
  42. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/budgets/tracker.py +0 -0
  43. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/cashflows/__init__.py +0 -0
  44. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/cashflows/core.py +0 -0
  45. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/categorization/__init__.py +0 -0
  46. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/categorization/add.py +0 -0
  47. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/categorization/ease.py +0 -0
  48. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/categorization/engine.py +0 -0
  49. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/categorization/llm_layer.py +0 -0
  50. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/categorization/models.py +0 -0
  51. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/categorization/rules.py +0 -0
  52. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/categorization/taxonomy.py +0 -0
  53. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/chat/__init__.py +0 -0
  54. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/chat/ease.py +0 -0
  55. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/cli/__init__.py +0 -0
  56. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/cli/cmds/__init__.py +0 -0
  57. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/clients/__init__.py +0 -0
  58. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/clients/base.py +0 -0
  59. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/clients/plaid.py +0 -0
  60. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/compliance/__init__.py +0 -0
  61. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/credit/__init__.py +0 -0
  62. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/credit/add.py +0 -0
  63. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/credit/experian/__init__.py +0 -0
  64. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/credit/experian/auth.py +0 -0
  65. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/credit/experian/client.py +0 -0
  66. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/credit/experian/parser.py +0 -0
  67. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/credit/experian/provider.py +0 -0
  68. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/credit/mock.py +0 -0
  69. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/crypto/__init__.py +0 -0
  70. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/crypto/insights.py +0 -0
  71. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/documents/__init__.py +0 -0
  72. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/documents/add.py +0 -0
  73. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/documents/analysis.py +0 -0
  74. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/documents/ease.py +0 -0
  75. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/documents/models.py +0 -0
  76. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/documents/ocr.py +0 -0
  77. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/documents/storage.py +0 -0
  78. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/exceptions.py +0 -0
  79. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/goals/__init__.py +0 -0
  80. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/goals/add.py +0 -0
  81. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/goals/funding.py +0 -0
  82. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/goals/models.py +0 -0
  83. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/goals/scaffold_templates/README.md +0 -0
  84. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/goals/scaffold_templates/__init__.py +0 -0
  85. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/goals/scaffold_templates/models.py.tmpl +0 -0
  86. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/goals/scaffold_templates/repository.py.tmpl +0 -0
  87. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/goals/scaffold_templates/schemas.py.tmpl +0 -0
  88. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/insights/__init__.py +0 -0
  89. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/insights/aggregator.py +0 -0
  90. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/insights/models.py +0 -0
  91. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/investments/__init__.py +0 -0
  92. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/investments/add.py +0 -0
  93. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/investments/ease.py +0 -0
  94. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/investments/models.py +0 -0
  95. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/investments/providers/__init__.py +0 -0
  96. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/investments/providers/base.py +0 -0
  97. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/investments/providers/plaid.py +0 -0
  98. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/investments/providers/snaptrade.py +0 -0
  99. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/investments/scaffold_templates/__init__.py +0 -0
  100. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/investments/scaffold_templates/models.py.tmpl +0 -0
  101. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/investments/scaffold_templates/repository.py.tmpl +0 -0
  102. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/investments/scaffold_templates/schemas.py.tmpl +0 -0
  103. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/markets/__init__.py +0 -0
  104. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/models/__init__.py +0 -0
  105. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/models/accounts.py +0 -0
  106. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/models/brokerage.py +0 -0
  107. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/models/candle.py +0 -0
  108. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/models/credit.py +0 -0
  109. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/models/money.py +0 -0
  110. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/models/quotes.py +0 -0
  111. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/models/tax.py +0 -0
  112. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/models/transactions.py +0 -0
  113. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/net_worth/add.py +0 -0
  114. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/net_worth/aggregator.py +0 -0
  115. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/net_worth/calculator.py +0 -0
  116. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/net_worth/ease.py +0 -0
  117. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/net_worth/goals.py +0 -0
  118. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/net_worth/models.py +0 -0
  119. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/net_worth/scaffold_templates/README.md +0 -0
  120. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/net_worth/scaffold_templates/__init__.py +0 -0
  121. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/net_worth/scaffold_templates/models.py.tmpl +0 -0
  122. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/net_worth/scaffold_templates/repository.py.tmpl +0 -0
  123. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/net_worth/scaffold_templates/schemas.py.tmpl +0 -0
  124. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/normalization/__init__.py +0 -0
  125. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/normalization/currency_converter.py +0 -0
  126. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/normalization/models.py +0 -0
  127. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/normalization/providers/__init__.py +0 -0
  128. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/normalization/providers/exchangerate.py +0 -0
  129. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/normalization/providers/static_mappings.py +0 -0
  130. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/normalization/symbol_resolver.py +0 -0
  131. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/obs/__init__.py +0 -0
  132. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/obs/classifier.py +0 -0
  133. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/__init__.py +0 -0
  134. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/banking/base.py +0 -0
  135. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/banking/plaid_client.py +0 -0
  136. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/banking/teller_client.py +0 -0
  137. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/base.py +0 -0
  138. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/brokerage/base.py +0 -0
  139. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/credit/experian.py +0 -0
  140. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/identity/stripe_identity.py +0 -0
  141. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/market/alphavantage.py +0 -0
  142. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/market/base.py +0 -0
  143. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/market/ccxt_crypto.py +0 -0
  144. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/market/coingecko.py +0 -0
  145. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/market/yahoo.py +0 -0
  146. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/registry.py +0 -0
  147. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/tax/__init__.py +0 -0
  148. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/tax/irs.py +0 -0
  149. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/tax/mock.py +0 -0
  150. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/providers/tax/taxbit.py +0 -0
  151. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/py.typed +0 -0
  152. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/recurring/__init__.py +0 -0
  153. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/recurring/add.py +0 -0
  154. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/recurring/detector.py +0 -0
  155. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/recurring/detectors_llm.py +0 -0
  156. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/recurring/ease.py +0 -0
  157. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/recurring/insights.py +0 -0
  158. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/recurring/models.py +0 -0
  159. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/recurring/normalizer.py +0 -0
  160. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/recurring/normalizers.py +0 -0
  161. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/recurring/summary.py +0 -0
  162. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/scaffold/__init__.py +0 -0
  163. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/scaffold/budgets.py +0 -0
  164. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/scaffold/goals.py +0 -0
  165. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/security/__init__.py +0 -0
  166. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/security/add.py +0 -0
  167. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/security/audit.py +0 -0
  168. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/security/encryption.py +0 -0
  169. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/security/models.py +0 -0
  170. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/security/pii_filter.py +0 -0
  171. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/security/pii_patterns.py +0 -0
  172. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/security/token_store.py +0 -0
  173. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/settings.py +0 -0
  174. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/tax/__init__.py +0 -0
  175. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/tax/tlh.py +0 -0
  176. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/utils/__init__.py +0 -0
  177. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/utils/deprecation.py +0 -0
  178. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/utils/http.py +0 -0
  179. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/utils/retry.py +0 -0
  180. {fin_infra-0.2.0 → fin_infra-0.2.1}/src/fin_infra/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: fin-infra
3
- Version: 0.2.0
3
+ Version: 0.2.1
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
@@ -28,7 +28,9 @@ Provides-Extra: yahoo
28
28
  Requires-Dist: ai-infra (>=0.1.142)
29
29
  Requires-Dist: cashews[redis] (>=7.0)
30
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)
31
32
  Requires-Dist: httpx (>=0.25.0)
33
+ Requires-Dist: langchain-core (>=1.2.5,<2.0.0)
32
34
  Requires-Dist: loguru (>=0.7.0)
33
35
  Requires-Dist: numpy (>=1.24.0)
34
36
  Requires-Dist: numpy-financial (>=1.0.0)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "fin-infra"
3
- version = "0.2.0"
3
+ version = "0.2.1"
4
4
  description = "Financial infrastructure toolkit: banking connections, market data, credit, cashflows, and brokerage integrations"
5
5
  authors = ["Ali Khatami <aliikhatami94@gmail.com>"]
6
6
  license = "MIT"
@@ -57,6 +57,8 @@ ai-infra = ">=0.1.142"
57
57
  yahooquery = { version = ">=2.3.0", optional = true }
58
58
  ccxt = { version = ">=4.0.0", optional = true }
59
59
  plaid-python = { version = ">=25.0.0", optional = true }
60
+ fastapi-users = "^15.0.2"
61
+ langchain-core = "^1.2.5"
60
62
 
61
63
  [tool.poetry.extras]
62
64
  # Market data providers
@@ -87,6 +89,7 @@ python-semantic-release = ">=9.0.0"
87
89
  mkdocs = ">=1.6.0"
88
90
  mkdocs-material = ">=9.5.0"
89
91
  mkdocstrings = {extras = ["python"], version = ">=0.25.0"}
92
+ pip-audit = "^2.10.0"
90
93
 
91
94
  [tool.pytest.ini_options]
92
95
  testpaths = ["tests"]
@@ -145,6 +148,9 @@ ignore = [
145
148
  "tests/**/*.py" = [
146
149
  "B018", # useless expression in tests is fine
147
150
  ]
151
+ "examples/**/*.py" = [
152
+ "E402", # module level imports - examples show full context inline
153
+ ]
148
154
 
149
155
  [tool.ruff.lint.isort]
150
156
  known-first-party = ["fin_infra"]
@@ -8,8 +8,8 @@ This module provides comprehensive financial analytics capabilities including:
8
8
  - Growth projections (net worth forecasting with scenarios)
9
9
 
10
10
  Feature Status:
11
- STABLE: Core calculation functions (all analytics work with provided data)
12
- ⚠️ INTEGRATION: Auto-fetching from providers requires setup:
11
+ [OK] STABLE: Core calculation functions (all analytics work with provided data)
12
+ [!] INTEGRATION: Auto-fetching from providers requires setup:
13
13
  - Banking provider for transaction data
14
14
  - Brokerage provider for investment data
15
15
  - Categorization for expense categorization
@@ -4,7 +4,7 @@ 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.
7
+ [!] WARNING: This module uses IN-MEMORY storage by default. All data is LOST on restart.
8
8
  For production use, integrate with svc-infra SQL database or set FIN_INFRA_STORAGE_BACKEND.
9
9
 
10
10
  Features:
@@ -57,7 +57,7 @@ __all__ = [
57
57
  _logger = logging.getLogger(__name__)
58
58
 
59
59
  # In-memory storage for testing (will be replaced with SQL database in production)
60
- # ⚠️ WARNING: All data is LOST on restart when using in-memory storage!
60
+ # [!] WARNING: All data is LOST on restart when using in-memory storage!
61
61
  _balance_snapshots: list[BalanceSnapshot] = []
62
62
  _production_warning_logged = False
63
63
 
@@ -73,7 +73,7 @@ def _check_in_memory_warning() -> None:
73
73
 
74
74
  if env in ("production", "staging") and storage_backend == "memory":
75
75
  _logger.warning(
76
- "⚠️ CRITICAL: Balance history using IN-MEMORY storage in %s environment! "
76
+ "[!] CRITICAL: Balance history using IN-MEMORY storage in %s environment! "
77
77
  "All balance snapshots will be LOST on restart. "
78
78
  "Set FIN_INFRA_STORAGE_BACKEND=sql for production persistence.",
79
79
  env,
@@ -114,7 +114,7 @@ def record_balance_snapshot(
114
114
  This function stores a point-in-time balance record for trend analysis.
115
115
  In production, this would write to a SQL database via svc-infra.
116
116
 
117
- ⚠️ WARNING: Uses in-memory storage by default. Data is LOST on restart!
117
+ [!] WARNING: Uses in-memory storage by default. Data is LOST on restart!
118
118
 
119
119
  Args:
120
120
  account_id: Account identifier
@@ -1,6 +1,6 @@
1
1
  """Brokerage module - easy setup for trading operations.
2
2
 
3
- ⚠️ **TRADING WARNING**: This module provides real trading capabilities.
3
+ [!] **TRADING WARNING**: This module provides real trading capabilities.
4
4
  Always use paper trading mode for development and testing.
5
5
  Live trading requires explicit opt-in and involves real financial risk.
6
6
 
@@ -51,7 +51,7 @@ def easy_brokerage(
51
51
  ) -> BrokerageProvider:
52
52
  """Create a brokerage provider with paper/live trading support.
53
53
 
54
- ⚠️ **SAFETY**: Defaults to paper trading mode. Live trading requires explicit mode="live".
54
+ [!] **SAFETY**: Defaults to paper trading mode. Live trading requires explicit mode="live".
55
55
 
56
56
  Auto-detects provider based on environment variables:
57
57
  1. If ALPACA_API_KEY and ALPACA_API_SECRET are set → Alpaca
@@ -138,7 +138,7 @@ def add_brokerage(
138
138
  ) -> BrokerageProvider:
139
139
  """Wire brokerage provider to FastAPI app with routes and safety checks.
140
140
 
141
- ⚠️ **TRADING WARNING**: This mounts trading API endpoints.
141
+ [!] **TRADING WARNING**: This mounts trading API endpoints.
142
142
  Always use paper trading mode for development.
143
143
  Live trading requires explicit mode="live" and proper safeguards.
144
144
 
@@ -280,7 +280,7 @@ def add_brokerage(
280
280
  async def submit_order(order_request: OrderRequest):
281
281
  """Submit a new order.
282
282
 
283
- ⚠️ **TRADING WARNING**: This endpoint executes real trades in live mode.
283
+ [!] **TRADING WARNING**: This endpoint executes real trades in live mode.
284
284
  """
285
285
  try:
286
286
  order = brokerage_provider.submit_order(
@@ -447,7 +447,7 @@ def add_brokerage(
447
447
  add_prefixed_docs(
448
448
  app,
449
449
  prefix=prefix,
450
- title="Brokerage" + (" (Paper Trading)" if mode == "paper" else " ⚠️ LIVE"),
450
+ title="Brokerage" + (" (Paper Trading)" if mode == "paper" else " [!] LIVE"),
451
451
  auto_exclude_from_root=True,
452
452
  visible_envs=None, # Show in all environments
453
453
  )
@@ -173,7 +173,7 @@ Answer: "To assess your retirement progress, I need more information: (1) What's
173
173
  Follow-ups: ["I want to retire at 65 with $1.5M", "How much should I save monthly?", "What's a realistic retirement goal?"]
174
174
  Sources: []
175
175
 
176
- ⚠️ This is AI-generated advice. Not a substitute for a certified financial advisor.
176
+ [!] This is AI-generated advice. Not a substitute for a certified financial advisor.
177
177
  Verify calculations independently. For personalized advice, consult a professional."""
178
178
 
179
179
 
@@ -102,7 +102,7 @@ def cmd_scaffold(
102
102
  # Validate required parameters
103
103
  if dest_dir is None:
104
104
  typer.secho(
105
- " Error: --dest-dir is required",
105
+ "[X] Error: --dest-dir is required",
106
106
  fg=typer.colors.RED,
107
107
  err=True,
108
108
  )
@@ -137,7 +137,7 @@ def cmd_scaffold(
137
137
  )
138
138
  else:
139
139
  typer.secho(
140
- f" Unknown domain: {domain}. Must be one of: budgets, goals",
140
+ f"[X] Unknown domain: {domain}. Must be one of: budgets, goals",
141
141
  fg=typer.colors.RED,
142
142
  err=True,
143
143
  )
@@ -145,7 +145,7 @@ def cmd_scaffold(
145
145
 
146
146
  # Display results
147
147
  typer.echo("")
148
- typer.secho("📦 Scaffold Results:", bold=True)
148
+ typer.secho(" Scaffold Results:", bold=True)
149
149
  typer.echo("")
150
150
 
151
151
  files = result.get("files", [])
@@ -157,7 +157,7 @@ def cmd_scaffold(
157
157
  action = file_info.get("action", "unknown")
158
158
 
159
159
  if action == "wrote":
160
- typer.secho(f" Created: {path}", fg=typer.colors.GREEN)
160
+ typer.secho(f" [OK] Created: {path}", fg=typer.colors.GREEN)
161
161
  wrote_count += 1
162
162
  elif action == "skipped":
163
163
  reason = file_info.get("reason", "unknown")
@@ -168,7 +168,7 @@ def cmd_scaffold(
168
168
 
169
169
  # Summary
170
170
  typer.echo("")
171
- typer.secho(f" Done! Created {wrote_count} file(s), skipped {skipped_count}.", bold=True)
171
+ typer.secho(f" Done! Created {wrote_count} file(s), skipped {skipped_count}.", bold=True)
172
172
  typer.echo("")
173
173
 
174
174
  # Next steps
@@ -187,7 +187,7 @@ def cmd_scaffold(
187
187
  }
188
188
  route_prefix = prefix_map.get(domain, f"/{domain}")
189
189
 
190
- typer.secho("📝 Next Steps:", bold=True)
190
+ typer.secho(" Next Steps:", bold=True)
191
191
  typer.echo("")
192
192
  typer.echo(" 1. Review generated files and customize as needed")
193
193
  typer.echo(" 2. Run migrations:")
@@ -182,7 +182,7 @@ Your response: {
182
182
  "confidence": 0.94
183
183
  }
184
184
 
185
- ⚠️ This is AI-generated advice. Not a substitute for a certified financial advisor.
185
+ [!] This is AI-generated advice. Not a substitute for a certified financial advisor.
186
186
  Verify calculations independently. For personalized advice, consult a professional."""
187
187
 
188
188
  GOAL_PROGRESS_SYSTEM_PROMPT = """You are a financial advisor reviewing goal progress.
@@ -237,7 +237,7 @@ Your response: {
237
237
  "projected_completion_date": "2029-06-01",
238
238
  "variance_from_target_days": -365,
239
239
  "course_corrections": [
240
- "⚠️ 12 months behind! Current $1,000/month payment needs to increase to $1,500/month",
240
+ "[!] 12 months behind! Current $1,000/month payment needs to increase to $1,500/month",
241
241
  "Emergency: reduce expenses by $500/month (cancel subscriptions, cut entertainment)",
242
242
  "Contact debt counselor for consolidation or negotiation options",
243
243
  "Consider side income: gig work, selling unused items ($500/month target)",
@@ -246,7 +246,7 @@ Your response: {
246
246
  "confidence": 0.95
247
247
  }
248
248
 
249
- ⚠️ This is AI-generated advice. Not a substitute for a certified financial advisor.
249
+ [!] This is AI-generated advice. Not a substitute for a certified financial advisor.
250
250
  Verify calculations independently. For personalized advice, consult a professional."""
251
251
 
252
252
 
@@ -22,7 +22,7 @@ Example:
22
22
  # Check which milestones have been reached
23
23
  reached = check_milestones("goal_123")
24
24
  for m in reached:
25
- print(f"🎉 Milestone reached: {m['description']}")
25
+ print(f" Milestone reached: {m['description']}")
26
26
  """
27
27
 
28
28
  from datetime import datetime
@@ -124,7 +124,7 @@ def check_milestones(goal_id: str) -> list[dict[str, Any]]:
124
124
 
125
125
  reached = check_milestones("goal_123")
126
126
  if reached:
127
- print(f"🎉 {len(reached)} milestones reached!")
127
+ print(f" {len(reached)} milestones reached!")
128
128
  for m in reached:
129
129
  print(f" - {m['description']}: ${m['amount']:,.0f}")
130
130
 
@@ -184,17 +184,17 @@ def get_celebration_message(milestone: dict[str, Any]) -> str:
184
184
 
185
185
  Example:
186
186
  message = get_celebration_message(milestone)
187
- # "🎉 Milestone reached! You've hit $25,000 - 25% to target!"
187
+ # " Milestone reached! You've hit $25,000 - 25% to target!"
188
188
  """
189
189
  amount = milestone["amount"]
190
190
  description = milestone["description"]
191
191
 
192
192
  messages = [
193
- f"🎉 Milestone reached! You've hit ${amount:,.0f} - {description}!",
193
+ f" Milestone reached! You've hit ${amount:,.0f} - {description}!",
194
194
  f"🎊 Congratulations! ${amount:,.0f} milestone achieved - {description}",
195
195
  f"🌟 Great progress! You reached ${amount:,.0f} - {description}",
196
- f"💪 Keep going! ${amount:,.0f} milestone completed - {description}",
197
- f"🚀 Amazing! You hit ${amount:,.0f} - {description}",
196
+ f" Keep going! ${amount:,.0f} milestone completed - {description}",
197
+ f" Amazing! You hit ${amount:,.0f} - {description}",
198
198
  ]
199
199
 
200
200
  # Use amount to pick consistent message for same milestone
@@ -18,15 +18,15 @@ These templates generate production-ready persistence code for investment holdin
18
18
  ## Why Historical Snapshots?
19
19
 
20
20
  **Investment data providers (Plaid, SnapTrade, etc.) only provide current/live data:**
21
- - No historical portfolio values from past dates
22
- - No historical performance metrics
23
- - Cannot answer "What was my portfolio worth 3 months ago?"
21
+ - [X] No historical portfolio values from past dates
22
+ - [X] No historical performance metrics
23
+ - [X] Cannot answer "What was my portfolio worth 3 months ago?"
24
24
 
25
25
  **Solution: Store periodic snapshots in your database**
26
- - Track portfolio value changes over time
27
- - Calculate performance metrics (returns, growth)
28
- - Show trend charts and historical analysis
29
- - Works even if user disconnects provider
26
+ - [OK] Track portfolio value changes over time
27
+ - [OK] Calculate performance metrics (returns, growth)
28
+ - [OK] Show trend charts and historical analysis
29
+ - [OK] Works even if user disconnects provider
30
30
 
31
31
  ## Template Variables
32
32
 
@@ -148,12 +148,12 @@ async def capture_holdings_snapshot(
148
148
  holdings_data: list[dict], # From Plaid/SnapTrade API
149
149
  ) -> None:
150
150
  """Capture current holdings as a snapshot for historical tracking."""
151
-
151
+
152
152
  # Calculate aggregated metrics
153
153
  total_value = sum(Decimal(str(h.get("institution_value", 0))) for h in holdings_data)
154
154
  total_cost_basis = sum(Decimal(str(h.get("cost_basis", 0))) for h in holdings_data if h.get("cost_basis"))
155
155
  total_unrealized_gain_loss = sum(Decimal(str(h.get("unrealized_gain_loss", 0))) for h in holdings_data if h.get("unrealized_gain_loss"))
156
-
156
+
157
157
  # Create snapshot
158
158
  service = create_holding_snapshot_service(session)
159
159
  snapshot = await service.create(HoldingSnapshotCreate(
@@ -167,7 +167,7 @@ async def capture_holdings_snapshot(
167
167
  provider="plaid", # or "snaptrade"
168
168
  notes="Automatic daily snapshot"
169
169
  ))
170
-
170
+
171
171
  await session.commit()
172
172
  ```
173
173
 
@@ -181,18 +181,18 @@ async def daily_holdings_snapshot():
181
181
  """Capture holdings snapshots for all users with investment accounts."""
182
182
  from sqlalchemy import select
183
183
  from my_app.models.user import User
184
-
184
+
185
185
  async with AsyncSession(engine) as session:
186
186
  # Get all users with Plaid/SnapTrade connections
187
187
  stmt = select(User).where(User.banking_providers.isnot(None))
188
188
  result = await session.execute(stmt)
189
189
  users = result.scalars().all()
190
-
190
+
191
191
  for user in users:
192
192
  try:
193
193
  # Fetch current holdings from provider
194
194
  holdings = await fetch_holdings_from_provider(user)
195
-
195
+
196
196
  # Create snapshot
197
197
  await capture_holdings_snapshot(session, user.id, holdings)
198
198
  except Exception as e:
@@ -299,10 +299,10 @@ async def get_portfolio_performance_data(user_id: str):
299
299
  """Get data for portfolio performance dashboard."""
300
300
  async with AsyncSession(engine) as session:
301
301
  repo = create_holding_snapshot_service(session)
302
-
302
+
303
303
  # Get last 12 months trend
304
304
  snapshots = await repo.get_trend(user_id=user_id, months=12)
305
-
305
+
306
306
  # Calculate YTD performance
307
307
  today = date.today()
308
308
  year_start = date(today.year, 1, 1)
@@ -311,10 +311,10 @@ async def get_portfolio_performance_data(user_id: str):
311
311
  start_date=year_start,
312
312
  end_date=today
313
313
  )
314
-
314
+
315
315
  # Get latest snapshot
316
316
  latest = await repo.get_latest(user_id=user_id)
317
-
317
+
318
318
  return {
319
319
  "current_value": latest.total_value if latest else 0,
320
320
  "ytd_return": ytd_performance["percent_return"],
@@ -5,11 +5,11 @@ Calculates net worth by aggregating balances from multiple financial providers
5
5
  (banking, brokerage, crypto) with historical snapshots and change detection.
6
6
 
7
7
  **Feature Status**:
8
- STABLE: Core calculation (works with provided data)
9
- STABLE: Banking integration (Plaid, Teller)
10
- ⚠️ INTEGRATION: Brokerage integration (requires provider setup)
11
- ⚠️ INTEGRATION: Crypto integration (requires provider setup)
12
- ⚠️ INTEGRATION: Currency conversion (pass exchange_rate manually)
8
+ [OK] STABLE: Core calculation (works with provided data)
9
+ [OK] STABLE: Banking integration (Plaid, Teller)
10
+ [!] INTEGRATION: Brokerage integration (requires provider setup)
11
+ [!] INTEGRATION: Crypto integration (requires provider setup)
12
+ [!] INTEGRATION: Currency conversion (pass exchange_rate manually)
13
13
 
14
14
  **Key Features**:
15
15
  - Multi-provider aggregation (banking + brokerage + crypto)
@@ -215,7 +215,7 @@ Response: {
215
215
  "confidence": 0.89
216
216
  }
217
217
 
218
- ⚠️ This is AI-generated advice. Not a substitute for a certified financial advisor.
218
+ [!] This is AI-generated advice. Not a substitute for a certified financial advisor.
219
219
  Verify calculations independently. For personalized advice, consult a professional."""
220
220
 
221
221
  DEBT_REDUCTION_SYSTEM_PROMPT = """You are a debt counselor using the avalanche method (highest APR first).
@@ -269,7 +269,7 @@ Response: {
269
269
  "confidence": 0.98
270
270
  }
271
271
 
272
- ⚠️ This is AI-generated advice. Not a substitute for a certified financial advisor.
272
+ [!] This is AI-generated advice. Not a substitute for a certified financial advisor.
273
273
  Verify calculations independently. For personalized advice, consult a professional."""
274
274
 
275
275
  GOAL_RECOMMENDATION_SYSTEM_PROMPT = """You are a financial planner validating goals and suggesting paths.
@@ -322,7 +322,7 @@ Response: {
322
322
  "confidence": 0.89
323
323
  }
324
324
 
325
- ⚠️ This is AI-generated advice. Not a substitute for a certified financial advisor.
325
+ [!] This is AI-generated advice. Not a substitute for a certified financial advisor.
326
326
  Verify calculations independently. For personalized advice, consult a professional."""
327
327
 
328
328
  ASSET_ALLOCATION_SYSTEM_PROMPT = """You are a portfolio advisor recommending asset allocation.
@@ -365,7 +365,7 @@ Response: {
365
365
  "confidence": 0.91
366
366
  }
367
367
 
368
- ⚠️ This is AI-generated advice. Not a substitute for a certified financial advisor.
368
+ [!] This is AI-generated advice. Not a substitute for a certified financial advisor.
369
369
  Verify calculations independently. For personalized advice, consult a professional."""
370
370
 
371
371
 
@@ -1,6 +1,6 @@
1
1
  """Alpaca brokerage provider for paper and live trading.
2
2
 
3
- ⚠️ IMPORTANT: This module provides real trading capabilities. Always use paper trading
3
+ [!] IMPORTANT: This module provides real trading capabilities. Always use paper trading
4
4
  mode for development and testing. Live trading requires explicit opt-in.
5
5
  """
6
6
 
@@ -279,7 +279,7 @@ def add_tax_data(
279
279
  that can be sold to offset capital gains. Suggests replacement securities
280
280
  to maintain market exposure without triggering wash sale rules.
281
281
 
282
- ⚠️ **DISCLAIMER**: Not a substitute for professional tax or financial advice.
282
+ [!] **DISCLAIMER**: Not a substitute for professional tax or financial advice.
283
283
  Consult a certified tax professional before executing TLH trades.
284
284
 
285
285
  Args:
@@ -344,7 +344,7 @@ def add_tax_data(
344
344
  Projects the outcome of executing provided TLH opportunities, including
345
345
  total tax savings, portfolio impact, and risk assessment.
346
346
 
347
- ⚠️ **DISCLAIMER**: Not a substitute for professional tax or financial advice.
347
+ [!] **DISCLAIMER**: Not a substitute for professional tax or financial advice.
348
348
  Consult a certified tax professional before executing TLH trades.
349
349
 
350
350
  Args:
File without changes
File without changes