wip
This commit is contained in:
@@ -22,9 +22,8 @@ class DailyChangesSheet implements FromCollection, WithHeadings, WithTitle
|
|||||||
'Portfolio ID',
|
'Portfolio ID',
|
||||||
'Total Market Value',
|
'Total Market Value',
|
||||||
'Total Cost Basis',
|
'Total Cost Basis',
|
||||||
'Total Gain',
|
|
||||||
'Total Dividends Earned',
|
|
||||||
'Realized Gains',
|
'Realized Gains',
|
||||||
|
'Total Dividends Earned',
|
||||||
'Annotation',
|
'Annotation',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-91
@@ -65,7 +65,7 @@ class DailyChange extends Model
|
|||||||
$dividendSub = DB::table('holdings')
|
$dividendSub = DB::table('holdings')
|
||||||
->join('dividends', 'dividends.symbol', '=', 'holdings.symbol')
|
->join('dividends', 'dividends.symbol', '=', 'holdings.symbol')
|
||||||
->leftJoin('currency_rates as cr', function ($join) use ($currency) {
|
->leftJoin('currency_rates as cr', function ($join) use ($currency) {
|
||||||
$join->on('cr.date', '=', 'dividends.date')
|
$join->on(DB::raw('DATE(cr.date)'), '=', DB::raw('DATE(dividends.date)'))
|
||||||
->where('cr.currency', '=', $currency);
|
->where('cr.currency', '=', $currency);
|
||||||
})
|
})
|
||||||
->join('transactions as tx', function ($join) {
|
->join('transactions as tx', function ($join) {
|
||||||
@@ -86,112 +86,78 @@ class DailyChange extends Model
|
|||||||
AS total_dividends_earned")
|
AS total_dividends_earned")
|
||||||
->groupBy(['holdings.portfolio_id', 'dividends.date', 'tx.transaction_type', 'tx.quantity']);
|
->groupBy(['holdings.portfolio_id', 'dividends.date', 'tx.transaction_type', 'tx.quantity']);
|
||||||
|
|
||||||
$totalCostBasisSub = DB::table('transactions as tx1')
|
$costBasisSub = DB::table('transactions')
|
||||||
->leftJoin('currency_rates as cr', function ($join) use ($currency) {
|
->leftJoin('currency_rates as cr', function ($join) use ($currency) {
|
||||||
$join->on('cr.date', '=', 'tx1.date')
|
$join->on(DB::raw('DATE(cr.date)'), '=', DB::raw('DATE(transactions.date)'))
|
||||||
->where('cr.currency', '=', $currency);
|
->where('cr.currency', $currency);
|
||||||
})
|
})
|
||||||
->select([
|
->select(['transactions.portfolio_id', 'transactions.date']);
|
||||||
'tx1.portfolio_id',
|
|
||||||
'tx1.date',
|
|
||||||
'tx1.symbol',
|
|
||||||
'tx1.transaction_type',
|
|
||||||
'tx1.cost_basis_base',
|
|
||||||
'tx1.quantity',
|
|
||||||
])
|
|
||||||
->selectRaw("(CASE
|
|
||||||
WHEN
|
|
||||||
tx1.transaction_type = 'BUY'
|
|
||||||
OR SUM(tx1.cost_basis_base) = 0
|
|
||||||
THEN
|
|
||||||
COALESCE(cr.rate, 1)
|
|
||||||
ELSE (
|
|
||||||
SELECT
|
|
||||||
SUM(COALESCE(cr2.rate, 1) * buy.cost_basis_base)
|
|
||||||
/ SUM(buy.cost_basis_base)
|
|
||||||
FROM transactions as buy
|
|
||||||
LEFT JOIN currency_rates as cr2
|
|
||||||
ON cr2.date = buy.date
|
|
||||||
AND cr2.currency = '{$currency}'
|
|
||||||
WHERE buy.symbol = tx1.symbol
|
|
||||||
AND buy.portfolio_id = tx1.portfolio_id
|
|
||||||
AND buy.transaction_type = 'BUY'
|
|
||||||
AND buy.date <= tx1.date
|
|
||||||
) END)
|
|
||||||
AS rate")
|
|
||||||
->selectRaw(
|
|
||||||
"COALESCE(SUM(CASE
|
|
||||||
WHEN tx1.transaction_type = 'BUY'
|
|
||||||
THEN tx1.cost_basis_base * tx1.quantity
|
|
||||||
END), 0)
|
|
||||||
AS total_cost_basis_for_purchases"
|
|
||||||
)
|
|
||||||
->selectRaw(
|
|
||||||
"COALESCE(SUM(CASE
|
|
||||||
WHEN tx1.transaction_type = 'SELL'
|
|
||||||
THEN tx1.cost_basis_base * tx1.quantity
|
|
||||||
END), 0)
|
|
||||||
AS total_cost_basis_for_sales"
|
|
||||||
)
|
|
||||||
->selectRaw(
|
|
||||||
"(CASE
|
|
||||||
WHEN tx1.transaction_type = 'SELL'
|
|
||||||
THEN tx1.sale_price_base - tx1.cost_basis_base
|
|
||||||
ELSE 0 END)
|
|
||||||
* tx1.quantity
|
|
||||||
* COALESCE(cr.rate, 1)
|
|
||||||
AS realized_gain_dollars")
|
|
||||||
->groupBy([
|
|
||||||
'tx1.portfolio_id',
|
|
||||||
'tx1.date',
|
|
||||||
'tx1.symbol',
|
|
||||||
'tx1.transaction_type',
|
|
||||||
'tx1.cost_basis_base',
|
|
||||||
'tx1.quantity',
|
|
||||||
'cr.rate',
|
|
||||||
'tx1.sale_price_base',
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $query
|
return $query
|
||||||
->select(['daily_change.date', 'daily_change.portfolio_id'])
|
->select(['daily_change.date', 'daily_change.portfolio_id'])
|
||||||
->leftJoinSub($totalCostBasisSub, 'cost_basis_display', function ($join) {
|
->selectRaw('daily_change.total_market_value * COALESCE(cr.rate, 1) AS total_market_value')
|
||||||
$join->on('daily_change.date', '>=', 'cost_basis_display.date')
|
|
||||||
->whereColumn('daily_change.portfolio_id', '=', 'cost_basis_display.portfolio_id');
|
|
||||||
})
|
|
||||||
->leftJoin('currency_rates as cr', function ($join) use ($currency) {
|
->leftJoin('currency_rates as cr', function ($join) use ($currency) {
|
||||||
$join->on('cr.date', '=', 'daily_change.date')
|
$join->on(DB::raw('DATE(cr.date)'), '=', DB::raw('DATE(daily_change.date)'))
|
||||||
->where('cr.currency', '=', $currency);
|
->where('cr.currency', '=', $currency);
|
||||||
})
|
})
|
||||||
->selectRaw('
|
->selectSub(function ($query) use ($costBasisSub) {
|
||||||
SUM(cost_basis_display.rate * cost_basis_display.total_cost_basis_for_purchases) - SUM(cost_basis_display.rate * cost_basis_display.total_cost_basis_for_sales)
|
$query->fromSub(
|
||||||
AS total_cost_basis
|
$costBasisSub->selectRaw("
|
||||||
')
|
(CASE
|
||||||
->selectRaw('(
|
WHEN transactions.transaction_type = 'BUY'
|
||||||
daily_change.total_market_value * COALESCE(cr.rate, 1)
|
THEN 1 ELSE -1 END
|
||||||
) - (SUM(cost_basis_display.rate * cost_basis_display.total_cost_basis_for_purchases) - SUM(cost_basis_display.rate * cost_basis_display.total_cost_basis_for_sales))
|
) * transactions.cost_basis_base * transactions.quantity * COALESCE(cr.rate, 1) AS total_cost_basis"),
|
||||||
as total_gain')
|
'cb')
|
||||||
->selectRaw('(
|
->selectRaw('SUM(cb.total_cost_basis)')
|
||||||
daily_change.total_market_value * COALESCE(cr.rate, 1)
|
->whereColumn('cb.date', '<=', 'daily_change.date')
|
||||||
) as total_market_value')
|
->whereColumn('cb.portfolio_id', '=', 'daily_change.portfolio_id');
|
||||||
->selectRaw('
|
}, 'total_cost_basis')
|
||||||
SUM(
|
->selectSub(function ($query) use ($costBasisSub) {
|
||||||
cost_basis_display.realized_gain_dollars
|
$query->fromSub(
|
||||||
) as realized_gain_dollars')
|
$costBasisSub->selectRaw("
|
||||||
->selectSub(function ($query) use ($dividendSub) {
|
(CASE
|
||||||
|
WHEN transactions.transaction_type = 'SELL'
|
||||||
|
THEN transactions.sale_price_base - transactions.cost_basis_base
|
||||||
|
END
|
||||||
|
) * transactions.quantity * COALESCE(cr.rate, 1) AS realized_gain_loss"),
|
||||||
|
'cb')
|
||||||
|
->selectRaw('SUM(cb.realized_gain_loss)')
|
||||||
|
->whereColumn('cb.date', '<=', 'daily_change.date')
|
||||||
|
->whereColumn('cb.portfolio_id', '=', 'daily_change.portfolio_id');
|
||||||
|
}, 'realized_gain_loss')
|
||||||
|
->selectSub(function ($query) use ($dividendSub) { // todo: maybe costbasis uses this model?
|
||||||
$query->fromSub($dividendSub, 'd')
|
$query->fromSub($dividendSub, 'd')
|
||||||
->selectRaw('SUM(d.total_dividends_earned)')
|
->selectRaw('SUM(d.total_dividends_earned)')
|
||||||
->whereColumn('d.date', '<=', 'daily_change.date')
|
->whereColumn('d.date', '<=', 'daily_change.date')
|
||||||
->whereColumn('d.portfolio_id', '=', 'daily_change.portfolio_id');
|
->whereColumn('d.portfolio_id', '=', 'daily_change.portfolio_id');
|
||||||
}, 'total_dividends_earned')
|
}, 'total_dividends_earned')
|
||||||
->groupBy([
|
->addSelect('annotation')
|
||||||
'daily_change.date',
|
|
||||||
'cr.rate',
|
|
||||||
'daily_change.total_market_value',
|
|
||||||
'daily_change.portfolio_id',
|
|
||||||
])
|
|
||||||
->orderBy('daily_change.date');
|
->orderBy('daily_change.date');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function scopeGetDailyPerformance($query)
|
||||||
|
{
|
||||||
|
return $query->get()
|
||||||
|
->sortBy('date')
|
||||||
|
->groupBy('date')
|
||||||
|
->map(function ($group) {
|
||||||
|
|
||||||
|
$total_market_value = $group->sum('total_market_value');
|
||||||
|
$total_cost_basis = $group->sum('total_cost_basis');
|
||||||
|
$total_market_gain = $total_market_value - $total_cost_basis;
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
'date' => $group->first()->date->toDateString(),
|
||||||
|
'total_market_value' => $total_market_value,
|
||||||
|
'total_cost_basis' => $total_cost_basis,
|
||||||
|
'total_gain' => $total_market_gain,
|
||||||
|
'realized_gain_dollars' => $group->sum('realized_gain_dollars'),
|
||||||
|
'total_dividends_earned' => $group->sum('total_dividends_earned'),
|
||||||
|
];
|
||||||
|
})
|
||||||
|
->values();
|
||||||
|
}
|
||||||
|
|
||||||
public function portfolio()
|
public function portfolio()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Portfolio::class);
|
return $this->belongsTo(Portfolio::class);
|
||||||
|
|||||||
@@ -53,22 +53,7 @@ new class extends Component
|
|||||||
$dailyChangeQuery->whereDate('daily_change.date', '>=', now()->{$filterMethod['method']}(...$filterMethod['args']));
|
$dailyChangeQuery->whereDate('daily_change.date', '>=', now()->{$filterMethod['method']}(...$filterMethod['args']));
|
||||||
}
|
}
|
||||||
|
|
||||||
$dailyChange = $dailyChangeQuery->get();
|
$dailyChange = $dailyChangeQuery->getDailyPerformance();
|
||||||
|
|
||||||
$dailyChange = $dailyChange
|
|
||||||
->sortBy('date')
|
|
||||||
->groupBy('date')
|
|
||||||
->map(function ($group) {
|
|
||||||
return (object) [
|
|
||||||
'date' => $group->first()->date->toDateString(),
|
|
||||||
'total_market_value' => $group->sum('total_market_value'),
|
|
||||||
'total_cost_basis' => $group->sum('total_cost_basis'),
|
|
||||||
'total_gain' => $group->sum('total_gain'),
|
|
||||||
'realized_gain_dollars' => $group->sum('realized_gain_dollars'),
|
|
||||||
'total_dividends_earned' => $group->sum('total_dividends_earned'),
|
|
||||||
];
|
|
||||||
})
|
|
||||||
->values();
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'series' => [
|
'series' => [
|
||||||
|
|||||||
@@ -557,20 +557,9 @@ class MultiCurrencyTest extends TestCase
|
|||||||
|
|
||||||
$dailyChange = DailyChange::withDailyPerformance()
|
$dailyChange = DailyChange::withDailyPerformance()
|
||||||
->portfolio($portfolio->id)
|
->portfolio($portfolio->id)
|
||||||
->get();
|
->getDailyPerformance();
|
||||||
|
|
||||||
$dailyChange = $dailyChange->sortBy('date')
|
dump($dailyChange->toArray());
|
||||||
->groupBy('date')
|
|
||||||
->map(function ($group) {
|
|
||||||
return (object) [
|
|
||||||
'date' => $group->first()->date->toDateString(),
|
|
||||||
'total_market_value' => $group->sum('total_market_value'),
|
|
||||||
'total_cost_basis' => $group->sum('total_cost_basis'),
|
|
||||||
'total_gain' => $group->sum('total_gain'),
|
|
||||||
'realized_gain_dollars' => $group->sum('realized_gain_dollars'),
|
|
||||||
'total_dividends_earned' => $group->sum('total_dividends_earned'),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
$metrics = Holding::query()
|
$metrics = Holding::query()
|
||||||
->portfolio($portfolio->id)
|
->portfolio($portfolio->id)
|
||||||
@@ -618,32 +607,16 @@ class MultiCurrencyTest extends TestCase
|
|||||||
|
|
||||||
$dailyChange = DailyChange::withDailyPerformance()
|
$dailyChange = DailyChange::withDailyPerformance()
|
||||||
->portfolio($portfolio->id)
|
->portfolio($portfolio->id)
|
||||||
->get();
|
->getDailyPerformance();
|
||||||
|
|
||||||
dump($dailyChange->toArray());
|
|
||||||
|
|
||||||
$dailyChange = $dailyChange->sortBy('date')
|
|
||||||
->groupBy('date')
|
|
||||||
->map(function ($group) {
|
|
||||||
|
|
||||||
return (object) [
|
|
||||||
'date' => $group->first()->date->toDateString(),
|
|
||||||
'total_market_value' => $group->sum('total_market_value'),
|
|
||||||
'total_cost_basis' => $group->sum('total_cost_basis'),
|
|
||||||
'total_gain' => $group->sum('total_gain'),
|
|
||||||
'realized_gain_dollars' => $group->sum('realized_gain_dollars'),
|
|
||||||
'total_dividends_earned' => $group->sum('total_dividends_earned'),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
$metrics = Holding::query()
|
$metrics = Holding::query()
|
||||||
->portfolio($portfolio->id)
|
->portfolio($portfolio->id)
|
||||||
->getPortfolioMetrics();
|
->getPortfolioMetrics();
|
||||||
|
|
||||||
$this->assertEqualsWithDelta($metrics->get('total_market_value'), $dailyChange->last()->total_market_value, 0.01); // TODO:
|
$this->assertEqualsWithDelta($metrics->get('total_market_value'), $dailyChange->last()->total_market_value, 0.01);
|
||||||
$this->assertEqualsWithDelta($metrics->get('total_cost_basis'), $dailyChange->last()->total_cost_basis, 0.01);
|
$this->assertEqualsWithDelta($metrics->get('total_cost_basis'), $dailyChange->last()->total_cost_basis, 0.01);
|
||||||
$this->assertEqualsWithDelta($metrics->get('realized_gain_dollars'), $dailyChange->last()->realized_gain_dollars, 0.01);
|
$this->assertEqualsWithDelta($metrics->get('realized_gain_dollars'), $dailyChange->last()->realized_gain_dollars, 0.01);
|
||||||
$this->assertEqualsWithDelta($metrics->get('total_market_value') - $metrics->get('total_cost_basis'), $dailyChange->last()->total_gain, 0.01); // TODO:
|
$this->assertEqualsWithDelta($metrics->get('total_market_value') - $metrics->get('total_cost_basis'), $dailyChange->last()->total_gain, 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_multi_currency_import_calculates_correct_holding_data(): void
|
public function test_multi_currency_import_calculates_correct_holding_data(): void
|
||||||
|
|||||||
Reference in New Issue
Block a user