1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-08-08 06:55:21 +02:00

Checkpoint

This commit is contained in:
Zach Gollwitzer 2025-05-21 07:20:49 -04:00
parent 3bd7baf2f5
commit 31eafbf578
7 changed files with 162 additions and 0 deletions

View file

@ -0,0 +1,16 @@
class PlaidAccount::CreditLiabilityProcessor
def initialize(plaid_account)
@plaid_account = plaid_account
end
def process
puts "processing credit liability!"
end
private
attr_reader :plaid_account
def account
plaid_account.account
end
end

View file

@ -1,6 +1,8 @@
# Plaid Investment balances have a ton of edge cases. This processor is responsible
# for deriving "brokerage cash" vs. "total value" based on Plaid's reported balances and holdings.
class PlaidAccount::InvestmentBalanceProcessor
include PlaidAccount::Securitizable
attr_reader :plaid_account
def initialize(plaid_account)

View file

@ -0,0 +1,18 @@
class PlaidAccount::InvestmentHoldingsProcessor
include PlaidAccount::Securitizable
def initialize(plaid_account)
@plaid_account = plaid_account
end
def process
puts "processing investment holdings!"
end
private
attr_reader :plaid_account
def account
plaid_account.account
end
end

View file

@ -0,0 +1,17 @@
class PlaidAccount::InvestmentTransactionsProcessor
include PlaidAccount::Securitizable
def initialize(plaid_account)
@plaid_account = plaid_account
end
def process
end
private
attr_reader :plaid_account
def account
plaid_account.account
end
end

View file

@ -0,0 +1,39 @@
module PlaidAccount::Securitizable
extend ActiveSupport::Concern
# TODO
def get_security(plaid_security_id)
plaid_security = get_plaid_security(plaid_security_id)
return [ nil, nil ] if plaid_security.nil? || plaid_security["ticker_symbol"].blank?
return [ nil, plaid_security ] if plaid_security["ticker_symbol"] == "CUR:USD" # internally, we do not consider cash a security and track it separately
operating_mic = plaid_security["market_identifier_code"]
# Find any matching security
security = Security.find_or_create_by!(
ticker: plaid_security["ticker_symbol"]&.upcase,
exchange_operating_mic: operating_mic&.upcase
)
[ security, plaid_security ]
end
private
def securities
@securities ||= plaid_account.raw_investments_payload["securities"] || []
end
# These are the tickers that Plaid considers a "security", but we do not (mostly cash-like tickers)
#
# For example, "CUR:USD" is what Plaid uses for the "Cash Holding" and represents brokerage cash sitting
# in the brokerage account. Internally, we treat brokerage cash as a separate concept. It is NOT a holding
# in the Maybe app (although in the UI, we show it next to other holdings).
def ignored_plaid_security_tickers
[ "CUR:USD" ]
end
end

View file

@ -0,0 +1,22 @@
# Resolves a Plaid security to an internal Security record, or nil
class PlaidAccount::SecurityProcessor
def initialize(plaid_security_id, plaid_securities)
@plaid_security_id = plaid_security_id
@plaid_securities = plaid_securities
end
def process
end
private
attr_reader :plaid_security_id, :plaid_securities
# Tries to find security, or returns the "proxy security" (common with options contracts that have underlying securities)
def plaid_security
security = securities.find { |s| s["security_id"] == plaid_security_id && s["ticker_symbol"].present? }
return security if security.present?
securities.find { |s| s["proxy_security_id"] == plaid_security_id }
end
end

View file

@ -0,0 +1,48 @@
class Security::Resolver
def initialize(symbol, exchange_operating_mic: nil, country_code: nil)
@symbol = symbol
@exchange_operating_mic = exchange_operating_mic
@country_code = country_code
end
def resolve
return nil unless symbol
exact_match = Security.find_by(
ticker: symbol,
exchange_operating_mic: exchange_operating_mic
)
exact_match if exact_match.present?
end
private
attr_reader :symbol, :exchange_operating_mic, :country_code
def fetch_from_provider
return nil unless Security.provider.present?
result = Security.search_provider(
symbol,
exchange_operating_mic: exchange_operating_mic
)
return nil unless result.success?
selection = if exchange_operating_mic.present?
result.data.find do |s|
s.ticker == symbol && s.exchange_operating_mic == exchange_operating_mic
end
else
result.data.sort_by
end
unless selection.present?
end
selection
end
def
end