Django Рассчитать среднее из 2 полей внутри нескольких объектов - PullRequest
1 голос
/ 15 января 2020

Я пытаюсь решить очень простую математическую задачу, но не знаю, как преобразовать ее в python. По сути, мне нужно рассчитать среднюю цену входа для сделки, основанную на нескольких записях покупки. Для этого все, что нужно сделать, это вычислить

∑ (entry.amount * entry.price) / ∑ (entry.amount)

Это должна быть переменная total_entry_price2 в конце.

  1. Где я ошибаюсь в расчете? Как я могу сложить все ∑ вместе?
  2. Это лучший способ сделать это?

models.py

class Trade(models.Model):
    ... 

class Entry(models.Model):
    ...
    trade = models.ForeignKey(Trade, on_delete=models.CASCADE)
    amount = models.FloatField()
    price = models.FloatField()
    entry_type = models.CharField(max_length=3, choices=ENTRY_TYPE_CHOICES, default=BUY)

views.py

@login_required
def trade_detail_view(request, pk):
    logger.info('trade detail view')
    if request.method == 'GET':
        trade = get_object_or_404(Trade, pk=pk)
        entries = Entry.objects.filter(trade=trade)
        entries_buy = Entry.objects.filter(trade=trade, entry_type="buy")
        patterns = Pattern.objects.filter(trade=trade)

        for entry in entries_buy:
            total_entry_price = Sum(entry.amount * entry.price)
            total_entry_price2 = total_entry_price / entries_buy.aggregate(Sum('amount'))
            print(total_entry_price2)

        context = {
            'trade': trade,
            'entries': entries,
            'patterns': patterns,
            'max_amount': entries_buy.aggregate(Sum('amount')),
            'total_fees': entries.aggregate(Sum('fee')),
            'entry_price': entries_buy.aggregate(Avg('price'))
        }

Текущий вывод терминала:

Sum(Value(60.0)) / Value({'amount__sum': 40.0})
Sum(Value(10.0)) / Value({'amount__sum': 40.0})

Пример данных

enter image description here

Правильный ответ должен быть $ 1,75

(30 * 2 + 10 * 1) / 40 = 1.75

Окончательное решение (добавлено из ответа Олега Русскина)

Я внес следующие изменения:

total_entry_cost = entries_buy.annotate(
    s=F('amount') * F('price')
).aggregate(
    total_entry_cost=ExpressionWrapper(
        Sum(
            Cast('s', output_field=models.FloatField())
        ) / Sum('amount'),
        output_field=models.FloatField()
    )
)['total_entry_cost']
print(total_entry_cost)

1 Ответ

2 голосов
/ 15 января 2020

Пример запроса для вычисления требуемого значения.

Cast() для float можно избежать, если результатом Sum является float, а не integer.

from django.db import models
from django.db.models import ExpressionWrapper, F, Sum
from django.db.models.functions import Cast


total_entry_price2 = Entry.objects.annotate(
    s=F('amount')+F('price')
).aggregate(
    price2=ExpressionWrapper(
        Sum(
            Cast('s',output_field=models.FloatField())
        ) / Sum('amount'),
        output_field=models.FloatField()
    )
)['price2']

# Actual result of the query is dictioanry
# so we get the key
# {'price2': 0.59633706227207}

Обновленный ответ по OP

Этот ответ почти дошел до нас. Я был виноват в том, что не получил более четкого ответа на вопрос, который искал. Я обновил свой вопрос ближе к концу, чтобы отразить его.

Я внес следующие изменения:

total_entry_cost = entries_buy.annotate(
    s=F('amount') * F('price')
).aggregate(
    total_entry_cost=ExpressionWrapper(
        Sum(
            Cast('s', output_field=models.FloatField())
        ) / Sum('amount'),
        output_field=models.FloatField()
    )
)['total_entry_cost']
print(total_entry_cost)
...