I couldn't find a solution to this anywhere. I have a table with daily data having four columns with floating values. See picture here
I want to calculate a weekly price such that the Open price is the price from the start of the week ie, date_sub(running_date, Interval 5 workday) and the High and Low are the highs and lows over those last 5 workdays. The close being the last close (so nothing to calc).
This is the calculation of the weekly price data as shown here
I imagine it works something like this here for a single stock
SELECT
*,
( SELECT max( High ) FROM dat WHERE stock = "AAPL" AND `Date` >= date_sub( Curdate( ), INTERVAL 8 DAY ) ) AS High
FROM
dat
WHERE
stock = "AAPL"
ORDER BY
`Date` DESC
but where Curdate() is actually the Date of the table 'dat' and is changing in every row. This here only adds the weekly for the current date.
As a result I want the four columns wOpen, wHigh, wLow, wCLose as shown here
This looks like window functions over weekly partitions :
select d.*,
first_value(open) over(partition by stock, yearweek(tradeDate, 1) order by tradeDate) as wOpen,
max(high) over(partition by stock, yearweek(tradeDate, 1) order by tradeDate) as wHigh,
min(low) over(partition by stock, yearweek(tradeDate, 1) order by tradeDate) as wLow,
last_value(close) over(partition by stock, yearweek(tradeDate, 1) order by tradeDate) as wClose
from dat d
The trick is to partition your data by stock and by trade week; yearweek(tradeDate, 1)
gives you the year/week for the given date, based on weeks that start on Monday (as inferred from your sample data).
The rest is just windowing: the first opening and last closing values can be accessed with first_value()
and last_value
, and the window min
/max
give you the running minium and maximum.
If you want to restrict the results to a specific date range, you can just add a where
clause to the query.
If you are running MySQL 5.x, where window functions are not available, one alternative uses a couple of correlated subqueries. This will probably be far less efficient if you have a lot of rows to process:
select d.*,
(
select open
from dat d1
where d1.stock = d.stock and d1.tradeDate <= d.tradeDate and d1.tradeDate >= d.tradeDate - interval weekday(d.tradeDate) day
order by d1.tradeDate limit 1
) as wOpen,
(
select max(high)
from dat d1
where d1.stock = d.stock and d1.tradeDate <= d.tradeDate and d1.tradeDate >= d.tradeDate - interval weekday(d.tradeDate) day
) as wHigh,
(
select min(low)
from dat d1
where d1.stock = d.stock and d1.tradeDate <= d.tradeDate and d1.tradeDate >= d.tradeDate - interval weekday(d.tradeDate) day
) as wLow,
(
select close
from dat d1
where d1.stock = d.stock and d1.tradeDate <= d.tradeDate and d1.tradeDate >= d.tradeDate - interval weekday(d.tradeDate) day
order by d1.tradeDate desc limit 1
) as wClose
from dat d