Négocier quand le marché négocie
Le prix moyen pondéré par les volumes est le prix moyen de tout ce qui s'est échangé, chaque transaction pesant selon sa taille : sum(price * volume) / sum(volume). C'est le benchmark le plus courant du trading institutionnel car c'est ce qu'un participant moyen a payé ce jour-là. Un algorithme VWAP cherche à l'égaler ou le battre en négociant *proportionnellement au volume du marché* : beaucoup quand le marché est actif (l'ouverture, la clôture), peu quand il est calme (l'heure du déjeuner). Là où le TWAP suit l'horloge, le VWAP suit la foule : chacun de vos ordres est une fraction similaire de ce que font les autres et déplace moins le prix.
# 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 profil de volume et un calendrier qui le suit
On ne peut pas connaître à l'avance le volume du jour : un algorithme VWAP utilise donc un profil de volume estimé sur l'historique, la part moyenne du volume de la journée échangée dans chaque intervalle sur, disons, les 20 dernières séances. Le volume intrajournalier a typiquement une forme en U, fort à l'ouverture et à la clôture. Le calendrier donne alors à chaque intervalle total * part actions. Ce sont rarement des entiers, et arrondir chacun peut faire perdre ou gagner des actions. La méthode du plus grand reste règle cela : donnez à chaque intervalle la partie entière, puis distribuez les actions restantes, une chacune, aux intervalles ayant les plus grandes parties fractionnaires, pour que le calendrier totalise toujours l'ordre.
# 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 participation et suivi de la référence
Un calendrier est un plan ; le marché peut être plus calme que ce que le profil prévoyait. Un plafond de participation limite vos échanges dans chaque intervalle à une fraction du volume réel du marché dans cet intervalle, disons 10 %, pour que vous ne deveniez jamais l'essentiel du flux. Les actions que vous n'avez pas pu négocier sont reportées aux intervalles suivants, ce qui peut en laisser d'inexécutées à la fin ; vous décidez alors de prendre plus de risque, d'étendre la fenêtre ou d'arrêter. Après la journée, comparez votre prix moyen au VWAP du marché : l'écart, en points de base, est l'erreur de suivi de l'algorithme. Battre le VWAP en négociant tard dans un marché haussier n'est pas de l'habileté ; jugez un algorithme à son glissement sur de nombreux jours.
# 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- Le VWAP est
sum(price * volume) / sum(volume), le benchmark courant ; un algorithme VWAP négocie proportionnellement au volume du marché. - Estimez un profil de volume sur l'historique et répartissez l'ordre selon lui par la méthode du plus grand reste pour que les actions entières totalisent exactement l'ordre.
- Plafonnez votre participation dans chaque intervalle et reportez le reste ; jugez l'algorithme à son erreur de suivi et à son glissement sur de nombreux jours.
À suivre : la tenue de marché, coter les deux côtés et gérer l'inventaire qui en résulte.
Lab : ce qui est noté
- Calculer la référence avec
vwap25 pts - Modeler la journée avec
volume_profile25 pts - Répartir l'ordre avec
vwap_schedule25 pts - Respecter le marché avec
cap_participation25 pts