I have the daily data for monthly VIX futures and am trying to convert it into a front-month time series. so I wrote the following code to extract the front month time serie
It takes a lot of pre-checking, but in essence, I had to build SQL variables based on one step at a time, as if it was in a program of "let X = something", "let y = X + somethingelse", etc. By building the inner most @SQLVars variables, once the first is declared, it can be used as the basis of the next variable and so on... First, here's the full query that you can apply to your data that builds based on whatever the current date is. You knowing your data better may have to tweak it some, but I think this gets you well on your way.
select
CONCAT( 'Q (', LEFT( MonthName( DateBasis.dMonth1 ), 3 ), ' ', RIGHT( Year( DateBasis.dMonth1 ), 2 ), ')' ) as FirstMonth,
CONCAT( 'U (', LEFT( MonthName( DateBasis.dMonth2 ), 3 ), ' ', RIGHT( Year( DateBasis.dMonth2 ), 2 ), ')' ) as SecondMonth,
CONCAT( 'V (', LEFT( MonthName( DateBasis.dMonth3 ), 3 ), ' ', RIGHT( Year( DateBasis.dMonth3 ), 2 ), ')' ) as ThirdMonth,
CONCAT( 'X (', LEFT( MonthName( DateBasis.dMonth4 ), 3 ), ' ', RIGHT( Year( DateBasis.dMonth4 ), 2 ), ')' ) as FourthMonth
from
( select @FirstOfMonth dFirstOfMonth,
@FDOM nWeekDay,
@SWOM nSecondWedOfMonth,
@SkipMonths nSkip,
@Month1 dMonth1,
@Month2 dMonth2,
@Month3 dMonth3,
@Month4 dMonth4
from
( select @FirstOfMonth := CONCAT( year(curdate()), '-', month( curdate()), '-01' ),
@FDOW := DayOfWeek( @FirstOfMonth ),
@SWOM := if( @FDOW <= 4, 12, 19) - @FDOW,
@SkipMonths := if( day( CurDate()) <= @SWOM, 1, 2 ),
@Month1 := date_add( @FirstOfMonth, interval 0 + @SkipMonths month ),
@Month2 := date_add( @Month1, interval 1 month ),
@Month3 := date_add( @Month2, interval 1 month ),
@Month4 := date_add( @Month3, interval 1 month )
) sqlvars
) DateBasis
The result of this one query above will return a SINGLE record (based on current date of Jan 31) to show FirstMonth SecondMonth ThirdMonth FourthMonth Q (Mar 12) U (Apr 12) V (May 12) X (Jun 12)
Now, nest this inside the rest of your query for your ticker IDs something like
SELECT hist.date,
hist.ticker_id,
hist.settle_price,
hist.volume
FROM
hist,
( entire select statement above ) FinalDates
WHERE
hist.ticker_id IN ( FinalDates.FirstMonth,
FinalDates.SecondMonth,
FinalDates.ThirdMonth,
FinalDates.FourthMonth )
and hist.trade_dt = curdate()
If you look at the innermost @SqlVariables as mentioned earlier is like a bunch of "let x=something". I always need a basis to begin, so I'm first getting the first day of a given month into a variable @FirstOfMonth by doing a concatenation of whatever the year is of the current date + "-" + month of the current date + "-01" to always start at the first of the month... ex: Today is Jan 31, 2012 will build out a string of '2012-01-01' which in year/month/date format is immediately recognized by MySQL as a date format we can perform date arithmetic on. So now, I have @FirstOfMonth = '2012-01-01'. Now, we need to determine the first Day of Week this date represents of the month we are in (hence @FDOW). This will return a value from 1-7 (Sunday = 1, Wed = 4, Sat = 7).
From this, we now need to compute when the 2nd Wednesday of the month will be. If the day of week is Sunday to (and including) Wednesday, the SECOND Wednesday is 12 days MINUS the day of week. Ex: Sunday the 1st would be Wed 4th, then Wed the 11th... so 12 - 1 (Sunday) = 11. If the first day of the month WAS a Wed, it would be a day of week = 4, but the 1st of the month = Wed, the second Wed = 8, so 12 - 4 = 8. Now, if the date was Thu, Fri or Sat as the first of the month, the Day of Week would be a 5, 6 or 7. The MINIMUM Date of the first Wednesday would be 7, the second Wed would be 14, so this is starting with 19 - whatever day of week... 5, 6, 7... Ex: 19 - 5(Thu Day of Week) = 14, 19 - 6(Fri Day of Week) = 13, 19 - 7(Sat Day of Week) = 12.. So, we know that the first Wed will be the full week out, so the earliest it would be is 7th and 14th as opposed to 1st and 8th (earliest of month).
Now that we know WHEN the 2nd Wednesday of the month is, compare that to the date we are running the query based on (ie: curdate() ). If the current date is ON or BEFORE (via <=) the SECOND WED of MONTH (@SWOM), then we only want to skip over 1 month... if we are further in the month, we need to skip 2 months.
Now, build out the dates. The date basis for Month 1 is the first of the current month PLUS an interval of however many months to skip. Month 2 is one month past the first, Month 3 one past Month 2, and Month 4 one past Month 3.
@FirstOfMonth := CONCAT( year(curdate()), '-', month( curdate()), '-01' ),
@FDOW := DayOfWeek( @FirstOfMonth ),
@SWOM := if( @FDOM <= 4, 12, 19) - @FDOM,
@SkipMonths := if( day( CurDate()) <= @SWOM, 1, 2 ),
@Month1 := date_add( @FirstOfMonth, interval 0 + @SkipMonths month ),
@Month2 := date_add( @Month1, interval 1 month ),
@Month3 := date_add( @Month2, interval 1 month ),
@Month4 := date_add( @Month3, interval 1 month )
So we finally have all 4 months basis to work with in a single row of ( select ... ) sqlvars result set showing something like
@Month1 @Month2 @Month3 @Month4
2012-03-01 2012-04-01 2012-05-01 2012-06-01 ... the four months out
Finally, once this data appears ok, we can now build out the specific strings you are looking for with the respective "Q", "U", "V" and "X" prefixes plus the left 3 of the month name with the 2 digit year.
So, with this getting all the date ranges and strings you are expecting, query this against your other table as I listed in the initial.
I hope this helps you and opens your eyes to a completely new context to trick SQL to... in essence doing an inline program to create many variables and operate from that... Pretty cool huh...
And in all truthfulness, this is the first time I've specifically tried this technique, although I've done many queries in the past using SQLVars.