Nano
Discover Nano: digital money for everyday payments, businesses and the software we build. Explore wallets, the ecosystem and AI-agent integrations.
Operativa diaria de los agentes de tradesim: cuentas, operaciones abiertas y cerradas, capital asignado, rendimiento y aprendizaje.
Translation pending
FinancePython · JavaScriptGitHub @andygargol2010-afkOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Simulador de trading de alta fidelidad para entrenar agentes de IA (RL y LLM) que después operarán en mercados reales. El objetivo de diseño es que lo aprendido aquí se transfiera a real con la menor fricción posible: por eso el simulador es deliberadamente conservador (nunca regala precios, liquidez ni información del futuro).
Decimal exacta (0.003 BTC, 1.75 AAPL...).PaperTradingEnv) + API "humana" + tools JSON para LLMs (Claude, OpenAI-style).act(observation) -> action; run_episode, evaluate_agent y
compare_agents se encargan del resto, con métricas, resumen legible y gráficas.cd tradesim
pip install -e ".[all,dev]" # todo: gymnasium, yfinance, ccxt, alpaca-py, pyarrow...
# o lo mínimo (solo numpy + pandas; datos sintéticos):
pip install -e .
Python 3.11+. Tests: pytest -q (61 tests, < 10 s, sin red). Las gráficas usan matplotlib (pip install -e ".[viz]").
from tradesim import TradingSimulator, SimulatorConfig, SyntheticProvider, SimpleRSIAgent, evaluate_agent
env = TradingSimulator(
provider=SyntheticProvider(seed=1), # o CachedProvider(YFinanceProvider()) con datos reales
config=SimulatorConfig(initial_cash=10_000, fees="alpaca"),
symbols=["SPY", "AAPL", "MSFT", "NVDA", "GLD", "BTC-USD", "ETH-USD"],
start="2021-01-01", end="2024-12-31",
)
agent = SimpleRSIAgent(period=14, oversold=30, overbought=70, position_pct=10)
report = evaluate_agent(agent, env, n_episodes=10, episode_length=126) # imprime cada episodio y la tabla
report.plot("runs/evaluation.png") # curvas de todos los episodios, distribución de retornos, Sharpe
env.print_summary() # resumen del último episodio
env.print_portfolio() # cash, posiciones (cantidad fraccionaria, precio medio, PnL no realizado)
env.plot_results(path="runs/episode.png") # equity vs buy & hold, drawdown, compras/ventas sobre el precio
Ejemplo completo ejecutable: python examples/practice.py (crea el entorno, corre un episodio con render, 10
episodios de evaluación, gráficas en runs/practice/ y comparación contra baselines).
Los agentes operan cada día con precios reales sobre 100 activos (ETF, acciones y cripto), cada uno en su propia cuenta de práctica de 100 $ (initial_cash en live/config.json), y el
dashboard (dashboard/index.html, la portada en Vercel) muestra por cuenta: equity, P&L total y del día, operaciones
abiertas (cantidad fraccionaria, capital asignado y % de la cuenta al entrar, precio medio, precio actual, resultado
no realizado, días abierta, motivo de entrada), operaciones cerradas (asignado, entrada/salida, resultado neto,
rendimiento, comisiones, motivos de entrada y salida), órdenes pendientes, el diario de decisiones (qué vio el agente
y por qué hizo lo que hizo), su aprendizaje y los precios. Las cripto se actualizan en tiempo real en el navegador.
| Cuenta | Agente | Cómo decide / aprende |
|---|---|---|
| Aprendiz lineal | OnlineLinearAgent | Predice qué activos batirán a la media en 5 sesiones (de apertura a apertura, lo que realmente puede operar) con dos modelos: lineal online (olvido por día) y LightGBM re-entrenado cada semana, pesados por su IC fuera de muestra. Solo opera si su IC es significativo y si la ventaja esperada paga 1,5 veces los costes |
| RSI adaptativo | WalkForwardRSIAgent | RSI cuyo ajuste se re-optimiza cada semana simulando variantes en el último año (con costes), puntuadas por la mediana del Sharpe de 4 trimestres |
| Cruce de medias | MovingAverageCrossAgent | Seguidor de tendencia fijo (referencia sin aprendizaje) |
| Buy & hold | BuyAndHoldAgent | Compra todo el primer día y mantiene: la vara de medir |
| Analista discrecional | DiscretionaryAgent | Sin regla fija: estudia patrones técnicos, macro por tipo de activo, noticias analizadas y el calendario del mes, y decide la cartera con tesis, catalizadores e invalidación. Cada posición lleva un pronóstico (probabilidad de subida, horizonte, retorno esperado) que se puntúa (Brier, calibración por convicción, régimen y patrón); solo guarda lecciones respaldadas por datos (n ≥ 30, t > 2). Su motor local aprende los pesos de técnico/noticias/tendencia. Decide con IA gratuita (Gemini/Groq) si hay clave, o con el motor local gratuito |
| Inversor tradicional | TraditionalInvestorAgent | Cartera de política diversificada, rebalanceo trimestral o por bandas |
| Meta-agente (Hedge) | HedgeMetaAgent | Reparte el capital entre los demás agentes con pesos multiplicativos (w_i *= exp(η·r_i)): converge al mejor agente con arrepentimiento ≤ √T |
El aprendiz usa como variables precio, patrones técnicos (ruptura del rango de 20 días, calidad de la tendencia, velas), volumen, contexto de mercado (SPY; BTC para cripto), fuerza frente al mercado y a su sector, e interacciones con el régimen. Aprende el retorno relativo a la media de los activos (elige activos, no adivina el mercado) y su olvido es por día (λ por muestra = λ_día^(1/N)), así no olvida antes por tener más activos.
Riesgo. El aprendiz, el cruce de medias y el combinador dimensionan por riesgo: peso ∝ 1/σ y cartera escalada a
una volatilidad del 15 % anual (covarianza de 60 días), con un tope del 30 % por grupo de activos correlacionados
(correlación > 0,7, enlace completo) y para la cripto en conjunto; el analista también respeta esos topes. En una
caída, el runner limita lo invertido a 1 − (caída / 35 %)² del capital y vende hasta ese nivel; al 35 % cierra todo
y, tras 20 días, reinicia el máximo y la cuenta vuelve a operar (buy & hold queda fuera: "drawdown_control": false).
El aprendiz penaliza la rotación: solo vende si la pérdida esperada supera el coste de vender y solo sustituye una
posición si la ventaja nueva paga las dos operaciones. La decisión diaria es a las 15:40 UTC, lejos de la apertura.
Señales verificadas. El analista mide el IC de cada señal (técnico, noticias, tendencia) a 1, 3 y 5 días: una señal sin IC positivo tras 30 días pierde su peso y con IC positivo no significativo pesa la mitad. Cada patrón técnico lleva su historial por régimen y solo cuenta si demuestra ventaja (n ≥ 30, |t| > 2; si funciona al revés, se invierte). El aprendiz descarta barras con |retorno| > 10σ robustas o volumen 0 y recibe el régimen de mercado (k-means sobre volatilidad, tendencia y amplitud del mercado) como variable y en interacciones.
Memoria y combinación. El analista guarda cada día el estado del mercado y lo que pasó después, y al decidir ve
los 10 días pasados más parecidos. El Combinador de señales (SignalStackingAgent) aprende a combinar las señales
de los demás agentes, con pesos propios para los regímenes de tensión.
Cambios seguros. Una cuenta con "shadow_of": "<cuenta>" prueba en paralelo una versión nueva; a los 28 días la
sustituye solo si gana con evidencia (prueba t pareada de rendimientos diarios > 2, mejor Sharpe y mejor IC). El estado
aprendido se guarda cada semana en live/accounts/<cuenta>/checkpoints/ y, si el IC de los últimos 20 días se vuelve
negativo, se restaura el último punto con IC positivo. El panel ¿Ventaja real o suerte? muestra por agente el Sharpe
con su intervalo de confianza (bootstrap), la probabilidad de batir a buy & hold, el IC con su t y los días que faltan
para que el resultado sea significativo.
Validación honesta (scripts/validate.py, .github/workflows/nightly-validation.yml, cada noche): validación
cruzada con purga y embargo del aprendiz sobre 600 activos del universo y 5 años (muestras semanales, IC de rango
fuera de muestra del modelo lineal, LightGBM y su media), y backtests hacia adelante de cada cuenta tras costes frente a
buy & hold, en live/validation.json. Puerta de promoción (.github/workflows/validation-gate.yml, en cada pull
request que toque agentes o la configuración): cada cuenta cambiada se prueba en su versión nueva y actual y solo pasa
si mejora el Sharpe tras costes (y el IC, si lo mide) y supera a buy & hold. Ojo: universe.csv solo tiene los
integrantes actuales de los índices (sesgo de supervivencia), así que los resultados sobre el pasado son optimistas.
Cómo funciona. .github/workflows/live-trading.yml ejecuta scripts/live_run.py con GitHub Actions:
decide; más de 1 h después de la apertura de Nueva York, cuando los spreads ya se estrecharon): sincroniza, y si la bolsa de Nueva York está abierta (lunes a viernes, sin
festivos NYSE) cada agente observa precios actuales, decide y sus órdenes se ejecutan al momento contra la cotización
y el libro reales (spread, impacto y comisiones de Alpaca). Primero ventas, luego compras con lo liberado.
Una sola decisión por día y cuenta, aunque el workflow se relance.mark): valoración a mercado (las cripto cotizan 24/7).research): noticias analizadas, datos macro, calendario del próximo mes y
patrones técnicos (ver Investigación abajo), y valoración de las cuentas (ejecuta los stop-loss y objetivos que el
mercado haya alcanzado). La decisión diaria también refresca la investigación antes de decidir.live/ (estado de cuentas, modelos aprendidos, diarios, runs.jsonl) y de
dashboard/live.json; Vercel redespliega solo. El historial de git es el registro auditable de todo.mark o decide).Datos. Cripto: Coinbase vía ccxt (bid/ask y libro L2 reales, sin claves). Acciones: Yahoo Finance (último precio,
spread modelado) o, si defines los secretos ALPACA_API_KEY / ALPACA_SECRET_KEY en GitHub, Alpaca (bid/ask reales).
Stop-loss y objetivo de beneficio. El analista protege cada posición con dos órdenes reales que se ejecutan solas, sin esperar a la siguiente decisión: un stop-loss (venta si el precio cae al nivel que invalida la tesis, normalmente justo bajo un soporte o a 2 ATR) y un objetivo (venta en una resistencia o a 2 veces el riesgo). Son órdenes one-cancels-other (al ejecutarse una se cancela la otra) y reduce-only (solo pueden cerrar la posición). El stop solo sube, nunca baja (stop dinámico); si el precio ya está bajo el stop, la posición se cierra.
OrderIntent.buy(..., stop_loss=, take_profit=) o la acción SetExits(símbolo, stop_loss, take_profit); en el simulador, sim.set_exits() / sim.get_exits().Stop-loss para todos. Toda posición que su agente deja sin stop propio recibe uno automático a 2,5 ATR del precio
(risk.stop_loss_atr; null lo desactiva), dinámico: sube con el precio y nunca baja. El analista usa los suyos y
Buy & hold queda excluido ("auto_stop": false en su cuenta) para seguir siendo una referencia pura.
Diario de la simulación ("chronicle": true). Cada agente escribe su propia nota al decidir (Agent.diary):
qué hizo, por qué y cómo le influyó la situación del mercado (etiqueta calma · alcista, tensión · bajista,
solo cripto...). Los agentes de reglas la redactan desde su propia lógica (RSI, medias, predicciones, bandas) y el
analista con su propio motor de IA, en la misma petición de su decisión. Tras el primer run de cada día (UTC) se
compila la entrada del día anterior en dashboard/diario.md y dashboard/diario.json (página diario.html), con la
situación y los patrones técnicos del día y una tabla acumulada de qué hace cada agente en cada tipo de situación.
Escribir o rehacer un día a mano: python scripts/live_run.py --chronicle 2026-10-26.
Inversor tradicional (TraditionalInvestorAgent). No hace trading: mantiene una cartera de política (índices
SPY/QQQ/EEM 43 %, bonos TLT 15 %, oro GLD 7 %, 8 acciones de calidad 28 %, BTC/ETH 5 %, 2 % en efectivo; máx. 25 % por
activo). Elige las acciones una vez al año (crecimiento estable a 1 año ajustado por volatilidad, sobre su media de 200
días, una por sector; conserva las que sigan entre las 16 mejores) y rebalancea cada trimestre o antes si una clase de
activo sale de su banda (regla 5/25), sin tocar desviaciones menores del 1 %. Sin stops, sin market timing y sin operar
en fin de semana. Prueba a 1 año con 100 USD: 120,8 (oct-24→oct-25, caída máx. 15,8 %) y 109,8 (oct-25→oct-26, caída
máx. 7,0 %), frente a 130,2 y 103,1 de buy & hold.
Cripto en fin de semana y festivos ("weekend_crypto": true). Con la bolsa cerrada cada agente decide igual que
un día laborable, comparando los 100 activos (acciones a su último precio), y solo se ejecuta la parte cripto: una
compra de cripto ocurre solo si entra en su selección frente a las acciones, es decir, si la prefiere a lo que podría
comprar el lunes; si no, espera. Las ventas de cripto se ejecutan; las compras usan solo efectivo libre (nunca se venden
acciones para financiarlas) y se reducen si no alcanza. El resto de la decisión queda en el diario como aplazado.
Límites de riesgo (live/config.json → risk): máximo 30 % por posición, la cuenta deja de abrir posiciones con un
drawdown ≥ 35 %, máximo 60 órdenes por decisión. Una cuenta que falla no detiene a las demás.
Añadir o cambiar agentes: edita accounts en live/config.json (agent es una clase de tradesim.agents o
"paquete.modulo:MiAgente", params sus argumentos). Cualquier Agent sirve; si aprende, implementa
state_dict() / load_state_dict() y su estado se guarda en live/accounts/<id>/agent.json tras cada ejecución.
tradesim/research/ alimenta a los agentes (y a la página Investigación del dashboard, dashboard/research.html).
Se guarda en live/research/*.json en cada ejecución horaria:
tradesim/patterns.py, sin red): tendencia, soportes y resistencias con número de toques,
velas (envolventes, martillo, estrellas, harami, tres soldados…), figuras (doble techo/suelo, hombro-cabeza-hombro,
triángulos, cuñas, rupturas con volumen, máximos de 52 semanas) y eventos de indicadores (cruce dorado/muerte,
divergencias RSI, cruces MACD, compresión de Bollinger, huecos, picos de volumen). Cada patrón trae dirección,
fuerza 0-1, estado (en formación/confirmado) y niveles (clavicular, objetivo, invalidación). Cualquier agente los usa
con obs.patterns("AAPL"); los agentes LLM con la herramienta detect_patterns.Motores de análisis: gratis por defecto. Se prueban en orden y, si uno falla o agota su cupo, entra el siguiente:
GEMINI_API_KEY o GROQ_API_KEY en GitHub → Settings → Secrets and variables → Actions. Se usan solo
para el análisis de las noticias importantes y la decisión diaria (unas 25-50 peticiones al día), con un tope diario
propio (daily_requests) para no pasar del cupo gratuito. Sus límites los fija cada proveedor y cambian a menudo.ANTHROPIC_API_KEY): de pago, solo si añades esa clave; añade búsqueda web.Sin ninguna clave el sistema funciona entero y gratis. Ajustes en live/config.json → research (engines,
free_models, daily_requests, triage_engine…) y en los params de la cuenta analista. La página de
Investigación muestra qué motor analizó cada noticia y el analista indica con qué motor tomó cada decisión.
El agente no cambia: solo cambia quién ejecuta sus órdenes. AlpacaTrader ofrece la misma interfaz que el simulador,
envía las órdenes a Alpaca, registra los fills reales y se reconcilia con el bróker en cada ejecución (el bróker manda).
ALPACA_API_KEY y ALPACA_SECRET_KEY.live/config.json, añade una cuenta nueva con el mismo agent/params y "broker": "alpaca-paper", y copia
live/accounts/<id-original>/agent.json a live/accounts/<id-nuevo>/agent.json para que empiece con lo aprendido.
Una cuenta de Alpaca por agente.alpaca-paper, cambia a "broker": "alpaca-live" (claves de la cuenta real) y crea la variable
de repositorio TRADESIM_ALLOW_LIVE = 1. Sin esa variable, el sistema se niega a operar con dinero real.Ensayo local (sin esperar días reales): ReplayLiveProvider reproduce datos históricos o sintéticos como si fueran en
vivo, con un reloj inyectable; los tests de tests/test_live_trading.py lo usan para simular semanas de operativa.
Limitaciones. En las cuentas paper la ejecución es simulada (sobre precios reales); los horarios de GitHub
Actions pueden retrasarse unos minutos; la cadencia es diaria (no es trading intradía); y que un agente aprenda no
significa que tenga ventaja: el panel de aprendizaje muestra la correlación entre predicción y realidad (cerca de 0 =
sin ventaja). Compara siempre con Buy & hold después de costes antes de arriesgar dinero.
dashboard/backtest.html es una página estática que compara agentes y permite explorar cada episodio: tabla comparativa,
retorno medio barra a barra frente a buy & hold, retorno de cada episodio, y por episodio equity, drawdown,
precio con compras/ventas, trades cerrados y ejecuciones. Modo claro/oscuro, tooltips y enlaces directos
(#agent=1&episode=3).
python scripts/build_dashboard.py # genera dashboard/data.json (datos sintéticos, ~3 s)
python scripts/build_dashboard.py --data yfinance --episodes 20 --length 252
python -m http.server -d dashboard 8000 # vista previa en http://localhost:8000
Despliegue en Vercel: importa el repo (Add New → Project). vercel.json ya sirve dashboard/ como sitio estático,
sin build de Python; para actualizar los resultados, regenera data.json, haz commit y push.
from tradesim import TradingSimulator, SimulatorConfig, CachedProvider
from tradesim.data.yfinance_provider import YFinanceProvider
sim = TradingSimulator(
provider=CachedProvider(YFinanceProvider()), # o SyntheticProvider(seed=1) sin red
config=SimulatorConfig(initial_cash=10_000, timeframe="1d", fees="alpaca", seed=42),
)
sim.reset(symbols=["AAPL", "BTC-USD", "SPY"], start="2023-01-01", end="2024-01-01")
sim.get_price("BTC-USD") # {'bid':..., 'ask':..., 'mid':..., 'spread_bps':..., ...}
sim.place_order("BTC-USD", "buy", 0.003) # fracción de BTC
sim.place_order("AAPL", "buy", 1.75, order_type="limit", limit_price=180.0)
sim.place_order("SPY", "buy", notional=2_000) # por importe en USD
sim.place_order("SPY", "sell", 5, order_type="stop", stop_price=400)
sim.step() # avanza una barra: las órdenes se ejecutan aquí
sim.get_portfolio(); sim.get_trades(); sim.get_metrics()
sim.get_historical_data("AAPL", timeframe="1w", limit=20, indicators=True)
sim.save_state("runs/state.json")
Scripts de ejemplo (examples/):
| Script | Qué demuestra |
|---|---|
practice.py | Flujo completo de práctica: entorno → agente → episodios → métricas → gráficas → comparación con baselines |
demo.py | Carga 2.000+ activos, compras fraccionadas, todos los tipos de orden, rechazos, liquidez limitada con fills parciales, estrategia, portfolio, métricas y persistencia. `--data synthetic |
rl_agent.py | Entorno Gymnasium: baselines (random, buy&hold, momentum) en episodios fuera de muestra; PPO opcional con stable-baselines3 |
llm_agent.py | ClaudeAgent(Agent) con function calling, ejecutado con run_episode (--dry-run para un agente guionizado sin API) |
live_paper.py | Paper trading en vivo con cotizaciones y libro L2 reales vía ccxt, con estado reanudable |
┌─────────────────────── agentes ───────────────────────┐
│ RL (Gymnasium) LLM (tools JSON) scripts │
│ env.PaperTradingEnv agents/llm_tools API Python│
└───────────┬───────────────────┬──────────────┬────────┘
▼ ▼ ▼
┌──────────────────────────────────────────────────────────┐
│ simulator.BaseSimulator (bróker: validación pre-trade, │
│ reservas de cash, API humana, métricas, persistencia) │
├───────────────────────────┬──────────────────────────────┤
│ TradingSimulator (reloj │ live.LivePaperTrader (reloj │
│ histórico, barra a barra) │ de pared, cotizaciones+L2) │
└─────────────┬─────────────┴───────────────┬──────────────┘
▼ ▼
┌──────────────────────────────────┐ ┌───────────────────────────────────┐
│ execution.engine.ExecutionEngine │──▶│ portfolio.Portfolio (Decimal) │
│ camino intrabarra, stops/gaps, │ │ posiciones, PnL, comisiones, │
│ maker/taker, capacidad, TIF │ │ margen, financiación, equity curve │
│ market.liquidity spread + libro │ │ portfolio.metrics Sharpe, DD... │
│ execution.fees perfiles bróker│ └───────────────────────────────────┘
└──────────────┬───────────────────┘
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ data.store.MarketData (barras + estimaciones sin look-ahead + escenarios)│
│ data.base.DataProvider ← YFinance | CCXT | Alpaca | Synthetic | Routing │
│ └─ CachedProvider (parquet en ~/.tradesim/cache)│
│ assets.universe.AssetUniverse (reglas por activo: tick, lote, mínimos) │
└──────────────────────────────────────────────────────────────────────────┘
| Módulo | Responsabilidad |
|---|---|
types.py | Order, Fill, ClosedTrade, Quote, enums; conversión D() a Decimal sin artefactos binarios |
assets/universe.py | Asset (clase, tier de liquidez, tick, paso de lote, mínimo nocional, sesión) y AssetUniverse (CSV, filtros) |
data/ | Proveedores, caché, timeframes/resampling, MarketData con volatilidad y volumen típico calculados solo con barras pasadas |
market/liquidity.py | Perfiles por clase/tier, modelo de spread, libro sintético por ley de la raíz cuadrada, barrido de libro L2 real |
market/conditions.py | Escenarios: volatilidad ×N, tendencia, gaps, flash crash, sequía de liquidez |
execution/engine.py | Motor de fills único para histórico y vivo |
execution/fees.py | FeeSchedule (maker/taker %, fijo, por acción, mín./máx., tasas SEC/FINRA) y presets por bróker |
portfolio/ | Contabilidad exacta y métricas |
simulator.py / live.py | Relojes histórico y en vivo sobre el mismo núcleo de bróker |
env.py | PaperTradingEnv (Gymnasium) |
agents/base.py | Contrato Agent.act(observation) -> action, Observation, tipos de acción, adaptadores (FunctionAgent, PolicyAgent) |
agents/examples.py | SimpleRSIAgent, MovingAverageCrossAgent, RandomAgent, BuyAndHoldAgent |
practice.py | run_episode, evaluate_agent, compare_agents, EpisodeResult, EvaluationReport |
reporting.py | print_summary, print_portfolio, plot_results, plot_evaluation |
El simulador está situado en el cierre de la barra k (sim.now). El agente ve todo hasta la barra k (incluida),
envía órdenes y llama a step(). Las órdenes se ejecutan en la barra k+1 (apertura y luego camino intrabarra), se
valora la cartera al cierre de k+1 y avanza el reloj. Es exactamente la latencia mínima que tendrías en real. La
volatilidad y el volumen típico que usan el spread y el impacto se calculan solo con barras anteriores a la ejecutada.
get_historical_data nunca devuelve barras no cerradas, tampoco al agregar a timeframes mayores.
| Aspecto | Cómo se simula |
|---|---|
| Bid/ask | Real si el feed lo da (ccxt, Alpaca, modo vivo). Si no: spread = base(clase, tier) × (σ/σ_típica)^0.5 × multiplicador de escenario, con mínimo de 1 tick. Ej.: AAPL ~1,5 bps, small cap ~25 bps, token long-tail ~100 bps |
| Market | Apertura de la siguiente barra ± medio spread + impacto + slippage fijo/ruido opcional |
| Impacto | Ley de la raíz cuadrada: el libro sintético cumple Δp = Y·σ·√(Q/V) (Y=impact_coefficient, por defecto 1.0). Con libro L2 real (ccxt en vivo) se barre nivel a nivel |
| Liquidez | Máximo participation_rate (10 %) del volumen de la barra por barra, compartido entre todas las órdenes del símbolo. El resto sigue trabajando (GTC/DAY) o se cancela (IOC). FOK: todo o nada |
| Limit | Marketable al llegar → taker hasta su límite (con mejora de precio). Si no, descansa y solo se ejecuta al límite (maker) si el precio lo atraviesa; si solo lo toca, con probabilidad limit_touch_fill_prob (incertidumbre de cola) |
| Stop / stop-limit | Se disparan al tocar el stop en el camino O→L→H→C (barra alcista) u O→H→L→C (bajista). Gaps: si la apertura salta el stop, se ejecuta a la apertura (te comes el gap) |
| Ticks y lotes | Reg NMS (0,01 $ ≥ 1 $, 0,0001 $ por debajo); cripto ~6 cifras significativas. Precios redondeados en contra del trader; cantidades hacia abajo al paso del lote (1e-9 acciones, 1e-8 cripto). Mínimo nocional 1 $ |
| Comisiones | Por clase de activo y tipo de liquidez (maker/taker), con mínimos/máximos por orden y tasas regulatorias en ventas de acciones |
| Cash / margen | Cuenta de efectivo por defecto (sin apalancamiento ni cortos). Reservas por órdenes abiertas (no se gasta dos veces el cash). Opcional: max_leverage, cortos con coste de préstamo, interés de margen, margin call con liquidación |
| Horarios | Acciones solo en barras de sesión (las órdenes esperan en fin de semana); cripto 24/7; forex 24/5. Órdenes DAY expiran al cambiar la fecha de sesión (Nueva York) |
Configuración (todo en SimulatorConfig / ExecutionConfig):
from decimal import Decimal
from tradesim import SimulatorConfig, ExecutionConfig, MarginConfig, SpreadModel
config = SimulatorConfig(
initial_cash=50_000, timeframe="15m", fees="ibkr_tiered", seed=7, scenario="high_volatility",
execution=ExecutionConfig(slippage_model="sqrt_impact", impact_coefficient=0.8, participation_rate=0.05,
fixed_slippage_bps=1.0, slippage_noise_bps=2.0, limit_touch_fill_prob=0.3,
spread=SpreadModel(spread_multiplier=1.5)),
margin=MarginConfig(max_leverage=Decimal(2), allow_short=True, maintenance_margin=Decimal("0.25")),
max_position_pct=25, log_dir="runs/exp1",
)
slippage_model="none" desactiva spread/impacto/capacidad (solo para ablaciones: medir cuánto rendimiento se "come"
la microestructura).
fees=)| Preset | Acciones/ETFs | Cripto | Forex |
|---|---|---|---|
alpaca (defecto) | 0 $ + SEC/FINRA en ventas | 0,15 % maker / 0,25 % taker | 0 |
ibkr_fixed | 0,005 $/acción, mín. 1 $, máx. 1 % | 0,18 %, mín. 1,75 $ | 0,2 bps, mín. 2 $ |
ibkr_tiered | 0,004 $/acción (incl. aprox. tasas de exchange), mín. 0,35 $ | igual | igual |
binance | (Alpaca) | 0,10 % / 0,10 % | - |
coinbase | (Alpaca) | 0,60 % / 1,20 % | - |
kraken | (Alpaca) | 0,25 % / 0,40 % | - |
pct_plus_fixed | 0,10 % + 1 $ por orden | igual | igual |
zero | 0 | 0 | 0 |
Son tarifas del nivel de entrada (2025) para particulares. Verifica las tuyas: cualquier campo se sobrescribe con
FeeModel.preset("alpaca").with_override(AssetClass.CRYPTO, FeeSchedule(...)).
| Proveedor | Activos | Key | Notas |
|---|---|---|---|
SyntheticProvider(seed) | todos | no | GARCH + colas t + saltos + factor común + gaps nocturnos; estable por prefijo y coherente entre timeframes. Para desarrollo, CI y episodios ilimitados; no para validar una estrategia |
YFinanceProvider() | acciones, ETFs, cripto, forex, índices | no | No oficial y con rate limit (429): úsalo siempre con CachedProvider. Intradía corto: 1m ≈ 30 días, 5m–30m 60 días, 1h 730 días. Precios ajustados por splits y dividendos |
CCXTProvider("binance" | "coinbase" | "kraken" | ...) | cripto | no (endpoints públicos) | OHLCV + bid/ask y libro L2 reales. Binance bloquea algunas regiones (HTTP 451); Kraken solo sirve las últimas 720 velas |
AlpacaProvider() | acciones, ETFs, cripto | sí (gratis) | export ALPACA_API_KEY=... ALPACA_SECRET_KEY=.... Plan gratuito = feed IEX: el volumen se reescala ×40 (iex_volume_scale) para aproximar el consolidado; con SIP usa feed="sip" |
Combinar fuentes y cachear:
from tradesim import RoutingProvider, CachedProvider
from tradesim.data.ccxt_provider import CCXTProvider
from tradesim.data.yfinance_provider import YFinanceProvider
provider = CachedProvider(RoutingProvider([CCXTProvider("coinbase"), YFinanceProvider()]), "~/.tradesim/cache")
Detrás de un proxy corporativo exporta HTTPS_PROXY y REQUESTS_CA_BUNDLE; CCXTProvider las respeta
(trust_env=True, ccxt las ignora por defecto).
Para añadir otra fuente (Polygon, tu base de datos, ficheros...) implementa DataProvider.supports() y get_bars()
devolviendo un DataFrame con índice UTC (apertura de barra) y columnas open, high, low, close, volume; opcionalmente
spread_bps (spread medio observado, que el motor usará en lugar del modelo), get_quote() y get_order_book().
python scripts/build_universe.py [--crypto 300] (Wikipedia para índices,
CoinGecko para cripto, excluyendo stablecoins y tokens envueltos/stakeados).AssetUniverse.from_csv("mis_activos.csv"), columnas mínimas symbol,asset_class (+ opcionales
name, liquidity_tier, tick_size, qty_step, min_notional, shortable, session...). Combinar: AssetUniverse.default().merge(otro).universe.add(Asset("XYZ", AssetClass.EQUITY, liquidity_tier=4, fractionable=False)).El liquidity_tier (1 = SPY/AAPL/BTC … 5 = micro cap / token long-tail) determina spread base, volumen típico de
respaldo y (en sintético) la volatilidad.
Un agente solo necesita act(observation) -> action (y opcionalmente reset(), llamado al inicio de cada episodio):
from tradesim import Agent, OrderIntent, TargetWeights, ClosePosition
class MyAgent(Agent):
def act(self, obs):
closes = obs.closes("BTC-USD", 50) # numpy, solo barras ya cerradas
if obs.position("BTC-USD") == 0 and closes[-1] > closes.mean():
return OrderIntent.buy("BTC-USD", percent_of_equity=20) # o quantity=0.003 / notional=500
if obs.position("BTC-USD") > 0 and closes[-1] < closes.mean():
return ClosePosition("BTC-USD")
return [] # hold
Observation (para agentes de reglas / LLM con TradingSimulator): timestamp, step, steps_remaining,
symbols, prices (último cierre), cash, equity, positions (cantidad exacta Decimal), open_orders, y
los métodos closes(symbol, n) (rápido, numpy), history(symbol, n, timeframe, indicators) (DataFrame con
SMA/EMA/RSI/ATR/MACD/Bollinger), quote(symbol), portfolio(), weight(symbol), to_text() (para prompts) y
sim (para agentes LLM que operan con llm_tools.dispatch).
Acciones válidas: None/[] (hold), OrderIntent o un dict con los argumentos de place_order (market, limit,
stop, stop-limit, fracciones, notional, percent_of_equity), TargetWeights({...}) o {"weights": {...}},
ClosePosition(symbol), CancelOrders(symbol=None), o una lista de ellas. Las órdenes inválidas no lanzan
excepciones: vuelven con status="rejected" y su motivo, y se cuentan en rejected_orders.
Con PaperTradingEnv (RL) el mismo run_episode / evaluate_agent funciona: el agente recibe el vector de
observación y devuelve una acción del action_space. PolicyAgent(model) adapta cualquier política con
predict() (Stable-Baselines3) y RandomAgent(action_space=env.action_space) da un baseline aleatorio.
run_episode(agent, env, render=False, seed=None, episode_length=None, random_start=None, max_steps=None) →
EpisodeResult (metrics, equity, fills, trades, print_summary()).evaluate_agent(agent, env, n_episodes=20, episode_length=None, seed=0) → EvaluationReport: cada episodio
empieza en una fecha aleatoria (con seed seed + i), por defecto con la mitad de la ventana de datos.
report.episodes (DataFrame, una fila por episodio), report.summary (media, mediana, desviación, mínimo y
máximo de cada métrica; mejor/peor episodio; % de episodios positivos y % que baten a buy & hold),
print(report) y report.plot(path).compare_agents([a, b, c], env, n_episodes, episode_length, seed) → tabla con un agente por fila, evaluados
sobre exactamente los mismos episodios.benchmark_return_pct: buy & hold equiponderado de los símbolos del episodio, sin costes.
Es la vara de medir honesta: un agente que no lo supera después de costes no aporta nada.get_metrics())Capital final, retorno total, CAGR, max drawdown y su duración, volatilidad, Sharpe, Sortino, Calmar, VaR/CVaR,
número de trades, win rate, profit factor, average trade PnL, mejor y peor trade, payoff, comisiones, slippage,
financiación, turnover y tiempo en mercado. Las métricas de equity salen de la equity curve (un punto por barra);
las de trades salen de los round trips cerrados (get_trade_history("closed")), netos de su parte de comisiones.
Ojo: una posición perdedora que sigue abierta no cuenta para el win rate (sí para equity, drawdown y Sharpe); por eso
conviene mirar ambas cosas. get_trade_history("fills") devuelve cada ejecución (también los fills parciales).
from tradesim.env import PaperTradingEnv
env = PaperTradingEnv(["SPY", "QQQ", "TLT", "GLD", "BTC-USD"], "2018-01-01", "2022-12-31", provider=provider,
episode_length=252, window=30, action_mode="weights", reward="log_return",
drawdown_penalty=0.5)
obs, info = env.reset(seed=0)
obs, reward, terminated, truncated, info = env.step(env.action_space.sample())
"weights" = pesos objetivo por activo (Box[0,1], o [-1,1] con cortos), ejecutados con órdenes
de mercado y una banda de no-operación; "discrete" = MultiDiscrete hold/buy/sell una fracción del equity. En
cualquier modo se acepta {"orders": [{...}, ...]} con los argumentos de place_order.log_return · pnl · sharpe (Sharpe diferencial) − drawdown_penalty × aumento del drawdown. Los
costes no se añaden aparte: ya están en el equity (spread, impacto, comisiones), como en real.env.reset(seed=s) reproduce exactamente el mismo episodio.stable-baselines3: PPO("MlpPolicy", env).learn(...) (ver examples/rl_agent.py).Buenas prácticas para transferir a real: separa train/validación/test por tiempo; entrena también con
scenario="high_volatility" | "crash" | "gaps" | "illiquid" y slippage_noise_bps > 0 para robustez; compara siempre
contra buy & hold después de costes; valida en LivePaperTrader antes de arriesgar dinero.
tradesim.agents.llm_tools.TOOLS define 10 herramientas (get_available_assets, get_price, get_portfolio,
get_historical_data, place_order, cancel_order, get_open_orders, close_position, get_trades,
get_metrics) en formato de la Messages API de Anthropic (openai_tools() las convierte al formato
{"type": "function"}). dispatch(sim, name, args) las ejecuta y devuelve JSON; los errores vuelven como
{"error": ...} para que el modelo pueda corregirse. Funcionan igual sobre TradingSimulator y LivePaperTrader, así
que el mismo prompt pasa de backtest a paper trading en vivo sin cambios. Ver examples/llm_agent.py
(ANTHROPIC_API_KEY o ant auth login).
from tradesim.live import LivePaperTrader
trader = LivePaperTrader(CCXTProvider("kraken"), config=SimulatorConfig(initial_cash=10_000, fees="kraken"))
trader.place_order("BTC-USD", "buy", notional=500)
trader.sync() # ejecuta contra la cotización y el libro L2 actuales, valora y registra equity
trader.run(agent_fn, interval_seconds=60)
trader.save_state("runs/live/state.json") # y LivePaperTrader.load_state(...) para continuar
SimulatorConfig(scenario=...) con presets normal, high_volatility, crash, bull, gaps, flash_crash,
illiquid, o un Scenario propio:
from tradesim.market.conditions import Scenario, VolatilityScale, GapInjection, LiquidityDrought, FlashCrash, Trend
scenario = Scenario("stress", [VolatilityScale(2.5), GapInjection(prob=0.03, size=0.07), LiquidityDrought(0.4, 2.0)])
Se aplican a datos reales o sintéticos, solo dentro de la ventana del episodio, conservando la coherencia OHLC, y ensanchan spreads / reducen profundidad donde corresponde. Son deterministas dada la seed.
sim.save_state(path) / TradingSimulator.load_state(path, provider): cartera, órdenes, contadores de IDs, estado
del RNG y posición del reloj; continuar tras cargar da resultados idénticos (verificado en tests).log_dir=...: events.jsonl (cada orden enviada/rechazada/cancelada/expirada, fill, margin call, reset) y
trades.sqlite (tablas orders, fills, closed_trades, equity).sim.get_metrics(): retorno total, CAGR, volatilidad, Sharpe, Sortino, Calmar, max drawdown y su duración, VaR/CVaR
95 %, skew, número de trades, win rate, profit factor, payoff, expectancy, mayor ganancia/pérdida, tiempo medio en
posición, tiempo en mercado, turnover anual, comisiones, coste de slippage y financiación. Se anualiza según el
timeframe (252 días × 6,5 h para acciones, 365 × 24 h para cripto).
impact_coefficient).pytest -q
Cubren la interfaz de agentes, run_episode / evaluate_agent / compare_agents (reproducibilidad, inicio
aleatorio, entorno RL), métricas contra el historial de trades, gráficas, contabilidad (precio medio, PnL realizado, giros largo→corto, precisión fraccionaria), cada tipo de orden,
gaps, capacidad/fills parciales, IOC/FOK, impacto creciente con el tamaño, barrido de libro L2, comisiones, ausencia de
look-ahead, rechazos, reservas de cash, expiración DAY, margen y cortos, reproducibilidad, guardar/cargar,
escenarios, caché y el entorno Gymnasium (check_env).
agents/llm_tools.py | Definiciones de tools + dispatch() para agentes LLM |
persistence.py | Log de eventos JSONL, SQLite de órdenes/fills/equity, snapshots JSON atómicos |