Files
links/invest/models.py
T

92 lines
3.0 KiB
Python

from django.db import models
from django.core.validators import MinValueValidator
from decimal import Decimal
class Portfolio(models.Model):
"""Represents an investment account/portfolio (e.g., 'MOMO', 'User IBKR')."""
name = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Meta:
ordering = ['name']
class Stock(models.Model):
"""Current holdings for a portfolio. Quantity only — no cost tracking."""
portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, related_name='stocks')
stock_code = models.CharField(max_length=20, help_text="Stock ticker, e.g. 'NVDA', '9988.HK'")
quantity = models.DecimalField(
max_digits=20,
decimal_places=6,
default=Decimal('0'),
validators=[MinValueValidator(Decimal('0'))],
)
class Meta:
unique_together = [('portfolio', 'stock_code')]
ordering = ['stock_code']
def __str__(self):
return f"{self.stock_code} ({self.portfolio.name})"
class Transaction(models.Model):
"""Buy/sell event log. No price stored — only quantity changes tracked."""
ACTION_BUY = 'BUY'
ACTION_SELL = 'SELL'
ACTION_CHOICES = [(ACTION_BUY, 'Buy'), (ACTION_SELL, 'Sell')]
portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, related_name='transactions')
action = models.CharField(max_length=4, choices=ACTION_CHOICES)
stock_code = models.CharField(max_length=20)
quantity = models.DecimalField(
max_digits=20,
decimal_places=6,
validators=[MinValueValidator(Decimal('0.000001'))],
)
date = models.DateField()
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-date', '-created_at']
def __str__(self):
return f"{self.action} {self.quantity} {self.stock_code} on {self.date}"
class PortfolioSnapshot(models.Model):
"""Weekly total-value snapshot per portfolio, captured Saturday 8 AM."""
portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, related_name='snapshots')
captured_at = models.DateTimeField()
total_value = models.DecimalField(max_digits=20, decimal_places=2)
class Meta:
ordering = ['-captured_at']
indexes = [
models.Index(fields=['portfolio', 'captured_at']),
]
def __str__(self):
return f"{self.portfolio.name} @ {self.captured_at:%Y-%m-%d %H:%M}: ${self.total_value}"
class BenchmarkPrice(models.Model):
"""Daily closing price for a benchmark ticker (SPY, QQQ, etc.). Cached from yfinance."""
ticker = models.CharField(max_length=10)
date = models.DateField()
close = models.DecimalField(max_digits=12, decimal_places=4)
class Meta:
unique_together = [('ticker', 'date')]
indexes = [
models.Index(fields=['ticker', 'date']),
]
ordering = ['ticker', 'date']
def __str__(self):
return f"{self.ticker} {self.date}: ${self.close}"