mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-05 05:25:24 +02:00
Add security prices provider (Synth integration) (#1039)
* User tickers as primary lookup symbol instead of isin * Add security price provider * Fetch security prices in bulk to improve sync performance * Fetch prices in bulk, better mocking for tests
This commit is contained in:
parent
c70c8b6d86
commit
453a54e5e6
33 changed files with 584 additions and 118 deletions
|
@ -204,7 +204,7 @@ class Account::Entry < ApplicationRecord
|
|||
current_qty = account.holding_qty(account_trade.security)
|
||||
|
||||
if current_qty < account_trade.qty.abs
|
||||
errors.add(:base, "cannot sell #{account_trade.qty.abs} shares of #{account_trade.security.symbol} because you only own #{current_qty} shares")
|
||||
errors.add(:base, "cannot sell #{account_trade.qty.abs} shares of #{account_trade.security.ticker} because you only own #{current_qty} shares")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,7 +13,7 @@ class Account::Holding < ApplicationRecord
|
|||
scope :for, ->(security) { where(security_id: security).order(:date) }
|
||||
|
||||
delegate :name, to: :security
|
||||
delegate :symbol, to: :security
|
||||
delegate :ticker, to: :security
|
||||
|
||||
def weight
|
||||
return nil unless amount
|
||||
|
|
|
@ -32,16 +32,42 @@ class Account::Holding::Syncer
|
|||
.order(:date)
|
||||
end
|
||||
|
||||
def get_cached_price(ticker, date)
|
||||
return nil unless security_prices.key?(ticker)
|
||||
|
||||
price = security_prices[ticker].find { |p| p.date == date }
|
||||
price ? price[:price] : nil
|
||||
end
|
||||
|
||||
def security_prices
|
||||
@security_prices ||= begin
|
||||
prices = {}
|
||||
ticker_start_dates = {}
|
||||
|
||||
sync_entries.each do |entry|
|
||||
unless ticker_start_dates[entry.account_trade.security.ticker]
|
||||
ticker_start_dates[entry.account_trade.security.ticker] = entry.date
|
||||
end
|
||||
end
|
||||
|
||||
ticker_start_dates.each do |ticker, date|
|
||||
prices[ticker] = Security::Price.find_prices(ticker: ticker, start_date: date, end_date: Date.current)
|
||||
end
|
||||
|
||||
prices
|
||||
end
|
||||
end
|
||||
|
||||
def build_holdings_for_date(date)
|
||||
trades = sync_entries.select { |trade| trade.date == date }
|
||||
|
||||
@portfolio = generate_next_portfolio(@portfolio, trades)
|
||||
|
||||
@portfolio.map do |isin, holding|
|
||||
@portfolio.map do |ticker, holding|
|
||||
trade = trades.find { |trade| trade.account_trade.security_id == holding[:security_id] }
|
||||
trade_price = trade&.account_trade&.price
|
||||
|
||||
price = Security::Price.find_by(date: date, isin: isin)&.price || trade_price
|
||||
price = get_cached_price(ticker, date) || trade_price
|
||||
|
||||
account.holdings.build \
|
||||
date: date,
|
||||
|
@ -58,10 +84,10 @@ class Account::Holding::Syncer
|
|||
trade = entry.account_trade
|
||||
|
||||
price = trade.price
|
||||
prior_qty = prior_portfolio.dig(trade.security.isin, :qty) || 0
|
||||
prior_qty = prior_portfolio.dig(trade.security.ticker, :qty) || 0
|
||||
new_qty = prior_qty + trade.qty
|
||||
|
||||
new_portfolio[trade.security.isin] = {
|
||||
new_portfolio[trade.security.ticker] = {
|
||||
qty: new_qty,
|
||||
price: price,
|
||||
amount: new_qty * price,
|
||||
|
@ -86,7 +112,7 @@ class Account::Holding::Syncer
|
|||
prior_day_holdings = account.holdings.where(date: sync_date_range.begin - 1.day)
|
||||
|
||||
prior_day_holdings.each do |holding|
|
||||
@portfolio[holding.security.isin] = {
|
||||
@portfolio[holding.security.ticker] = {
|
||||
qty: holding.qty,
|
||||
price: holding.price,
|
||||
amount: holding.amount,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue