σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft AcademyRutas
Ctrl K
AO
+1,2 k estudiantes
Iniciar sesiónUnirse ↗
Rutas
AO
+1,2 k estudiantes
Iniciar sesión
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Ruta de Python›Módulo 6 · Backtesting y costes de trading›Tema 6.4·~45 minAvanzado

Costes de transacción

Posición en la rutaMódulo 6 de 11 · Tema 4 de 6
6.4 Costes de transacción
—Puntuación del lab— / 100XP total—Racha—

Operar no es gratis

Un backtest que opera gratis sobrestima toda estrategia, y cuanto más opera, más miente. Los costes reales son la comisión del bróker, tasas de bolsa y regulatorias y, sobre todo, el diferencial compra-venta que se cruza cada vez. Suelen cotizarse en puntos básicos del valor operado: 1 pb es 0,01 %. Un viaje de ida y vuelta a 5 pb por lado cuesta 10 pb, algo que una estrategia que gana 3 pb al día no sobrevive si cambia de lado a diario.

def trade_cost(notional, bps):
    return abs(notional) * bps / 10000

print(trade_cost(25000, 5))         # 12.5   buying 25,000 at 5 bps
print(trade_cost(-25000, 5))        # 12.5   selling costs the same
print(trade_cost(1_000_000, 1))     # 100.0  one basis point on a million

Rotación: cuánto operas

El coste depende de cuánto cambia la posición, no de su tamaño. Con la posición como fracción de la cuenta, la rotación de un día es el cambio absoluto respecto a la posición anterior: abs(position[t] - position[t - 1]), partiendo de cero el día 0. Seguir largo no cuesta nada tras la primera compra; pasar de largo a corto opera dos veces la cuenta. Sumada en un año, la rotación es la primera cifra que mirar en cualquier estrategia.

import numpy as np

positions = np.array([1.0, 1.0, -1.0, -1.0, 0.0])        # long, long, flip to short, short, flat
turnover = np.abs(np.diff(positions, prepend=0.0))       # day 0 starts from a flat position
print(turnover)                                          # [1. 0. 2. 0. 1.]
print(turnover.sum())                                    # 4.0  accounts traded over the five days

Rendimientos netos y coste de equilibrio

El coste del día, como fracción de la cuenta, es turnover * bps / 10000, y el rendimiento neto es el bruto menos eso. Un número útil es el coste de equilibrio: el coste por unidad operada con el que el beneficio total es exactamente cero, sum(gross) / sum(turnover) * 10000. Compáralo con lo que pagas de verdad. Una estrategia con equilibrio de 2 pb está muerta en la práctica; una de 40 pb tiene margen.

# Gross and net returns of a rule that flips often, at 5 bps per unit traded
import numpy as np

rng = np.random.default_rng(11)
positions = np.where(rng.random(250) > 0.5, 1.0, -1.0)      # a coin-flip position, flipping often
gross = rng.normal(0.0015, 0.005, size=250)                   # a strategy with a real daily edge before costs

turnover = np.abs(np.diff(positions, prepend=0.0))
net = gross - turnover * 5 / 10000
print(round(float(gross.sum()), 4), round(float(net.sum()), 4))
print(round(float(turnover.sum()), 1), "accounts traded;", round(float(gross.sum() / turnover.sum() * 10000), 2), "bps break-even")
# The break-even cost of a small example
import numpy as np

gross = np.array([0.004, 0.002, 0.003, 0.001, 0.002])
positions = np.array([1.0, 1.0, -1.0, -1.0, 0.0])
turnover = np.abs(np.diff(positions, prepend=0.0))
breakeven = gross.sum() / turnover.sum() * 10000
print(round(float(breakeven), 2))                                           # 30.0 bps per unit traded
print(abs(round(float((gross - turnover * breakeven / 10000).sum()), 10)))       # 0.0: the whole profit is gone
Los costes no son solo la comisiónUn cargo fijo en pb es el modelo más simple. Los costes reales dependen también del instrumento, la hora y el tamaño de la orden: el diferencial es mayor en small caps y en la apertura, y las órdenes grandes mueven el precio en tu contra, tema de los siguientes temas. Sea cual sea el modelo, un backtest que ignora costes es optimista, y uno que los cobra demasiado bajos es peligroso.
  • Los costes se cotizan en puntos básicos del valor operado: abs(notional) * bps / 10000, igual en compra y venta.
  • La rotación es abs(cambio de posición) (un giro opera 2); rendimiento neto = bruto - rotación * pb / 10000.
  • El coste de equilibrio sum(gross) / sum(turnover) * 10000 dice cuánto coste soporta una estrategia; compáralo con el real.

A continuación: el deslizamiento, la diferencia entre el precio esperado y el obtenido.

Lab: qué se califica

  • Poner precio a una operación con trade_cost25 pts
  • Contar lo operado con turnover25 pts
  • Restar la factura con net_returns25 pts
  • Hallar el coste que la anula con breakeven_bps25 pts
Explorador · lab-90-1
▾ lab-90-1
…
bash · en el navegador
learner@quantcraft:~/lab-90-1$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

Todo lo que hay en lab-90-1 vuelve a los archivos iniciales y se limpia el terminal. Tu solución actual se perderá: no se puede deshacer.

Se restablecerá
  • main.py → código inicial
  • Historial del terminal y salida de pruebas
  • Cambios sin enviar
Se conservará
  • Mejor puntuación del lab
  • Tu XP y tu racha
  • Progreso de la lección y notas

Inicia sesión para ejecutar tu código.

Todas las clases se pueden leer gratis. Crea una cuenta o inicia sesión para ejecutar código, lanzar las pruebas y enviar este lab.

  • IDE en el navegador con terminal bash — sin instalar nada
  • Corrección instantánea, XP y tu lugar en la ruta
Crear una cuentaIniciar sesión
O
Continuar con GitHub