56 lines
1.6 KiB
Ruby
56 lines
1.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class PortfolioPosition
|
|
attr_reader :stock, :shares, :portfolio, :change_amount, :total_return_amount
|
|
|
|
delegate :current_price, :price_cents, :ticker, to: :stock, prefix: :stock
|
|
|
|
def initialize(stock:, shares:, portfolio: nil, financial_data: {})
|
|
@stock = stock
|
|
@shares = shares
|
|
@portfolio = portfolio
|
|
@change_amount = financial_data[:change_amount]
|
|
@total_return_amount = financial_data[:total_return_amount]
|
|
end
|
|
|
|
def current_value
|
|
shares * stock_current_price
|
|
end
|
|
|
|
def current_value_cents
|
|
shares * stock_price_cents
|
|
end
|
|
|
|
def self.for_portfolio(portfolio)
|
|
Stock
|
|
.joins(:portfolio_stocks)
|
|
.where(portfolio_stocks: { portfolio_id: portfolio.id })
|
|
.group("stocks.id")
|
|
.having("SUM(portfolio_stocks.shares) > 0")
|
|
.select(
|
|
<<~SQL.squish
|
|
stocks.*,
|
|
SUM(portfolio_stocks.shares) AS total_shares,
|
|
(stocks.price_cents / 100.0) * SUM(portfolio_stocks.shares) -
|
|
SUM(portfolio_stocks.purchase_price * portfolio_stocks.shares) AS aggregated_change_amount,
|
|
(stocks.price_cents / 100.0) * SUM(portfolio_stocks.shares) AS aggregated_total_return
|
|
SQL
|
|
)
|
|
.map { |result| build_position(result, portfolio) }
|
|
end
|
|
|
|
def self.build_position(result, portfolio)
|
|
new(
|
|
stock: result,
|
|
shares: result.total_shares,
|
|
portfolio: portfolio,
|
|
financial_data: {
|
|
change_amount: result.aggregated_change_amount,
|
|
total_return_amount: result.aggregated_total_return
|
|
}
|
|
)
|
|
end
|
|
|
|
private_class_method :build_position
|
|
end
|