如何在Django中通过买卖交易计算投资组合剩余股票数量
问题:计算Django投资组合剩余股票数量
我正尝试在Django中计算用户投资组合经过多次股票交易后剩余的股票数量,以下是我的模型定义、实现尝试及遇到的问题:
模型代码
class Portfolio(models.Model): name = models.CharField(...) owner = models.ForeignKey(User, ...) equity = models.ManyToManyField(Equity, through="PortfolioEquity") class Equity(models.Model): symbol = models.CharField(...) name = models.CharField(...) class PortfolioEquity(models.Model): BUY = "BUY" SELL = "SELL" TRANSACTION_CHOICES = { BUY: "Buy", SELL: "Sell", } portfolio = models.ForeignKey(Portfolio, ...) equity = models.ForeignKey(Equity, ...) transaction = models.CharField(max_length=4, choices=TRANSACTION_CHOICES) amount = models.DecimalField(max_digits=8, decimal_places=2) shares = models.DecimalField(max_digits=8, decimal_places=3) date = models.DateField()
视图代码
def get(self, request): positive_equities = PortfolioEquity.objects.values('equity').annotate( tot_shares=Sum(Case( When(transaction="BUY", then=F('shares')), When(transaction="SELL", then=-F('shares')), default=Value(0), output_field=DecimalField() ))).filter(shares__gt=0) portfolios = Portfolio.objects.annotate(equity_count=Subquery( positive_equities.filter(portfolio=OuterRef('pk')) .values('portfolio') .annotate(count=Count('equity')) .values('count') )).filter(equity_count__gt=0) return self.render_to_response({'portfolios': portfolios})
HTML代码
{% for portfolio in portfolios %} <p> Owner: {{ portfolio.owner.get_full_name }} Equit{{ portfolio.equity_count|pluralize:'y,ies' }}: {{ portfolio.equity_count }} </p> {% endfor %}
交易记录
- 买入5股AAPL,2024年1月1日,金额300.00美元
- 卖出5股AAPL,2024年1月15日,金额350.00美元
- 买入1股AA,2024年1月20日,金额200.00美元
当前问题
当前输出为“Equities: 3”(统计了交易总数),但期望输出应为“Equity: 1”(剩余股票数量)。
解决方法
问题出在视图的查询逻辑上,需调整分组和过滤条件:
- 计算每只股票的净持仓时,要同时按
portfolio和equity分组,避免跨组合统计; - 过滤条件应针对计算出的净持仓
net_shares而非原始交易的shares; - 统计每个组合下净持仓为正的股票数量。
修改后的视图代码:
from django.db.models import Sum, Case, When, Value, DecimalField, Count, OuterRef, Subquery def get(self, request): # 计算每个portfolio下每个equity的净持仓,过滤净持仓>0的记录 equity_positions = PortfolioEquity.objects.values('portfolio', 'equity').annotate( net_shares=Sum(Case( When(transaction="BUY", then=F('shares')), When(transaction="SELL", then=-F('shares')), default=Value(0), output_field=DecimalField() )) ).filter(net_shares__gt=0) # 统计每个portfolio下净持仓>0的equity数量 portfolio_equity_counts = equity_positions.values('portfolio').annotate( count=Count('equity') ) # 将数量关联到Portfolio对象 portfolios = Portfolio.objects.annotate( equity_count=Subquery( portfolio_equity_counts.filter(portfolio=OuterRef('pk')).values('count')[:1], output_field=models.IntegerField() ) ).filter(equity_count__gt=0) return self.render_to_response({'portfolios': portfolios})
修改后,AAPL的净持仓为0(5-5)会被过滤,AA的净持仓为1,最终每个组合的equity_count会是1,符合期望输出。
内容的提问来源于stack exchange,提问作者toolman23
相关产品推荐
相关产品推荐

