# UCITS rebalancer — routes the adaptive MULTI-STRAT book (5 pure-equity ETFs) through the PRIIPs-legal
# US->UCITS execution map (Component B) on IBKR PAPER via `execute-multistrat --venue ucits
# --execute --paper-envelope 1000000`. The MODELED track is UNCHANGED (still modeled on the US underlyings);
# only the broker order symbols are remapped (SPY->CSPX, IEF->IBTM, GLD->SGLN, PDBC->ICOM, DBMF->DBMF.L) —
# an unmapped sleeve refuses rather than routing a wrong/absent order. SHIPPED SUSPENDED: this is a thin
# execution-routing addition validated by unit tests only, not yet armed against a real UCITS-capable
# account. Monthly cadence (1st of month) — a UCITS rebalance need not track the daily US forward-track pace.
#
# ib_async is NOT baked into the cockpit image -> pip-install it at runtime, like fxhnt-ibkr-rebalancer.
# Requires ib-gateway UP (kubectl scale deploy/ib-gateway --replicas=1).
apiVersion: batch/v1
kind: CronJob
metadata:
  name: fxhnt-ucits-rebalancer
  namespace: foxhunt
  labels: { app.kubernetes.io/name: fxhnt-ucits-rebalancer, app.kubernetes.io/part-of: foxhunt }
spec:
  schedule: "0 9 * * 1-5"            # weekdays 09:00 UTC — inside LSE/Euronext hours (open ~07:00 UTC BST,
                                     # close ~15:30) so the UCITS ETFs (CSPX/IBTM/SGLN/ICOM/DBMF) actually
                                     # price + fill. (Was monthly 14:40 UTC-1st; moved to a live-market
                                     # weekday slot for the PRIIPs-legal paper validation.)
  suspend: false                     # ARMED (paper) — the PRIIPs-legal leg for the EU-domiciled DU account;
                                     # ISIN-resolved UCITS contracts + real UCITS pricing (2026-07-15 fix).
                                     # The US leg (fxhnt-ibkr-rebalancer) is suspended — EU retail can't trade
                                     # US ETFs (PRIIPs/KID). Paper only; no live capital.
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 3
  jobTemplate:
    spec:
      backoffLimit: 1
      ttlSecondsAfterFinished: 604800
      template:
        metadata:
          labels:
            app.kubernetes.io/name: fxhnt-ucits-rebalancer     # egress NetworkPolicy selects this
            app.kubernetes.io/part-of: foxhunt                 # gitea ingress (git-sync) + base egress
            app: multistrat-rebalancer                          # ib-gateway INGRESS allows this label on 4004
        spec:
          restartPolicy: Never
          imagePullSecrets:
            - name: scw-registry
          initContainers:
            - name: git-sync
              image: alpine/git:latest
              command: [/bin/sh, -c]
              args:
                - |
                  mkdir -p /root/.ssh && cp /etc/git-ssh/ssh-privatekey /root/.ssh/id_ed25519 && chmod 600 /root/.ssh/id_ed25519
                  printf 'Host *\n  StrictHostKeyChecking no\n  UserKnownHostsFile /dev/null\n' > /root/.ssh/config
                  rm -rf /code/src
                  git clone --depth 1 --branch master --filter=blob:none --sparse \
                    "ssh://git@gitea-ssh.gitea.svc.cluster.local:22/gitadmin/fxhnt.git" /tmp/repo
                  cd /tmp/repo && git sparse-checkout set src && [ -d /tmp/repo/src ]
                  cp -a /tmp/repo/src /code/src && echo "git-sync OK: $(git rev-parse --short HEAD)"
              resources: { requests: { cpu: 50m, memory: 64Mi }, limits: { cpu: 500m, memory: 256Mi } }
              volumeMounts:
                - { name: code, mountPath: /code }
                - { name: git-ssh-key, mountPath: /etc/git-ssh, readOnly: true }
          containers:
            - name: rebalancer
              image: rg.fr-par.scw.cloud/bizworx/fxhnt-cockpit:latest
              command: ["/bin/sh", "-c"]
              args:
                - |
                  pip install --quiet --no-cache-dir ib_async==2.1.0
                  fxhnt execute-multistrat --venue ucits --execute --paper-envelope 100000
              env:
                - { name: PYTHONPATH, value: /code/src }
                - { name: FXHNT_OPERATIONAL_DSN, value: "postgresql+psycopg://foxhunt@postgres.foxhunt.svc.cluster.local:5432/fxhnt" }
                - name: PGPASSWORD
                  valueFrom: { secretKeyRef: { name: db-credentials, key: password } }
                - { name: FXHNT_IBKR_HOST, value: "ib-gateway" }
                - { name: FXHNT_IBKR_PORT, value: "4004" }      # socat-bridged pod-to-pod port (TrustedIPs-safe)
                - { name: FXHNT_IBKR_CLIENT_ID, value: "26" }   # unique client id (distinct from fxhnt-ibkr-rebalancer's 25)
                - { name: PYTHONUNBUFFERED, value: "1" }
              resources:
                requests: { memory: "512Mi", cpu: "250m" }
                limits: { memory: "1Gi", cpu: "1" }
              volumeMounts:
                - { name: code, mountPath: /code, readOnly: true }
          volumes:
            - { name: code, emptyDir: {} }
            - { name: git-ssh-key, secret: { secretName: argo-git-ssh-key, defaultMode: 256 } }
---
# Egress: DNS + Postgres (5432, record multistrat_exec) + gitea-sshd (22, git-sync) + ib-gateway (4004/4002)
# + external 443 (Yahoo price data). Bybit/Alpaca not needed.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: fxhnt-ucits-rebalancer
  namespace: foxhunt
  labels: { app.kubernetes.io/part-of: foxhunt }
spec:
  podSelector: { matchLabels: { app.kubernetes.io/name: fxhnt-ucits-rebalancer } }
  policyTypes: [Egress]
  egress:
    - ports: [{ port: 53, protocol: UDP }, { port: 53, protocol: TCP }]
    - to: [{ podSelector: { matchLabels: { app.kubernetes.io/name: postgres } } }]
      ports: [{ port: 5432, protocol: TCP }]
    - to: [{ namespaceSelector: { matchLabels: { kubernetes.io/metadata.name: gitea } }, podSelector: { matchLabels: { app.kubernetes.io/name: gitea } } }]
      ports: [{ port: 2222, protocol: TCP }, { port: 22, protocol: TCP }]
    - to: [{ podSelector: { matchLabels: { app: ib-gateway } } }]
      ports: [{ port: 4004, protocol: TCP }, { port: 4002, protocol: TCP }]
    - to: [{ ipBlock: { cidr: 0.0.0.0/0, except: [10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16] } }]   # Yahoo 443
      ports: [{ port: 443, protocol: TCP }]
