Permítanme tomar un ejemplo real para abordar esta pregunta.
Necesitaba calcular un promedio móvil ponderado en mis datos de OHL, tengo alrededor de 134000 velas con un símbolo para cada una.
- Opción 1 Hazlo en Python / Node, etc., etc.
- Opción 2 ¡Hazlo en el propio SQL!
¿Cuál es mejor?
- Si tuviera que hacer esto en Python, esencialmente, tendría que buscar todos los registros almacenados en el peor de los casos, realizar el cálculo y guardar todo lo que, en mi opinión, es un gran desperdicio de E / S
- El promedio móvil ponderado cambia cada vez que obtienes una nueva vela, lo que significa que estaría haciendo grandes cantidades de IO a intervalos regulares, lo que no es una buena opinión en mi signo
- En SQL, todo lo que tengo que hacer es probablemente escribir un disparador que calcule y almacene todo, por lo que solo es necesario obtener los valores finales de WMA para cada par de vez en cuando y eso es mucho más eficiente
Requisitos
- Si tuviera que calcular WMA para cada vela y almacenarlo, lo haría en Python
- Pero como solo necesito el último valor, SQL es mucho más rápido que Python
Para alentarlo, esta es la versión de Python para hacer un promedio móvil ponderado
WMA hecho a través del código
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 a través de 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()
¡Lo creas o no, la consulta se ejecuta más rápido que la versión Pure Python de hacer un PROMEDIO DE MOVIMIENTO PONDERADO! Fui paso a paso a escribir esa consulta, así que aguanta y harás bien
Velocidad
0.42141127300055814 segundos Python
0.23801879299935536 segundos SQL
Tengo 134000 registros OHLC falsos en mi base de datos divididos entre 1000 acciones, por lo que es un ejemplo de dónde SQL puede superar a su servidor de aplicaciones