mirror of
https://github.com/wahyd4/links.git
synced 2026-08-09 05:06:16 +10:00
P1: - WAC cost basis per (portfolio, ticker): avg_cost / unrealized P&L / P&L% - transaction price when recorded, else trade-date market close (estimated, flagged) - dashboard: Unrealized P&L card + Avg Cost & P&L columns on holdings tables - fx: frankfurter.dev rates (USD/HKD->AUD, 24h cache, UA header required) -> AUD total on Total Value card - chart: 1M/3M/6M/YTD/1Y/ALL period buttons (all periods precomputed server-side, top-level payload stays YTD for compat) P2: - /invest/stocks/<ticker>/ per-ticker page: cross-account holdings, WAC, P&L, weight, theme, trade history - manual transaction + cashflow entry forms (source=MANUAL, confidence=1.0) - dashboard transaction search (?q= ticker/account) + add-transaction buttons - tickers link to detail page; tabular-nums on all figures - 8 new tests (WAC sell, estimated price, incomplete, per-portfolio agg, stock page, forms, search)
69 lines
2.6 KiB
Python
69 lines
2.6 KiB
Python
"""Forms for manual invest data entry (transactions & cash flows)."""
|
|
from django import forms
|
|
|
|
from .models import CashFlow, Portfolio, Transaction
|
|
|
|
|
|
class TransactionForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Transaction
|
|
fields = [
|
|
'portfolio', 'action', 'stock_code', 'quantity',
|
|
'price_per_share', 'currency', 'fee', 'date',
|
|
]
|
|
widgets = {
|
|
'date': forms.DateInput(attrs={'type': 'date'}),
|
|
'stock_code': forms.TextInput(attrs={'placeholder': '如 NVDA / 9988.HK', 'autocomplete': 'off'}),
|
|
'quantity': forms.NumberInput(attrs={'step': '0.000001', 'min': '0.000001'}),
|
|
'price_per_share': forms.NumberInput(attrs={'step': '0.000001', 'min': '0', 'placeholder': '可空(用当日收盘价估算)'}),
|
|
'fee': forms.NumberInput(attrs={'step': '0.01', 'min': '0', 'placeholder': '可空'}),
|
|
}
|
|
labels = {
|
|
'portfolio': '账户',
|
|
'action': '类型',
|
|
'stock_code': '股票代码',
|
|
'quantity': '数量',
|
|
'price_per_share': '成交价(可空)',
|
|
'currency': '币种',
|
|
'fee': '手续费(可空)',
|
|
'date': '日期',
|
|
}
|
|
help_texts = {
|
|
'price_per_share': '留空则按交易日收盘价估算成本',
|
|
'fee': '以成交币种计',
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.fields['portfolio'].queryset = Portfolio.objects.all()
|
|
self.fields['portfolio'].empty_label = None
|
|
self.fields['price_per_share'].required = False
|
|
self.fields['fee'].required = False
|
|
self.fields['currency'].initial = 'USD'
|
|
|
|
|
|
class CashFlowForm(forms.ModelForm):
|
|
class Meta:
|
|
model = CashFlow
|
|
fields = ['portfolio', 'flow_type', 'amount', 'currency', 'date', 'note']
|
|
widgets = {
|
|
'date': forms.DateInput(attrs={'type': 'date'}),
|
|
'amount': forms.NumberInput(attrs={'step': '0.01', 'min': '0.01'}),
|
|
'note': forms.TextInput(attrs={'placeholder': '备注(可空)'}),
|
|
}
|
|
labels = {
|
|
'portfolio': '账户',
|
|
'flow_type': '类型',
|
|
'amount': '金额',
|
|
'currency': '币种',
|
|
'date': '日期',
|
|
'note': '备注',
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.fields['portfolio'].queryset = Portfolio.objects.all()
|
|
self.fields['portfolio'].empty_label = None
|
|
self.fields['currency'].initial = 'USD'
|
|
self.fields['note'].required = False
|