You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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”(剩余股票数量)。


解决方法

问题出在视图的查询逻辑上,需调整分组和过滤条件:

  1. 计算每只股票的净持仓时,要同时按portfolio和equity分组,避免跨组合统计;
  2. 过滤条件应针对计算出的净持仓net_shares而非原始交易的shares;
  3. 统计每个组合下净持仓为正的股票数量。

修改后的视图代码:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 16:40:15