Negociar quando o mercado negocia
O preço médio ponderado por volume é o preço médio de tudo que foi negociado, cada negócio ponderado pelo seu tamanho: sum(price * volume) / sum(volume). É a referência mais comum no trading institucional porque é o que um participante médio pagou naquele dia. Um algoritmo VWAP tenta igualá-lo ou superá-lo negociando *em proporção ao volume do mercado*: muito quando o mercado está ativo (a abertura, o fechamento), pouco quando está calmo (hora do almoço). Onde o TWAP segue o relógio, o VWAP segue a multidão, de modo que cada uma de suas ordens é uma fração parecida do que todos os outros fazem e move menos o preço.
# The benchmark: each price counts as much as the volume that traded at it
def vwap(prices, volumes):
return sum(p * v for p, v in zip(prices, volumes)) / sum(volumes)
prices = [100.0, 101.0, 102.0]
volumes = [100, 300, 600]
print(round(vwap(prices, volumes), 4)) # 101.5 most volume traded near 102, so the VWAP leans that way
print(sum(prices) / len(prices)) # 101.0 the plain average ignores how much traded at each priceUm perfil de volume e um cronograma que o segue
Não se pode saber o volume de hoje com antecedência, então um algoritmo VWAP usa um perfil de volume estimado do histórico: a fatia média do volume do dia negociada em cada intervalo nas últimas 20 sessões, digamos. O volume intradiário costuma ter forma de U, alto na abertura e no fechamento. O cronograma dá então a cada intervalo total * fatia ações. Raramente são inteiros, e arredondar cada um pode perder ou acrescentar ações. O método do maior resto resolve: dê a cada intervalo a parte inteira e distribua as ações restantes, uma a cada, aos intervalos com as maiores partes fracionárias, de modo que o cronograma sempre some a ordem.
# From volumes to a profile, then to whole shares by the largest remainder method
import math
def volume_profile(volumes):
total = sum(volumes)
return [v / total for v in volumes]
def vwap_schedule(total, profile):
exact = [total * share for share in profile]
shares = [math.floor(value + 1e-9) for value in exact]
leftover = total - sum(shares)
for index in sorted(range(len(profile)), key=lambda i: (-(exact[i] - shares[i]), i))[:leftover]:
shares[index] += 1
return shares
profile = volume_profile([300, 100, 100, 200, 500, 800]) # a U-shape: busy at the open and the close
print([round(p, 3) for p in profile]) # [0.15, 0.05, 0.05, 0.1, 0.25, 0.4]
print(vwap_schedule(1003, profile)) # [151, 50, 50, 100, 251, 401] adds up to 1003
print(sum(vwap_schedule(1003, profile))) # 1003Limites de participação e acompanhamento da referência
Um cronograma é um plano; o mercado pode estar mais calmo do que o perfil previu. Um teto de participação limita sua negociação em cada intervalo a uma fração do volume real do mercado nele, digamos 10 %, para que você nunca seja a maior parte do fluxo. As ações que você não conseguiu negociar são transferidas para os intervalos seguintes, o que pode deixar algumas sem executar no fim; então você decide se assume mais risco, estende a janela ou para. Depois do dia, compare seu preço médio com o VWAP do mercado: a diferença, em pontos-base, é o erro de acompanhamento do algoritmo. Bater o VWAP negociando tarde num mercado em alta não é habilidade; julgue um algoritmo pelo slippage ao longo de muitos dias.
# A participation cap: never more than a fraction of the market's volume, carrying the rest forward
import math
def cap_participation(schedule, market_volumes, rate):
trades, carry = [], 0
for scheduled, volume in zip(schedule, market_volumes):
wanted = scheduled + carry
done = min(wanted, math.floor(rate * volume + 1e-9))
trades.append(done)
carry = wanted - done
return trades, carry
print(cap_participation([100, 100, 100], [500, 1000, 1000], 0.1)) # ([50, 100, 100], 50)
print(cap_participation([100, 0, 0, 0], [500, 1000, 400, 800], 0.1)) # ([50, 50, 0, 0], 0) the carry is traded when the market allows it# Following the clock (TWAP) and following the volume (VWAP) give different average prices on the same day
prices = [100.0, 100.2, 100.1, 100.4, 100.3, 100.5]
volumes = [300, 100, 100, 200, 500, 800] # busy at the open and the close
market_vwap = sum(p * v for p, v in zip(prices, volumes)) / sum(volumes)
twap_average = sum(prices) / len(prices) # equal pieces at every time
print(round(market_vwap, 4), round(twap_average, 4)) # 100.33 100.25
print(round((twap_average - market_vwap) / market_vwap * 10000, 1)) # -8.0 a TWAP buy beat the VWAP here, by trading light when volume was heavy- O VWAP é
sum(price * volume) / sum(volume), a referência comum; um algoritmo VWAP negocia em proporção ao volume do mercado. - Estime um perfil de volume do histórico e divida a ordem por ele com o método do maior resto para que as ações inteiras somem exatamente.
- Limite sua participação em cada intervalo e transfira o resto; julgue o algoritmo pelo erro de acompanhamento e pelo slippage ao longo de muitos dias.
A seguir: market making, cotar os dois lados e gerir o estoque resultante.
Lab: o que é avaliado
- Calcular a referência com
vwap25 pts - Dar forma ao dia com
volume_profile25 pts - Repartir a ordem com
vwap_schedule25 pts - Respeitar o mercado com
cap_participation25 pts