Files
investbrain/app/Models/Portfolio.php
T

158 lines
5.0 KiB
PHP
Raw Normal View History

2024-08-01 13:53:10 -05:00
<?php
namespace App\Models;
2024-09-11 22:00:37 -05:00
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
2024-08-01 13:53:10 -05:00
use Illuminate\Database\Eloquent\Model;
2024-09-11 22:00:37 -05:00
use App\Interfaces\MarketData\MarketDataInterface;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
2024-08-01 13:53:10 -05:00
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Portfolio extends Model
{
use HasFactory;
use HasUuids;
2024-08-01 13:53:10 -05:00
protected $fillable = [
'title',
'notes',
'wishlist',
];
protected static function boot()
{
parent::boot();
static::saved(function ($model) {
2024-08-17 21:33:09 -05:00
2024-08-01 13:53:10 -05:00
self::syncUsers($model);
});
}
protected $hidden = [];
2024-08-04 18:27:47 -05:00
protected $casts = [
'wishlist' => 'boolean'
];
2024-08-01 13:53:10 -05:00
2024-08-15 21:35:43 -05:00
protected $with = ['users', 'transactions'];
2024-08-01 13:53:10 -05:00
public function users()
{
return $this->belongsToMany(User::class)->withPivot('owner');
}
2024-08-15 21:35:43 -05:00
public function holdings()
{
2024-08-28 23:32:01 -05:00
return $this->hasMany(Holding::class, 'portfolio_id')
->withMarketData()
2024-08-29 18:46:21 -05:00
->withPerformance();
2024-08-15 21:35:43 -05:00
}
2024-08-01 13:53:10 -05:00
2024-08-15 21:35:43 -05:00
public function transactions()
{
2024-08-17 21:33:09 -05:00
return $this->hasMany(Transaction::class)->orderBy('created_at', 'DESC');
2024-08-15 21:35:43 -05:00
}
public function daily_change()
{
return $this->hasMany(DailyChange::class);
}
2024-08-01 13:53:10 -05:00
2024-08-28 22:06:47 -05:00
public function scopeMyPortfolios()
{
return $this->whereHas('users', function ($query) {
$query->where('user_id', auth()->user()->id);
});
}
2024-08-01 13:53:10 -05:00
public function scopeWithoutWishlists()
{
return $this->where(['wishlist' => false]);
}
public function getOwnerIdAttribute()
{
return $this->users()->firstWhere('owner', 1)?->id;
}
2024-09-11 22:00:37 -05:00
public static function syncUsers(self $model)
{
2024-08-01 13:53:10 -05:00
// make sure we don't remove owner access
$user_id[$model->owner_id ?? auth()->user()->id] = ['owner' => true];
// // add other users
// foreach(request()->users ?? [] as $id) {
// $user_id[$id] = ['owner' => false];
// };
// save
$model->users()->sync($user_id);
}
2024-09-11 22:00:37 -05:00
public function syncDailyChanges(): void
{
$holdings = $this->holdings()
->join('transactions', function($join) {
$join->on('transactions.symbol', '=', 'holdings.symbol')
->where('transactions.portfolio_id', '=', $this->id);
})
->select('holdings.*', DB::raw('min(transactions.date) as first_transaction_date')) // get first transaction date
->groupBy('holdings.symbol')
->get();
$total_performance = [];
$holdings->each(function($holding) use (&$total_performance) {
$all_history = app(MarketDataInterface::class)->history('ACME', $holding->first_transaction_date, now());
$quantity = $holding->dailyPerformance($holding->first_transaction_date, now());
$dividends = $holding->dividends->keyBy(function ($dividend, $key) {
return $dividend['date']->format('Y-m-d');
})->toArray();
$dividends_earned = 0;
$daily_performance = [];
$all_history->sortBy('date')->each(function ($history, $date) use ($quantity, $dividends, &$daily_performance, &$dividends_earned) {
$close = Arr::get($history, 'close', 0);
$total_market_value = $quantity->get($date)->owned * $close;
$dividend = Arr::get($dividends, $date, []);
$dividends_earned += $quantity->get($date)->owned * Arr::get($dividend, 'dividend_amount', 0);
$daily_performance[$date] = [
'date' => $date,
'portfolio_id' => $this->id,
'total_market_value' => $total_market_value,
'total_cost_basis' => $quantity->get($date)->cost_basis,
'total_gain' => $total_market_value - $quantity->get($date)->cost_basis,
'realized_gains' => $quantity->get($date)->realized_gains,
'total_dividends_earned' => $dividends_earned
];
});
foreach ($daily_performance as $date => $performance) {
if (!isset($total_performance[$date])) {
$total_performance[$date] = $performance;
} else {
$total_performance[$date]['total_market_value'] += $performance['total_market_value'];
$total_performance[$date]['total_cost_basis'] += $performance['total_cost_basis'];
$total_performance[$date]['total_gain'] += $performance['total_gain'];
$total_performance[$date]['realized_gains'] += $performance['realized_gains'];
$total_performance[$date]['total_dividends_earned'] += $performance['total_dividends_earned'];
}
}
});
$this->daily_change()->delete();
DailyChange::insert($total_performance);
}
2024-08-01 13:53:10 -05:00
}