Vorrei fare un esempio reale per rispondere a questa domanda
Avevo bisogno di calcolare una media mobile ponderata sui miei dati Ohlc, ho circa 134000 candele con un simbolo per ognuno per farlo
- Opzione 1 Fallo in Python / Node ecc ecc
- Opzione 2 Fallo in SQL stesso!
Qual è il migliore?
- Se dovessi farlo in Python, in sostanza, dovrei recuperare tutti i record memorizzati nel peggiore dei casi, eseguire il calcolo e salvare tutto ciò che secondo me è un enorme spreco di IO
- Variazioni medie mobili ponderate ogni volta che ricevi una nuova candela, il che significa che farei enormi quantità di IO a intervalli regolari, il che non è una buona opinione nel mio segno
- In SQL, tutto ciò che devo fare è probabilmente scrivere un trigger che calcola e memorizza tutto, quindi è sufficiente recuperare i valori WMA finali per ogni coppia di tanto in tanto e questo è molto più efficiente
Requisiti
- Se dovessi calcolare il WMA per ogni candela e memorizzarlo, lo farei su Python
- Ma poiché ho solo bisogno dell'ultimo valore, SQL è molto più veloce di Python
Per darti un po 'di incoraggiamento, questa è la versione di Python per fare una media mobile ponderata
WMA fatto tramite codice
import psycopg2
import psycopg2.extras
from talib import func
import timeit
import numpy as np
with psycopg2.connect('dbname=xyz user=xyz') as conn:
with conn.cursor() as cur:
t0 = timeit.default_timer()
cur.execute('select distinct symbol from ohlc_900 order by symbol')
for symbol in cur.fetchall():
cur.execute('select c from ohlc_900 where symbol = %s order by ts', symbol)
ohlc = np.array(cur.fetchall(), dtype = ([('c', 'f8')]))
wma = func.WMA(ohlc['c'], 10)
# print(*symbol, wma[-1])
print(timeit.default_timer() - t0)
conn.close()
WMA tramite SQL
"""
if the period is 10
then we need 9 previous candles or 15 x 9 = 135 mins on the interval department
we also need to start counting at row number - (count in that group - 10)
For example if AAPL had 134 coins and current row number was 125
weight at that row will be weight = 125 - (134 - 10) = 1
10 period WMA calculations
Row no Weight c
125 1
126 2
127 3
128 4
129 5
130 6
131 7
132 8
133 9
134 10
"""
query2 = """
WITH
condition(sym, maxts, cnt) as (
select symbol, max(ts), count(symbol) from ohlc_900 group by symbol
),
cte as (
select symbol, ts,
case when cnt >= 10 and ts >= maxts - interval '135 mins'
then (row_number() over (partition by symbol order by ts) - (cnt - 10)) * c
else null
end as weighted_close
from ohlc_900
INNER JOIN condition
ON symbol = sym
WINDOW
w as (partition by symbol order by ts rows between 9 preceding and current row)
)
select symbol, sum(weighted_close)/55 as wma
from cte
WHERE weighted_close is NOT NULL
GROUP by symbol ORDER BY symbol
"""
with psycopg2.connect('dbname=xyz user=xyz') as conn:
with conn.cursor() as cur:
t0 = timeit.default_timer()
cur.execute(query2)
# for i in cur.fetchall():
# print(*i)
print(timeit.default_timer() - t0)
conn.close()
Che ci crediate o no, la query viene eseguita più velocemente rispetto alla versione Pure Python di fare una MEDIA IN MOVIMENTO PESATA !!! Sono andato passo dopo passo nella stesura di quella query, quindi tieni duro e farai bene
Velocità
0,42141127300055814 secondi Python
0,23801879299935536 secondi SQL
Ho 134000 falsi record OHLC nel mio database divisi tra 1000 titoli, questo è un esempio di dove SQL può superare il tuo server di app