1

I'm writing a Python/Django application to do some stock analysis.

I have two very simple models that look like this:

class Stock(models.Model):
    symbol = models.CharField(db_index=True, max_length=5, null=False, editable=False, unique=True)

class StockHistory(models.Model):
    stock = models.ForeignKey(Stock, related_name='StockHistory_stock', editable=False)
    trading_date = models.DateField(db_index=True, null=False, editable=False)
    close = models.DecimalField(max_digits=12, db_index=True, decimal_places=5, null=False, editable=False)

    class Meta:
        unique_together = ('stock', 'trading_date')

This is the dummy data I have populated them with:

import datetime
a = Stock.objects.create(symbol='A')
b = Stock.objects.create(symbol='B')
c = Stock.objects.create(symbol='C')
d = Stock.objects.create(symbol='D')

StockHistory.objects.create(trading_date=datetime.date(2018,1,1), close=200, stock=a)
StockHistory.objects.create(trading_date=datetime.date(2018,1,2), close=150, stock=a)
StockHistory.objects.create(trading_date=datetime.date(2018,1,3), close=120, stock=a)
StockHistory.objects.create(trading_date=datetime.date(2018,4,28), close=105, stock=a)
StockHistory.objects.create(trading_date=datetime.date(2018,5,3), close=105, stock=a)

StockHistory.objects.create(trading_date=datetime.date(2017,5,2), close=400, stock=b)
StockHistory.objects.create(trading_date=datetime.date(2017,11,11), close=200, stock=b)
StockHistory.objects.create(trading_date=datetime.date(2017,11,12), close=300, stock=b)
StockHistory.objects.create(trading_date=datetime.date(2017,11,13), close=400, stock=b)
StockHistory.objects.create(trading_date=datetime.date(2017,11,14), close=500, stock=b)

StockHistory.objects.create(trading_date=datetime.date(2018,4,28), close=105, stock=c)
StockHistory.objects.create(trading_date=datetime.date(2018,4,29), close=106, stock=c)
StockHistory.objects.create(trading_date=datetime.date(2018,4,30), close=107, stock=c)
StockHistory.objects.create(trading_date=datetime.date(2018,5,1), close=108, stock=c)
StockHistory.objects.create(trading_date=datetime.date(2018,5,2), close=109, stock=c)
StockHistory.objects.create(trading_date=datetime.date(2018,5,3), close=110, stock=c)
StockHistory.objects.create(trading_date=datetime.date(2018,5,4), close=90, stock=c)

I want to find all the stocks that made a yearly low within the past week.

But to make this question simpler, just assume that I want to find all the stocks whose lowest point since '2017-05-04' occurred on or after '2018-04-30'. Below is the SQL I wrote to find it. It works.

But I need help figuring out what Django Query to write to get the same results as this SQL. How can I do it?

mysql> select
    ->     s.symbol,
    ->     sh.trading_date,
    ->     low_table.low
    -> from
    ->     (
    ->         select
    ->             stock_id,
    ->             min(close) as low
    ->         from
    ->             stocks_stockhistory
    ->         where
    ->             trading_date >= '2017-05-04'
    ->         group by
    ->             stock_id
    ->     ) as low_table,
    ->     stocks_stockhistory as sh,
    ->     stocks_stock as s
    -> where
    ->     sh.stock_id = low_table.stock_id
    ->     and sh.stock_id = s.id
    ->     and sh.close = low_table.low
    ->     and sh.trading_date >= '2018-04-30'
    -> order by
    ->     s.symbol asc;
+--------+--------------+-----------+
| symbol | trading_date | low       |
+--------+--------------+-----------+
| A      | 2018-05-03   | 105.00000 |
| C      | 2018-05-04   |  90.00000 |
+--------+--------------+-----------+
2 rows in set (0.02 sec)
Saqib Ali
  • 11,931
  • 41
  • 133
  • 272

1 Answers1

1

Without trying to build it myself (these sorts of queries take a while to think about it) this part of the documentation should help you: Aggregation/Annotation.

These can be used to make complex queries based on the result of operations on the QuerySet (e.g. Min, Avg etc). You can annotate a QuerySet with a value (such as 'year low') and then filter based on that.

Try a few things, but one thing to remember is that your Django query may look nothing like your SQL query - getting these queries right can be difficult, but good luck!

EDIT: Query expressions might also be useful in a query like this.

benwad
  • 6,414
  • 10
  • 59
  • 93