Compare commits

..
Author SHA1 Message Date
chemavx 6cf4615596 decommission(F3): apagar postgres (replicas 0)
El dump definitivo de cierre está tomado y verificado (restore 11/11 en
/data/backups/backups/polymarket-decommission/). El PVC se conserva.
2026-07-06 12:19:33 +00:00
chemavx dd10f26f31 decommission(F3): suspender cronjobs metrics-retention y outcomes-joiner
Sin bot ni postgres ya no hay nada que retener ni joinear; suspend en vez
de borrar para conservar la definición durante la semana de gracia.
2026-07-06 12:19:33 +00:00
chemavx 0f8bd407f4 decommission(F3): apagar bot, api y dashboard (replicas 0)
Primer paso del apagado ordenado: se detiene el ciclo de evaluación con
postgres aún vivo, para que el dump final sea la foto exacta del cierre
sin ciclos a medias. Postgres y cronjobs se apagan en commits siguientes.
2026-07-06 12:05:43 +00:00
chemavx dc45c913a5 decommission(F2): silenciar notificaciones Telegram de la app polymarket-bot
Se retiran las suscripciones on-sync-failed / on-health-degraded: durante
el apagado (F3) generarían ruido sin valor. No hay trigger 'informativo'
viable: escalar a 0 vía git deja la app Synced/Healthy. El resto de apps
conservan sus notificaciones.
2026-07-06 11:47:33 +00:00
chemavx abcb38bd6d decommission(F2): retirar smoke test PostSync de polymarket-bot
El Job notificaba a Telegram tras cada sync; en decomisión ya no hay
deploys que verificar y sus avisos serían ruido durante el apagado.
2026-07-06 11:47:11 +00:00
Gitea CI 73bf1b6351 ci: update researchowl image to d31badeb [skip ci] 2026-07-05 16:01:12 +00:00
Gitea CI fea711375e ci: update researchowl image to 7d07375d [skip ci] 2026-07-04 20:26:34 +00:00
Gitea CI 8cd40002c8 ci: update researchowl image to 76c927f0 [skip ci] 2026-07-04 20:20:00 +00:00
Gitea CI b1b86945c3 ci: update researchowl image to 397546a3 [skip ci] 2026-07-04 20:18:47 +00:00
Gitea CI d0e8eaa001 ci: update researchowl image to 57f341fc [skip ci] 2026-07-04 11:26:10 +00:00
Gitea CI a78de3eacd ci: update researchowl image to 6c807212 [skip ci] 2026-07-04 11:21:02 +00:00
Gitea CI 5ef73e6705 ci: update researchowl image to becc43f7 [skip ci] 2026-07-04 11:16:57 +00:00
10 changed files with 149 additions and 346 deletions
-3
View File
@@ -3,9 +3,6 @@ kind: Application
metadata: metadata:
name: polymarket-bot name: polymarket-bot
namespace: argocd namespace: argocd
annotations:
notifications.argoproj.io/subscribe.on-sync-failed.telegram: "5138407666"
notifications.argoproj.io/subscribe.on-health-degraded.telegram: "5138407666"
spec: spec:
destination: destination:
namespace: polymarket-bot namespace: polymarket-bot
@@ -11,6 +11,7 @@ spec:
# days are dead weight. Today's rows are never touched: the latest one # days are dead weight. Today's rows are never touched: the latest one
# feeds /api/summary and the close is still evolving. # feeds /api/summary and the close is still evolving.
schedule: "10 0 * * *" schedule: "10 0 * * *"
suspend: true
concurrencyPolicy: Forbid concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3 successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3 failedJobsHistoryLimit: 3
+1
View File
@@ -13,6 +13,7 @@ spec:
# the upsert is idempotent per market_id (ON CONFLICT DO UPDATE) and the # the upsert is idempotent per market_id (ON CONFLICT DO UPDATE) and the
# fetch only queries markets that still lack an outcome. # fetch only queries markets that still lack an outcome.
schedule: "30 0 * * *" schedule: "30 0 * * *"
suspend: true
concurrencyPolicy: Forbid concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3 successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3 failedJobsHistoryLimit: 3
+1 -1
View File
@@ -5,7 +5,7 @@ metadata:
namespace: polymarket-bot namespace: polymarket-bot
spec: spec:
progressDeadlineSeconds: 600 progressDeadlineSeconds: 600
replicas: 1 replicas: 0
revisionHistoryLimit: 10 revisionHistoryLimit: 10
selector: selector:
matchLabels: matchLabels:
+1 -1
View File
@@ -5,7 +5,7 @@ metadata:
namespace: polymarket-bot namespace: polymarket-bot
spec: spec:
progressDeadlineSeconds: 600 progressDeadlineSeconds: 600
replicas: 1 replicas: 0
revisionHistoryLimit: 10 revisionHistoryLimit: 10
selector: selector:
matchLabels: matchLabels:
+1 -1
View File
@@ -5,7 +5,7 @@ metadata:
namespace: polymarket-bot namespace: polymarket-bot
spec: spec:
progressDeadlineSeconds: 600 progressDeadlineSeconds: 600
replicas: 1 replicas: 0
revisionHistoryLimit: 10 revisionHistoryLimit: 10
selector: selector:
matchLabels: matchLabels:
-68
View File
@@ -1,68 +0,0 @@
apiVersion: batch/v1
kind: Job
metadata:
name: postsync-smoke-test
namespace: polymarket-bot
annotations:
argocd.argoproj.io/hook: PostSync
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
ttlSecondsAfterFinished: 600
backoffLimit: 0
template:
spec:
restartPolicy: Never
containers:
- name: smoke-test
image: curlimages/curl:latest
env:
- name: TELEGRAM_BOT_TOKEN
valueFrom:
secretKeyRef:
name: telegram-notify-infisical
key: TELEGRAM_BOT_TOKEN
- name: TELEGRAM_CHAT_ID
valueFrom:
secretKeyRef:
name: telegram-notify-infisical
key: TELEGRAM_CHAT_ID
command: ["/bin/sh", "-c"]
args:
- |
set -e
API="http://api.polymarket-bot.svc.cluster.local:8000"
DASH="http://dashboard.polymarket-bot.svc.cluster.local:80"
notify() {
curl -s --max-time 10 -X POST \
"https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d "chat_id=${TELEGRAM_CHAT_ID}" \
--data-urlencode "text=$1" \
> /dev/null || true
}
fail() {
notify "[FAIL] polymarket-bot PostSync: $1"
exit 1
}
echo "--- [1/3] GET /api/summary ---"
SUMMARY=$(curl -sf --max-time 15 --retry 3 --retry-delay 5 --retry-all-errors \
"$API/api/summary") || fail "GET /api/summary unreachable"
echo "$SUMMARY"
echo "$SUMMARY" | grep -qE '"paper_mode"\s*:\s*true' \
|| fail "paper_mode is not true"
echo "OK"
echo "--- [2/3] GET /api/trades?status=open ---"
curl -sf --max-time 15 --retry 3 --retry-delay 5 --retry-all-errors \
"$API/api/trades?status=open" > /dev/null || fail "GET /api/trades unreachable"
echo "OK"
echo "--- [3/3] Dashboard HTTP 200 ---"
curl -sf --max-time 15 --retry 3 --retry-delay 5 --retry-all-errors \
"$DASH/" > /dev/null || fail "dashboard returned non-200"
echo "OK"
notify "[OK] polymarket-bot PostSync passed"
echo "=== polymarket-bot: all smoke tests passed ==="
+1 -1
View File
@@ -8,7 +8,7 @@ spec:
whenDeleted: Retain whenDeleted: Retain
whenScaled: Retain whenScaled: Retain
podManagementPolicy: OrderedReady podManagementPolicy: OrderedReady
replicas: 1 replicas: 0
revisionHistoryLimit: 10 revisionHistoryLimit: 10
selector: selector:
matchLabels: matchLabels:
+142 -270
View File
@@ -1,275 +1,147 @@
apiVersion: v1 apiVersion: v1
data: data:
index.html: | index.html: "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\"
<!DOCTYPE html> />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"
<html lang="en"> />\n <title>ChemaVX</title>\n <style>\n *, *::before, *::after { box-sizing:
<head> border-box; margin: 0; padding: 0; }\n\n :root {\n --bg: #0d1117;\n
<meta charset="UTF-8" /> \ --surface: #161b22;\n --border: #30363d;\n --text: #e6edf3;\n
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> \ --muted: #8b949e;\n --accent: #58a6ff;\n --green: #3fb950;\n
<title>ChemaVX</title> \ --red: #f85149;\n --yellow: #d29922;\n --purple: #bc8cff;\n
<style> \ }\n\n body {\n background: var(--bg);\n color: var(--text);\n
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } \ font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica,
Arial, sans-serif;\n min-height: 100vh;\n display: flex;\n flex-direction:
:root { column;\n align-items: center;\n padding: 3rem 1.5rem 4rem;\n }\n\n
--bg: #0d1117; \ /* ── Header ─────────────────────────────────── */\n header {\n text-align:
--surface: #161b22; center;\n margin-bottom: 3.5rem;\n }\n .avatar {\n width: 80px;
--border: #30363d; height: 80px;\n border-radius: 50%;\n background: linear-gradient(135deg,
--text: #e6edf3; #1f6feb 0%, #bc8cff 100%);\n display: flex; align-items: center; justify-content:
--muted: #8b949e; center;\n font-size: 2rem; font-weight: 700; color: #fff;\n margin:
--accent: #58a6ff; 0 auto 1.25rem;\n box-shadow: 0 0 0 3px var(--border);\n }\n h1 { font-size:
--green: #3fb950; 2rem; font-weight: 700; letter-spacing: -0.5px; }\n .tagline {\n color:
--red: #f85149; var(--muted); font-size: 0.95rem; margin-top: 0.4rem;\n }\n .dot { display:
--yellow: #d29922; inline-block; width: 8px; height: 8px;\n border-radius: 50%; background:
--purple: #bc8cff; var(--green);\n margin-right: 6px; animation: pulse 2s infinite; }\n
} \ @keyframes pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity:
0.4; }\n }\n\n /* ── Section title ───────────────────────────── */\n .section-title
body { {\n font-size: 0.7rem; font-weight: 600; letter-spacing: 0.08em;\n text-transform:
background: var(--bg); uppercase; color: var(--muted);\n margin-bottom: 0.875rem;\n }\n\n /*
color: var(--text); ── Services grid ───────────────────────────── */\n .services-grid {\n display:
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; grid;\n grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));\n gap:
min-height: 100vh; 0.75rem;\n width: 100%; max-width: 720px;\n margin-bottom: 2.5rem;\n
display: flex; \ }\n .service-card {\n background: var(--surface);\n border: 1px
flex-direction: column; solid var(--border);\n border-radius: 10px;\n padding: 1rem 1.1rem;\n
align-items: center; \ text-decoration: none;\n color: var(--text);\n transition: border-color
padding: 3rem 1.5rem 4rem; 0.15s, transform 0.15s, box-shadow 0.15s;\n display: flex; align-items: center;
} gap: 0.65rem;\n }\n .service-card:hover {\n border-color: var(--accent);\n
\ transform: translateY(-2px);\n box-shadow: 0 4px 16px rgba(88,166,255,0.1);\n
/* ── Header ─────────────────────────────────── */ \ }\n .service-icon { font-size: 1.3rem; flex-shrink: 0; }\n .service-name
header { { font-size: 0.9rem; font-weight: 500; }\n\n /* ── Polymarket card ───────────────────────────
text-align: center; */\n .poly-wrapper { width: 100%; max-width: 720px; }\n .poly-card {\n background:
margin-bottom: 3.5rem; var(--surface);\n border: 1px solid var(--border);\n border-radius:
} 12px;\n padding: 1.5rem;\n margin-bottom: 2.5rem;\n }\n .poly-header
.avatar { {\n display: flex; align-items: center; justify-content: space-between;\n
width: 80px; height: 80px; \ margin-bottom: 1.25rem;\n }\n .poly-title {\n display: flex;
border-radius: 50%; align-items: center; gap: 0.5rem;\n font-size: 0.95rem; font-weight: 600;\n
background: linear-gradient(135deg, #1f6feb 0%, #bc8cff 100%); \ }\n .poly-badge {\n font-size: 0.65rem; font-weight: 600; letter-spacing:
display: flex; align-items: center; justify-content: center; 0.04em;\n padding: 2px 7px; border-radius: 20px;\n background: rgba(188,140,255,0.15);
font-size: 2rem; font-weight: 700; color: #fff; color: var(--purple);\n border: 1px solid rgba(188,140,255,0.3);\n }\n
margin: 0 auto 1.25rem; \ #poly-updated {\n font-size: 0.72rem; color: var(--muted);\n }\n\n
box-shadow: 0 0 0 3px var(--border); \ .metrics-grid {\n display: grid;\n grid-template-columns: repeat(2,
} 1fr);\n gap: 0.75rem;\n }\n @media (min-width: 480px) {\n .metrics-grid
h1 { font-size: 2rem; font-weight: 700; letter-spacing: -0.5px; } { grid-template-columns: repeat(4, 1fr); }\n }\n .metric {\n background:
.tagline { var(--bg);\n border: 1px solid var(--border);\n border-radius: 8px;\n
color: var(--muted); font-size: 0.95rem; margin-top: 0.4rem; \ padding: 0.875rem 1rem;\n }\n .metric-label {\n font-size: 0.68rem;
} color: var(--muted);\n text-transform: uppercase; letter-spacing: 0.06em;\n
.dot { display: inline-block; width: 8px; height: 8px; \ margin-bottom: 0.35rem;\n }\n .metric-value {\n font-size: 1.35rem;
border-radius: 50%; background: var(--green); font-weight: 700; font-variant-numeric: tabular-nums;\n transition: color
margin-right: 6px; animation: pulse 2s infinite; } 0.3s;\n }\n .metric-value.positive { color: var(--green); }\n .metric-value.negative
@keyframes pulse { { color: var(--red); }\n .metric-value.neutral { color: var(--text); }\n .metric-value.loading
0%, 100% { opacity: 1; } \ { color: var(--muted); font-size: 1rem; }\n\n /* ── Footer ────────────────────────────────────
50% { opacity: 0.4; } */\n footer {\n color: var(--muted); font-size: 0.75rem; text-align: center;\n
} \ width: 100%; max-width: 720px;\n border-top: 1px solid var(--border);\n
\ padding-top: 1.25rem;\n }\n footer a { color: var(--accent); text-decoration:
/* ── Section title ───────────────────────────── */ none; }\n footer a:hover { text-decoration: underline; }\n </style>\n</head>\n<body>\n\n
.section-title { \ <header>\n <div class=\"avatar\">C</div>\n <h1>ChemaVX</h1>\n <p class=\"tagline\">\n
font-size: 0.7rem; font-weight: 600; letter-spacing: 0.08em; \ <span class=\"dot\"></span>Homelab · k3s · Self-hosted\n </p>\n </header>\n\n
text-transform: uppercase; color: var(--muted); \ <!-- Services -->\n <div style=\"width:100%;max-width:720px;margin-bottom:2.5rem;\">\n
margin-bottom: 0.875rem; \ <p class=\"section-title\">Services</p>\n <div class=\"services-grid\">\n
} \ <a class=\"service-card\" href=\"https://grafana.chemavx.xyz\" target=\"_blank\"
rel=\"noopener\">\n <span class=\"service-icon\">\U0001F4CA</span>\n <span
/* ── Services grid ───────────────────────────── */ class=\"service-name\">Grafana</span>\n </a>\n <a class=\"service-card\"
.services-grid { href=\"https://n8n.chemavx.xyz\" target=\"_blank\" rel=\"noopener\">\n <span
display: grid; class=\"service-icon\">⚙️</span>\n <span class=\"service-name\">n8n</span>\n
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); \ </a>\n <a class=\"service-card\" href=\"https://home.chemavx.xyz\"
gap: 0.75rem; target=\"_blank\" rel=\"noopener\">\n <span class=\"service-icon\">\U0001F3E0</span>\n
width: 100%; max-width: 720px; \ <span class=\"service-name\">Homarr</span>\n </a>\n <a class=\"service-card\"
margin-bottom: 2.5rem; href=\"https://polymarket.chemavx.xyz\" target=\"_blank\" rel=\"noopener\">\n
} \ <span class=\"service-icon\">\U0001F4C8</span>\n <span class=\"service-name\">Polymarket
.service-card { Bot</span>\n </a>\n <a class=\"service-card\" href=\"https://chat.chemavx.xyz\"
background: var(--surface); target=\"_blank\" rel=\"noopener\">\n <span class=\"service-icon\">\U0001F916</span>\n
border: 1px solid var(--border); \ <span class=\"service-name\">Open WebUI</span>\n </a>\n <a class=\"service-card\"
border-radius: 10px; href=\"https://git.chemavx.xyz\" target=\"_blank\" rel=\"noopener\">\n <span
padding: 1rem 1.1rem; class=\"service-icon\">\U0001F419</span>\n <span class=\"service-name\">Gitea</span>\n
text-decoration: none; \ </a>\n <a class=\"service-card\" href=\"https://argocd.chemavx.xyz\"
color: var(--text); target=\"_blank\" rel=\"noopener\">\n <span class=\"service-icon\">\U0001F504</span>\n
transition: border-color 0.15s, transform 0.15s, box-shadow 0.15s; \ <span class=\"service-name\">ArgoCD</span>\n </a>\n <a class=\"service-card\"
display: flex; align-items: center; gap: 0.65rem; href=\"https://vaultwarden.chemavx.xyz\" target=\"_blank\" rel=\"noopener\">\n
} \ <span class=\"service-icon\">\U0001F510</span>\n <span class=\"service-name\">Vaultwarden</span>\n
.service-card:hover { \ </a>\n </div>\n </div>\n\n <!-- Projects -->\n <div style=\"width:100%;max-width:720px;margin-bottom:2.5rem;\">\n
border-color: var(--accent); \ <p class=\"section-title\">Projects</p>\n <div class=\"services-grid\">\n
transform: translateY(-2px); \ <a class=\"service-card\" href=\"/ze\">\n <span class=\"service-icon\">\U0001F6F8</span>\n
box-shadow: 0 4px 16px rgba(88,166,255,0.1); \ <span class=\"service-name\">ze</span>\n </a>\n </div>\n </div>\n\n
} \ <!-- Polymarket live metrics -->\n <div class=\"poly-wrapper\">\n <p class=\"section-title\">Polymarket
.service-icon { font-size: 1.3rem; flex-shrink: 0; } Bot — Live</p>\n <div class=\"poly-card\">\n <div class=\"poly-header\">\n
.service-name { font-size: 0.9rem; font-weight: 500; } \ <div class=\"poly-title\">\n <span>\U0001F4C8</span>\n <span>Portfolio
Summary</span>\n <span class=\"poly-badge\" id=\"poly-mode\">PAPER</span>\n
/* ── Polymarket card ─────────────────────────── */ \ </div>\n <span id=\"poly-updated\">Loading…</span>\n </div>\n
.poly-wrapper { width: 100%; max-width: 720px; } \ <div class=\"metrics-grid\">\n <div class=\"metric\">\n <div
.poly-card { class=\"metric-label\">P&amp;L</div>\n <div class=\"metric-value loading\"
background: var(--surface); id=\"m-pnl\">—</div>\n </div>\n <div class=\"metric\">\n <div
border: 1px solid var(--border); class=\"metric-label\">Win Rate</div>\n <div class=\"metric-value loading\"
border-radius: 12px; id=\"m-winrate\">—</div>\n </div>\n <div class=\"metric\">\n <div
padding: 1.5rem; class=\"metric-label\">Deployed</div>\n <div class=\"metric-value loading\"
margin-bottom: 2.5rem; id=\"m-deployed\">—</div>\n </div>\n <div class=\"metric\">\n <div
} class=\"metric-label\">Trades</div>\n <div class=\"metric-value loading\"
.poly-header { id=\"m-trades\">—</div>\n </div>\n <div class=\"metric\">\n <div
display: flex; align-items: center; justify-content: space-between; class=\"metric-label\">Bankroll</div>\n <div class=\"metric-value loading\"
margin-bottom: 1.25rem; id=\"m-bankroll\">—</div>\n </div>\n <div class=\"metric\">\n <div
} class=\"metric-label\">Sharpe</div>\n <div class=\"metric-value loading\"
.poly-title { id=\"m-sharpe\">—</div>\n </div>\n <div class=\"metric\">\n <div
display: flex; align-items: center; gap: 0.5rem; class=\"metric-label\">Calibration</div>\n <div class=\"metric-value
font-size: 0.95rem; font-weight: 600; loading\" id=\"m-calibration\">—</div>\n </div>\n <div class=\"metric\">\n
} \ <div class=\"metric-label\">Status</div>\n <div class=\"metric-value
.poly-badge { loading\" id=\"m-status\">—</div>\n </div>\n </div>\n </div>\n
font-size: 0.65rem; font-weight: 600; letter-spacing: 0.04em; \ </div>\n\n <footer>\n <p>\n Hosted on <strong>k3s</strong> &mdash;\n
padding: 2px 7px; border-radius: 20px; \ <a href=\"https://grafana.chemavx.xyz/d/chemavx-homelab-v1/chemavx-homelab-overview\"
background: rgba(188,140,255,0.15); color: var(--purple); target=\"_blank\">Cluster Dashboard</a>\n &mdash; <a href=\"https://git.chemavx.xyz\"
border: 1px solid rgba(188,140,255,0.3); target=\"_blank\">Gitea</a>\n </p>\n </footer>\n\n <script>\n const API
} = 'https://polymarket.chemavx.xyz/api/summary';\n const fmt$ = v => v == null
#poly-updated { ? '—' : '$' + v.toFixed(2);\n const fmtPct = v => v == null ? '—' : (v * 100).toFixed(1)
font-size: 0.72rem; color: var(--muted); + '%';\n const fmtN = v => v == null ? '—' : String(v);\n const fmtF =
} (v, d=3) => v == null ? '—' : v.toFixed(d);\n\n function colorClass(id, val)
{\n const el = document.getElementById(id);\n el.classList.remove('positive','negative','neutral','loading');\n
.metrics-grid { \ if (val > 0) el.classList.add('positive');\n else if (val < 0) el.classList.add('negative');\n
display: grid; \ else el.classList.add('neutral');\n }\n\n async function fetchMetrics()
grid-template-columns: repeat(2, 1fr); {\n try {\n const r = await fetch(API, { cache: 'no-store' });\n if
gap: 0.75rem; (!r.ok) throw new Error(r.status);\n const d = await r.json();\n\n document.getElementById('m-pnl').textContent
} \ = fmt$(d.total_pnl);\n document.getElementById('m-winrate').textContent
@media (min-width: 480px) { \ = fmtPct(d.win_rate);\n document.getElementById('m-deployed').textContent
.metrics-grid { grid-template-columns: repeat(4, 1fr); } \ = fmt$(d.total_deployed);\n document.getElementById('m-trades').textContent
} \ = fmtN(d.total_trades);\n document.getElementById('m-bankroll').textContent
.metric { \ = fmt$(d.paper_bankroll);\n document.getElementById('m-sharpe').textContent
background: var(--bg); \ = fmtF(d.sharpe_ratio, 2);\n document.getElementById('m-calibration').textContent
border: 1px solid var(--border); = fmtF(d.calibration_score, 3);\n document.getElementById('m-status').textContent
border-radius: 8px; \ = d.promotion_ready ? '\U0001F680 Ready' : '⏳ Training';\n\n colorClass('m-pnl',
padding: 0.875rem 1rem; d.total_pnl);\n colorClass('m-sharpe', d.sharpe_ratio);\n\n const
} mode = d.paper_mode ? 'PAPER' : 'LIVE';\n const badge = document.getElementById('poly-mode');\n
.metric-label { \ badge.textContent = mode;\n badge.style.background = d.paper_mode\n
font-size: 0.68rem; color: var(--muted); \ ? 'rgba(188,140,255,0.15)' : 'rgba(63,185,80,0.15)';\n badge.style.color
text-transform: uppercase; letter-spacing: 0.06em; = d.paper_mode ? 'var(--purple)' : 'var(--green)';\n badge.style.borderColor
margin-bottom: 0.35rem; = d.paper_mode\n ? 'rgba(188,140,255,0.3)' : 'rgba(63,185,80,0.3)';\n\n
} \ const now = new Date();\n document.getElementById('poly-updated').textContent
.metric-value { =\n 'Updated ' + now.toLocaleTimeString();\n } catch (e) {\n document.getElementById('poly-updated').textContent
font-size: 1.35rem; font-weight: 700; font-variant-numeric: tabular-nums; = 'Error: ' + e.message;\n }\n }\n\n fetchMetrics();\n setInterval(fetchMetrics,
transition: color 0.3s; 30000);\n </script>\n</body>\n</html>\n"
}
.metric-value.positive { color: var(--green); }
.metric-value.negative { color: var(--red); }
.metric-value.neutral { color: var(--text); }
.metric-value.loading { color: var(--muted); font-size: 1rem; }
/* ── Footer ──────────────────────────────────── */
footer {
color: var(--muted); font-size: 0.75rem; text-align: center;
width: 100%; max-width: 720px;
border-top: 1px solid var(--border);
padding-top: 1.25rem;
}
footer a { color: var(--accent); text-decoration: none; }
footer a:hover { text-decoration: underline; }
</style>
</head>
<body>
<header>
<div class="avatar">C</div>
<h1>ChemaVX</h1>
<p class="tagline">
<span class="dot"></span>Homelab · k3s · Self-hosted
</p>
</header>
<!-- Services -->
<div style="width:100%;max-width:720px;margin-bottom:2.5rem;">
<p class="section-title">Services</p>
<div class="services-grid">
<a class="service-card" href="https://grafana.chemavx.xyz" target="_blank" rel="noopener">
<span class="service-icon">📊</span>
<span class="service-name">Grafana</span>
</a>
<a class="service-card" href="https://n8n.chemavx.xyz" target="_blank" rel="noopener">
<span class="service-icon">⚙️</span>
<span class="service-name">n8n</span>
</a>
<a class="service-card" href="https://home.chemavx.xyz" target="_blank" rel="noopener">
<span class="service-icon">🏠</span>
<span class="service-name">Homarr</span>
</a>
<a class="service-card" href="https://git.chemavx.xyz/chemavx/polymarket-bot" target="_blank" rel="noopener">
<span class="service-icon">📈</span>
<span class="service-name">Polymarket Bot (archived)</span>
</a>
<a class="service-card" href="https://chat.chemavx.xyz" target="_blank" rel="noopener">
<span class="service-icon">🤖</span>
<span class="service-name">Open WebUI</span>
</a>
<a class="service-card" href="https://git.chemavx.xyz" target="_blank" rel="noopener">
<span class="service-icon">🐙</span>
<span class="service-name">Gitea</span>
</a>
<a class="service-card" href="https://argocd.chemavx.xyz" target="_blank" rel="noopener">
<span class="service-icon">🔄</span>
<span class="service-name">ArgoCD</span>
</a>
<a class="service-card" href="https://vaultwarden.chemavx.xyz" target="_blank" rel="noopener">
<span class="service-icon">🔐</span>
<span class="service-name">Vaultwarden</span>
</a>
</div>
</div>
<!-- Projects -->
<div style="width:100%;max-width:720px;margin-bottom:2.5rem;">
<p class="section-title">Projects</p>
<div class="services-grid">
<a class="service-card" href="/ze">
<span class="service-icon">🛸</span>
<span class="service-name">ze</span>
</a>
</div>
</div>
<!-- Polymarket bot: archived case study (final figures, 2026-07-06) -->
<div class="poly-wrapper">
<p class="section-title">Polymarket Bot — Archived · Case study</p>
<div class="poly-card">
<div class="poly-header">
<div class="poly-title">
<span>📈</span>
<span>Paper Trading — Final Results</span>
<span class="poly-badge">ARCHIVED</span>
</div>
<span id="poly-updated">Final snapshot · 2026-07-06</span>
</div>
<div class="metrics-grid">
<div class="metric">
<div class="metric-label">Realized P&amp;L</div>
<div class="metric-value positive">+$247.78</div>
</div>
<div class="metric">
<div class="metric-label">Paper Trades</div>
<div class="metric-value neutral">12</div>
</div>
<div class="metric">
<div class="metric-label">Evaluations</div>
<div class="metric-value neutral">239k</div>
</div>
<div class="metric">
<div class="metric-label">Days Observed</div>
<div class="metric-value neutral">81</div>
</div>
</div>
<p style="font-size:0.8rem;color:var(--muted);margin-top:1rem;line-height:1.5;">
Bot bayesiano de paper trading sobre mercados de predicción, retirado en
julio de 2026 tras el bloqueo regulatorio de Polymarket en España.
Motor de replay determinista, ~239k evaluaciones archivadas y
observabilidad completa en k3s.
<a href="https://git.chemavx.xyz/chemavx/polymarket-bot" target="_blank" rel="noopener"
style="color:var(--accent);text-decoration:none;">Case study completo →</a>
</p>
</div>
</div>
<footer>
<p>
Hosted on <strong>k3s</strong> &mdash;
<a href="https://grafana.chemavx.xyz/d/chemavx-homelab-v1/chemavx-homelab-overview" target="_blank">Cluster Dashboard</a>
&mdash; <a href="https://git.chemavx.xyz" target="_blank">Gitea</a>
</p>
</footer>
</body>
</html>
kind: ConfigMap kind: ConfigMap
metadata: metadata:
name: portfolio-html name: portfolio-html
+1 -1
View File
@@ -44,7 +44,7 @@ spec:
spec: spec:
containers: containers:
- name: researchowl - name: researchowl
image: git.chemavx.xyz/chemavx/researchowl:a23810b9 image: git.chemavx.xyz/chemavx/researchowl:d31badeb
imagePullPolicy: Always imagePullPolicy: Always
env: env:
- name: TELEGRAM_BOT_TOKEN - name: TELEGRAM_BOT_TOKEN