Permettez-moi de prendre un exemple concret pour répondre à cette question
J'avais besoin de calculer une moyenne mobile pondérée sur mes données ohlc, j'ai environ 134000 bougies avec un symbole pour chacune pour le faire
- Option 1 Faites-le en Python / Node, etc.
- Option 2 Faites-le dans SQL lui-même!
Quel est le meilleur?
- Si je devais le faire en Python, essentiellement, je devrais récupérer tous les enregistrements stockés dans le pire des cas, effectuer le calcul et tout sauvegarder, ce qui, à mon avis, est un énorme gaspillage d'E / S.
- La moyenne mobile pondérée change à chaque fois que vous obtenez une nouvelle bougie, ce qui signifie que je ferais des quantités massives d'E / S à intervalles réguliers, ce qui n'est pas une bonne opinion à mon avis
- En SQL, tout ce que j'ai à faire est probablement d'écrire un déclencheur qui calcule et stocke tout, il suffit donc de récupérer les valeurs WMA finales pour chaque paire de temps en temps et c'est tellement plus efficace
Exigences
- Si je devais calculer WMA pour chaque bougie et la stocker, je le ferais sur Python
- Mais comme je n'ai besoin que de la dernière valeur, SQL est beaucoup plus rapide que Python
Pour vous donner quelques encouragements, voici la version Python pour faire une moyenne mobile pondérée
WMA fait par code
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 via 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()
Croyez-le ou non, la requête s'exécute plus rapidement que la version Pure Python de faire une MOYENNE DE MOUVEMENT PONDÉRÉE !!! Je suis allé étape par étape dans l'écriture de cette requête alors accrochez-vous et vous ferez très bien
La vitesse
0,42141127300055814 secondes Python
0,23801879299935536 secondes SQL
J'ai 134000 faux enregistrements OHLC dans ma base de données répartis sur 1000 actions, c'est donc un exemple où SQL peut surpasser votre serveur d'applications