mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-05 13:35:21 +02:00
Basic Portfolio Views (#1000)
* Add holdings tab to account view * Basic portfolio UI * Cleanup * Handle missing holding data * Remove synced at (implemented in separate pr) * translations * Tweak post sync streams * Remove stale methods from merge conflict
This commit is contained in:
parent
ef4be7948a
commit
7c2091b343
37 changed files with 582 additions and 86 deletions
|
@ -1,6 +1,46 @@
|
|||
class Account::Holding < ApplicationRecord
|
||||
include Monetizable
|
||||
|
||||
monetize :amount
|
||||
|
||||
belongs_to :account
|
||||
belongs_to :security
|
||||
|
||||
validates :qty, :currency, presence: true
|
||||
|
||||
scope :chronological, -> { order(:date) }
|
||||
scope :current, -> { where(date: Date.current).order(amount: :desc) }
|
||||
scope :for, ->(security) { where(security_id: security).order(:date) }
|
||||
|
||||
delegate :name, to: :security
|
||||
delegate :symbol, to: :security
|
||||
|
||||
def weight
|
||||
return nil unless amount
|
||||
|
||||
portfolio_value = account.holdings.current.where.not(amount: nil).sum(&:amount)
|
||||
portfolio_value.zero? ? 1 : amount / portfolio_value * 100
|
||||
end
|
||||
|
||||
# Basic approximation of cost-basis
|
||||
def avg_cost
|
||||
avg_cost = account.holdings.for(security).where("date <= ?", date).average(:price)
|
||||
Money.new(avg_cost, currency)
|
||||
end
|
||||
|
||||
def trend
|
||||
@trend ||= calculate_trend
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def calculate_trend
|
||||
return nil unless amount_money
|
||||
|
||||
start_amount = qty * avg_cost
|
||||
|
||||
TimeSeries::Trend.new \
|
||||
current: amount_money,
|
||||
previous: start_amount
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,14 +38,18 @@ class Account::Holding::Syncer
|
|||
@portfolio = generate_next_portfolio(@portfolio, trades)
|
||||
|
||||
@portfolio.map do |isin, holding|
|
||||
price = Security::Price.find_by!(date: date, isin: isin).price
|
||||
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
|
||||
|
||||
account.holdings.build \
|
||||
date: date,
|
||||
security_id: holding[:security_id],
|
||||
qty: holding[:qty],
|
||||
price: price,
|
||||
amount: price * holding[:qty]
|
||||
amount: price ? (price * holding[:qty]) : nil,
|
||||
currency: holding[:currency]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -61,6 +65,7 @@ class Account::Holding::Syncer
|
|||
qty: new_qty,
|
||||
price: price,
|
||||
amount: new_qty * price,
|
||||
currency: entry.currency,
|
||||
security_id: trade.security_id
|
||||
}
|
||||
end
|
||||
|
@ -85,6 +90,7 @@ class Account::Holding::Syncer
|
|||
qty: holding.qty,
|
||||
price: holding.price,
|
||||
amount: holding.amount,
|
||||
currency: holding.currency,
|
||||
security_id: holding.security_id
|
||||
}
|
||||
end
|
||||
|
|
|
@ -78,6 +78,6 @@ class Account::Sync < ApplicationRecord
|
|||
partial: "shared/notification",
|
||||
locals: { type: type, message: message }
|
||||
)
|
||||
broadcast_refresh_to account
|
||||
account.family.broadcast_refresh
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue