Deployment

Cómo ejecutar tus cBots de cTrader 24/5 en tu propio VPS con Docker

cTrader Cloud bloquea el acceso a internet, así que tu cBot no puede enviarte avisos por Telegram. Aquí está el montaje exacto que usamos en producción para tener cinco cBots corriendo de forma continua en un VPS.

Si has comprado un cBot y lo ejecutas en tu ordenador, dejas de operar cada vez que cierras el portátil, se va la luz o Windows decide actualizarse. Este artículo explica cómo montar un servidor que lo ejecute de forma continua, aislado, vigilado y reproducible — el mismo esquema con el que mantenemos cinco cBots en marcha 24/5 alimentando las estadísticas en vivo de nuestras fichas.

No es teoría: cada decisión de aquí viene de algo que nos costó un incidente.


0. ¿Por qué no cTrader Cloud, que es gratis?

Es la primera pregunta y merece respuesta directa. cTrader Cloud funciona bien para muchos casos, pero tiene una limitación que quizá te importe:

Los cBots ejecutados en la nube de cTrader no tienen acceso a internet.

Eso significa que cualquier notificación externa —Telegram, webhooks, tu propio servidor— no funciona. En nuestros logs se ve así:

📣 avisos activos → chat 123456789 · presupuesto 200/día
⚠ aviso NO entregado: HTTP 503

La configuración es correcta; lo que falla es la salida de red. Notifications.SendEmail() tampoco funciona allí. Si te basta con mirar la plataforma de vez en cuando, Cloud es perfecto y te ahorras esta guía. Si quieres que el bot te avise al móvil cuando abre, cierra o salta un guard de riesgo, necesitas ejecutarlo tú.

Las tres vías con acceso a red son: tu ordenador, cTrader CLI en tu propio servidor (lo que explica esta guía) y una VPS con cTrader de escritorio por escritorio remoto (más caro y frágil).


1. Qué necesitas

mínimorecomendadopor qué
RAM2 GB4 GBcada motor .NET come 300-600 MB. Con 2 GB corres uno; con 4, tres o cuatro
CPU1 core2 coreslos cBots son event-driven: casi 0% en reposo, picos cortos al cierre de vela
Disco20 GB40 GBla imagen Docker pesa ~1 GB; el resto son logs
SOUbuntu 22.04Ubuntu 22.04/24.04 LTSes donde la CLI está probada
Ubicacióncerca del brókerLondres o Nueva York según tu bróker; ahorra latencia

Cuesta entre 5 y 15 €/mes en cualquier proveedor (Hetzner, Contabo, DigitalOcean, Hostinger…).

No necesitas datos históricos. A diferencia de un backtest, el motor en vivo pide las velas de calentamiento al bróker al conectar. Un paso menos.


2. Instalar Docker

ssh root@TU_IP

apt update && apt upgrade -y
apt install -y ca-certificates curl gnupg

install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" \
  > /etc/apt/sources.list.d/docker.list

apt update && apt install -y docker-ce docker-ce-cli containerd.io
docker run --rm hello-world     # debe imprimir "Hello from Docker!"

3. La imagen: ctrader-console

cTrader publica una CLI oficial que ejecuta cBots sin interfaz gráfica. Es la pieza clave.

docker pull spotware/ctrader-console:latest

⛔ Y aquí va el consejo más importante de toda la guía: fija la versión

# 1) mira qué versión te has bajado
docker images spotware/ctrader-console

# 2) etiquétala como tuya y NO vuelvas a hacer pull sobre ella
docker tag spotware/ctrader-console:latest ctrader-console:certified

# 3) guarda una copia en disco, por si esa versión desaparece del registro
docker save ctrader-console:certified | gzip > /root/ctrader-console-backup.tar.gz

Por qué. A nosotros nos pasó esto: la versión 5.8.3 empezó a rechazar las modificaciones de stop loss (InvalidStopLossTakeProfit) — 1.090 rechazos de 2.676 intentos, con exactamente la misma entrada que en 5.7.10 daba 0 de 3.780. Y la 5.9 directamente hace caer la instancia de forma determinista. Nosotros seguimos pineados a 5.7.10, y esa versión ya no está en el registro público: si no la hubiéramos guardado, la habríamos perdido.

Un docker pull automático puede cambiarte el motor bajo un bot que está operando con tu dinero. Usa :certified, nunca :latest.


4. Estructura de ficheros

mkdir -p /opt/cbots/{mibot,logs}
cd /opt/cbots

Necesitas tres cosas por cada cBot:

/opt/cbots/mibot/
├── bot.algo           ← el fichero que compraste/compilaste
├── params.cbotset     ← preset de parámetros (opcional)
└── ctrader-cli.pwd    ← la contraseña de tu cTID, permisos 600
# el fichero de contraseña, con permisos restrictivos desde el primer momento
install -m600 /dev/stdin /opt/cbots/mibot/ctrader-cli.pwd <<< 'TU_PASSWORD_CTID'

⚠️ Una sola copia de la contraseña. Nosotros llegamos a tener el mismo password duplicado en cinco directorios distintos. Cada copia es un sitio más donde puede filtrarse y uno más que actualizar cuando cambie. Si tienes varios bots, deja el fichero en un único sitio y móntalo en todos los contenedores en modo lectura.


5. La configuración de cada bot

/opt/cbots/mibot/run.env:

# ── Cuenta ────────────────────────────────────────────────
[email protected]
BROKER=Skilling            # el nombre EXACTO del bróker en cTrader
ACCOUNT=1234567            # número de cuenta

# ── Gráfico anfitrión ─────────────────────────────────────
SYMBOL=GOLD                # ⚠️ LEE EL AVISO DE ABAJO
PERIOD=m1

# ── Recursos ──────────────────────────────────────────────
MEMORY=1g
CPUSET=1

⚠️ El SYMBOL es el nombre LITERAL de tu bróker, no el que use el cBot

Este es el error que más veces mata un despliegue. El gráfico anfitrión lo resuelve la plataforma, no el cBot: si pones un símbolo que tu bróker no tiene, el contenedor no arranca — y con reinicio automático, entras en un bucle infinito.

En Skilling el oro se llama GOLD, no XAUUSD. El DAX es Germany 40, con espacio. En IC Markets, DE40 y el petróleo XTIUSD; en Skilling, OIL WTI. Míralo en el buscador de símbolos de tu plataforma y cópialo tal cual.

(Nosotros tuvimos cuatro de cinco configuraciones apuntando a XAUUSD en un bróker que no lo tiene. Se descubrió en la revisión previa, antes de arrancar. De ahí el paso 6.)

El PERIOD debe ser el del backtest certificado

Si el resultado que compraste se midió en m1, pon m1. Cambiarlo altera el ritmo al que el bot evalúa y deja de reproducir la curva publicada.


6. Comprobación previa: valida antes de arrancar

La CLI trae dos comandos que cuestan segundos y ahorran horas:

docker run --rm -v /opt/cbots/mibot:/mnt ctrader-console:certified \
  accounts --ctid [email protected] --password-file /mnt/ctrader-cli.pwd

docker run --rm -v /opt/cbots/mibot:/mnt ctrader-console:certified \
  symbols --ctid [email protected] --password-file /mnt/ctrader-cli.pwd \
          --broker Skilling --account 1234567

El primero confirma que tus credenciales funcionan; el segundo te da la lista exacta de símbolos de tu bróker, que es de donde debes copiar el SYMBOL y con la que comprobar que los mercados de tu cBot existen ahí.

Hazlo siempre antes de arrancar nada. Un bot que arranca contra una cuenta mal configurada no falla con un error claro: se queda en bucle.


7. El lanzador

/opt/cbots/run_cbot.sh:

#!/usr/bin/env bash
set -euo pipefail
BOT="$1"
DIR="/opt/cbots/$BOT"
set -a; source "$DIR/run.env"; set +a

docker rm -f "cbot_$BOT" 2>/dev/null || true

exec docker run --rm --name "cbot_$BOT" \
  -v "$DIR:/mnt/Robots:ro" \
  --memory="${MEMORY:-1g}" --memory-swap="${MEMORY:-1g}" \
  --cpuset-cpus="${CPUSET:-0}" --cpu-shares=128 \
  --log-driver=json-file --log-opt max-size=50m --log-opt max-file=3 \
  ctrader-console:certified \
  run /mnt/Robots/bot.algo \
      --ctid "$CTID" --password-file /mnt/Robots/ctrader-cli.pwd \
      --broker "$BROKER" --account "$ACCOUNT" \
      --symbol "$SYMBOL" --period "$PERIOD" \
      --exit-on-stop
chmod +x /opt/cbots/run_cbot.sh

Detalles que importan:

  • --memory con --memory-swap igual — sin esto, un contenedor con fuga de memoria se come el swap y tumba el servidor entero. Empieza generoso (1 GB) y apriétalo después a (uso medido × 1,6).
  • --cpu-shares=128 (el valor por defecto es 1024) — peso bajo, para que si algo más compite por CPU, el bot ceda en vez de ahogar la máquina.
  • max-size=50m — sin límite de log, Docker te llena el disco en semanas.
  • :ro — el contenedor no necesita escribir en tus ficheros.

8. Prueba piloto: mide, no estimes

Antes de convertirlo en servicio, arráncalo a mano:

free -h                                  # memoria ANTES
bash /opt/cbots/run_cbot.sh mibot        # y déjalo correr

En otra sesión SSH:

docker stats --no-stream cbot_mibot      # RAM y CPU reales
free -h                                  # cuánto ha consumido de verdad

Qué debes ver para dar el piloto por bueno:

  • Conecta sin error de autenticación.
  • Todos los mercados del cBot resuelven. Busca en el log las líneas de resolución de símbolos; si alguna dice que no encuentra un símbolo, esa estrategia queda desactivada y no operará — y el bot seguirá funcionando con las demás, produciendo un resultado que no es el que compraste.
  • RAM estable entre 300 y 600 MB.
  • CPU cercano a 0 en reposo.

⚠️ Cero operaciones en las primeras horas NO es un fallo. Un cBot de cartera con marcos de 1h a 8h puede hacer menos de dos operaciones al día. Para saber si está vivo, mira el consumo del contenedor, no el log.


9. Convertirlo en servicio con systemd

/etc/systemd/system/[email protected]:

[Unit]
Description=cTrader cBot %i
After=docker.service
Requires=docker.service

[Service]
Type=simple
ExecStart=/opt/cbots/run_cbot.sh %i
ExecStop=/usr/bin/docker stop -t 30 cbot_%i
TimeoutStopSec=45
Restart=always
RestartSec=30
StartLimitBurst=5
StartLimitIntervalSec=600

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now cbot@mibot
journalctl -u cbot@mibot -f

El StartLimitBurst=5 no es decorativo. Con Restart=always a secas, una cuenta mal configurada reintentaría eternamente en silencio. Con el límite, tras 5 intentos en 10 minutos el servicio pasa a failed — y un servicio en failed sí se puede vigilar.

Un solo fichero de servicio sirve para todos tus bots: cbot@gold, cbot@nasdaq… Toda la diferencia vive en cada run.env, así que no tienes cinco ficheros que se desincronicen.


10. Vigilancia: lo que de verdad te va a salvar

Aquí va la lección más cara que aprendimos. Teníamos un bot con OnFailure configurado para avisarnos si el servicio caía. Estuvo cinco semanas sin operar y no avisó, porque el servicio nunca cayó: su token había caducado, así que arrancaba, fallaba al autenticar, reintentaba, y systemd lo veía "activo".

Un servicio "activo" no significa un bot que opera. Y un monitor callado se lee como salud.

Vigila dos cosas distintas:

a) Que el proceso esté vivo — por consumo, no por logs

#!/usr/bin/env bash
# /opt/cbots/watchdog.sh — cada 20 minutos por cron
for BOT in mibot; do
  if ! docker ps --format '{{.Names}}' | grep -q "^cbot_$BOT$"; then
    curl -s "https://api.telegram.org/bot$TG_TOKEN/sendMessage" \
      -d chat_id="$TG_CHAT" -d text="⚠️ cbot_$BOT NO está corriendo"
    continue
  fi
  RX=$(docker stats --no-stream --format '{{.NetIO}}' "cbot_$BOT")
  echo "$(date -Is) $BOT $RX" >> /opt/cbots/logs/heartbeat.log
done

Compara los bytes recibidos entre lecturas: si no crecen, el bot no está hablando con el bróker aunque el contenedor exista. Contar líneas de log no sirve — un bot sano puede pasar horas sin escribir nada.

b) Que la cuenta se mueva — desde fuera

Lo ideal es un segundo proceso que consulte tu cuenta por la API del bróker (solo lectura) y te mande un parte diario: saldo, posiciones abiertas, operaciones del día. Así detectas el caso peligroso: el bot corre, pero no opera.


11. Mantenimiento

# ver qué hace ahora mismo
journalctl -u cbot@mibot -f

# reiniciarlo (cierra limpio, con 30s de margen)
systemctl restart cbot@mibot

# pararlo del todo
systemctl stop cbot@mibot

# ¿cuánto consume?
docker stats --no-stream

# actualizar el .algo a una versión nueva del cBot
systemctl stop cbot@mibot
cp nuevo.algo /opt/cbots/mibot/bot.algo
systemctl start cbot@mibot

Al actualizar el cBot

  1. Guarda el .algo anterior antes de sobrescribirlo. Volver atrás debe costar un cp.
  2. Comprueba el log de arranque tras actualizar: que resuelven todos los símbolos y que el preset es el que esperas.
  3. No actualices la imagen de cTrader al mismo tiempo. Si algo se rompe, no sabrás cuál de los dos cambios fue.

12. Errores que vas a cometer (nosotros los cometimos)

errorqué verásarreglo
SYMBOL que el bróker no tieneel contenedor arranca y muere en buclecópialo de symbols (§6)
:latest en vez de versión fijaun día el bot deja de modificar stops, o cae:certified (§3)
Restart=always sin StartLimitBurstreintentos infinitos en silencio§9
Sin --memoryuna fuga tumba el servidor entero§7
Sin max-size en los logsdisco lleno en semanas§7
Vigilar por logsfalsos positivos y negativosvigila consumo (§10)
Fiarte de systemctl is-active5 semanas sin operar y "activo"vigila la cuenta (§10b)
Esperar Telegram desde cTrader CloudHTTP 503no es posible: usa tu VPS (§0)
PERIOD distinto al certificadocurva distinta a la publicada§5

13. ¿Y si tengo varios cBots?

Un contenedor por cBot, siempre. Es tentador meter varios en uno, pero:

  • Si uno falla, caen todos.
  • No puedes reiniciar uno sin tocar los demás.
  • No puedes limitar la memoria por bot.
  • Los logs se mezclan y dejan de ser diagnosticables.

Con un contenedor por bot, systemctl restart cbot@gold no molesta a cbot@nasdaq. Nosotros corremos cinco así, en unos 860 MB en total.

Reparte los cores con CPUSET en cada run.env y arranca de uno en uno, dejando el primero 24 horas estable antes de añadir el segundo.


Resumen

# 1. Docker                          apt install docker-ce
# 2. Imagen FIJADA                   docker tag …:latest ctrader-console:certified
# 3. Ficheros                        bot.algo + run.env + ctrader-cli.pwd (600)
# 4. Comprobar ANTES                 accounts + symbols
# 5. Piloto en primer plano          medir RAM/CPU reales
# 6. systemd                         cbot@mibot con StartLimitBurst
# 7. Vigilar DOS cosas               proceso vivo + cuenta que se mueve

Con esto tienes tus cBots operando de forma continua, aislados unos de otros, con la versión del motor congelada, con límites de recursos y con avisos que llegan a tu móvil — que es justo lo que cTrader Cloud no puede darte.


Guía basada en el montaje que mantenemos en producción para las estadísticas en vivo de nuestros productos: cinco cBots, cinco cuentas, funcionando 24/5 desde julio de 2026.

Publicado Aug 19, 2026 · realbacktesting · Contenido educativo y comentario de mercado — no es asesoramiento financiero. El trading conlleva riesgo; los resultados pasados no garantizan resultados futuros.