Calculate 12 months rolling / moving average, median, min, max, percentiles, etc. as single query in Postgres
Generic code for calculating 1,2,3,4,..6,...12 years / quarters / months / weeks / days /hours moving average, median, percentiles, etc. summary stats where table contains a list of individual time records (like sales transactions,etc)

WITH grid AS (
      SELECT end_time, start_time
      FROM (

            SELECT end_time
          , lag(end_time, 12, 'infinity') OVER (ORDER BY end_time) AS start_time
            FROM (

                SELECT
                generate_series(date_trunc('month', min(time2))
              , date_trunc('month', max(time2)) + interval '1 month', interval '1 month') AS end_time
                FROM   my_table

                ) sub

           ) sub2

      WHERE end_time &gt; start_time

)

SELECT
    to_char(date_trunc('month',a.end_time - interval '1 month'), 'YYYY-MM') as d
  , count(e.time2)
  , percentile_cont(0.25) within group (order by e.price) as Q1
  , percentile_cont(0.5) within group (order by e.price) as median
  , percentile_cont(0.75) within group (order by e.price) as Q3
  , avg(e.price) as Aver
  , min(e.price) as Mi
  , max(e.price) as Mx

FROM grid a

LEFT JOIN my_table e ON e.time2 &gt;= a.start_time

                   AND e.time2 &lt;  a.end_time

GROUP  BY end_time
ORDER  BY d DESC


The answer looks 'simple' but it is very disappointing nobody was prepared to lend a hand with some code snippets to put me on the right track... 

A note about time:

. The first section of the scrip generates a list of start-end time ranges to run queries for.

. The second part calculates the stats and outputs the results for each interval of time (eg. for "12 months median" the time shown in the first column will be "12 months to the end of the given month")

. The convention in PosgreSQL is that "end of the month" is actually "0 hour" of the next month (ie. end of Oct 2019 is "2019.11.01 at 00:00:00"). Same applies for any time range (eg. end of 2019 is actually "2020.01.01 at 00:00:00").

. So, for display purposes you may wish use the following: 

 to_char(date_trunc('month',a.end_time - interval '1 month'), 'YYYY-MM') as d


Else, if you don't include "- interval '1 month' ", the 12 months moving stats ending October 2019 will be shown as "for" 1st November 2019 at 00:00:00 (trunkaded to 2019-11).