Operar cuando opera el mercado
El precio medio ponderado por volumen es el precio medio de todo lo negociado, cada operación ponderada por su tamaño: sum(price * volume) / sum(volume). Es la referencia más común en el trading institucional porque es lo que pagó un participante medio ese día. Un algoritmo VWAP intenta igualarlo o superarlo operando *en proporción al volumen del mercado*: mucho cuando el mercado está activo (la apertura, el cierre), poco cuando está tranquilo (la hora de comer). Donde el TWAP sigue el reloj, el VWAP sigue a la multitud, así que cada una de tus órdenes es una fracción parecida de lo que hacen los demás y mueve menos el precio.
# 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 priceUn perfil de volumen y un calendario que lo sigue
No se puede conocer el volumen de hoy por adelantado, así que un algoritmo VWAP usa un perfil de volumen estimado del histórico: la parte media del volumen del día negociada en cada intervalo durante, digamos, las últimas 20 sesiones. El volumen intradía suele tener forma de U, alto en la apertura y el cierre. El calendario da entonces a cada intervalo total * parte acciones. Rara vez son enteros, y redondear cada uno puede perder o añadir acciones. El método del mayor resto lo arregla: da a cada intervalo la parte entera y reparte las acciones sobrantes, una a cada uno, a los intervalos con las mayores partes fraccionarias, de modo que el calendario siempre suma la orden.
# 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))) # 1003Límites de participación y seguimiento de la referencia
Un calendario es un plan; el mercado puede estar más tranquilo de lo que el perfil predijo. Un tope de participación limita tu negociación en cada intervalo a una fracción del volumen real del mercado en él, digamos el 10 %, para que nunca seas la mayor parte del flujo. Las acciones que no pudiste negociar se trasladan a intervalos posteriores, lo que puede dejar algunas sin ejecutar al final; entonces decides si asumir más riesgo, ampliar la ventana o parar. Tras el día, compara tu precio medio con el VWAP del mercado: la diferencia, en puntos básicos, es el error de seguimiento del algoritmo. Superar el VWAP operando tarde en un mercado alcista no es habilidad; juzga un algoritmo por su deslizamiento durante muchos días.
# 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- VWAP es
sum(price * volume) / sum(volume), la referencia común; un algoritmo VWAP opera en proporción al volumen del mercado. - Estima un perfil de volumen del histórico y reparte la orden según él con el método del mayor resto para que las acciones enteras sumen exactamente.
- Limita tu participación en cada intervalo y traslada el resto; juzga el algoritmo por su error de seguimiento y su deslizamiento durante muchos días.
A continuación: creación de mercado, cotizar ambos lados y gestionar el inventario resultante.
Lab: qué se califica
- Calcular la referencia con
vwap25 pts - Dar forma al día con
volume_profile25 pts - Repartir la orden con
vwap_schedule25 pts - Respetar el mercado con
cap_participation25 pts