Deixe-me dar um exemplo real para resolver esta questão
Eu precisava calcular uma média móvel ponderada nos meus dados ohlc, tenho cerca de 134000 velas com um símbolo para cada um fazer isso
- Opção 1 Faça em Python / Node etc etc
- Opção 2 Faça isso no próprio SQL!
Qual é o melhor?
- Se eu tivesse que fazer isso em Python, essencialmente, teria que buscar todos os registros armazenados, na pior das hipóteses, executar o cálculo e salvar tudo de volta, o que, na minha opinião, é um enorme desperdício de IO
- A média móvel ponderada muda sempre que você obtém uma nova vela, o que significa que eu estaria fazendo grandes quantidades de IO em intervalos regulares, o que não é uma boa opinião para mim.
- No SQL, tudo o que tenho a fazer é provavelmente escrever um gatilho que calcule e armazene tudo; portanto, é necessário buscar apenas os valores finais de WMA para cada par de vez em quando e isso é muito mais eficiente
Exigências
- Se eu tivesse que calcular o WMA para cada vela e armazená-lo, faria no Python
- Mas como eu só preciso do último valor, o SQL é muito mais rápido que o Python
Para dar algum incentivo, esta é a versão do Python para fazer uma média móvel ponderada
WMA feito através do 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 através do 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()
Acredite ou não, a consulta é executada mais rapidamente do que a versão Pure Python de fazer uma MÉDIA EM MOVIMENTO PESADO !!! Eu fui passo a passo para escrever essa consulta, então aguente firme e você vai se sair bem
Rapidez
0.42141127300055814 segundos Python
0.23801879299935536 segundos SQL
Eu tenho 134000 registros OHLC falsos no meu banco de dados, divididos entre 1000 ações. Esse é um exemplo de onde o SQL pode superar o servidor de aplicativos