Compare commits

..
1 Commits
Author SHA1 Message Date
eric d02b313e2c Copilot redesign 2026-10-21 21:09:45 -04:00
10 changed files with 341 additions and 132 deletions
+80 -32
View File
@@ -1,43 +1,91 @@
Pykraken # ansible-role-kraken-bot
=========
Deploys a Kubernetes CronJob for scheduled execution of pykraken. Deploys the current Kraken Trader container as a continuously running Kubernetes
Deployment. The role creates a namespace, PVC, ConfigMap, Kubernetes Secret,
Deployment, and internal ClusterIP Service. It does not use a CronJob.
Requirements The Deployment is fixed at one replica: multiple pods could place duplicate trades
------------ and cannot safely share the SQLite state file.
An API token and secret from Kraken. ## Requirements
Role Variables - Ansible 2.14 or newer.
-------------- - The `kubernetes.core` collection and Python `kubernetes` package on the Ansible
controller. Install the collection with `ansible-galaxy collection install -r requirements.yml`.
- A configured Kubernetes context, or set `kraken_bot_kubeconfig` and optionally
`kraken_bot_kube_context`.
- A published app image and dashboard password at least 16 characters long.
kraken_api_token: "..." ## Example playbook
kraken_api_sec: "..."
pykraken_namespace: pykraken ```yaml
pykraken_schedule: "0 * * * * " - name: Deploy Kraken Trader
pykraken_r_value_target: .33 hosts: localhost
pykraken_investment_count: 3 gather_facts: false
pykraken_investment_volume: 100 vars:
kraken_bot_image: ghcr.io/example/kraken-trader:2.0.0
Dependencies kraken_bot_dashboard_password: "{{ vault_kraken_dashboard_password }}"
------------ kraken_bot_api_key: "{{ vault_kraken_api_key | default('') }}"
kraken_bot_api_secret: "{{ vault_kraken_api_secret | default('') }}"
None. kraken_bot_config:
poll_interval_seconds: 60
Example Playbook snapshot_interval_seconds: 300
---------------- fee_rate: 0.004
slippage: 0.0005
- hosts: servers min_trade_fraction: 0.01
strategies:
- id: hodl-btc
type: buy_and_hold
mode: paper
starting_capital: 1000
pairs: [XBTUSD]
interval: 1440
roles: roles:
- { role: ericomeehan.kraken-bot } - ansible-role-kraken-bot
```
License Store passwords and Kraken credentials in Ansible Vault or an external secret
------- manager. Do not commit plaintext credentials to playbooks or inventory.
BSD ## Variables
Author Information | Variable | Default | Description |
------------------ |---|---|---|
| `kraken_bot_image` | required | Full published image reference, preferably pinned to a version or digest. |
| `kraken_bot_namespace` | `kraken-trader` | Namespace to create and deploy into. |
| `kraken_bot_name` | `kraken-trader` | Resource name prefix. |
| `kraken_bot_dashboard_password` | required | Dashboard Basic Auth password (minimum 16 characters). |
| `kraken_bot_dashboard_username` | `admin` | Dashboard Basic Auth username. |
| `kraken_bot_api_key` / `kraken_bot_api_secret` | empty | Kraken credentials. Read-only keys display account values; live trading needs order permission. Never enable withdrawals. |
| `kraken_bot_live_trading_enabled` | `false` | Explicit live-trading guard. Live mode requires both Kraken credentials. |
| `kraken_bot_config` | paper `buy_and_hold` example | Full application `strategies.yaml` content as a mapping. |
| `kraken_bot_storage_size` | `1Gi` | Persistent volume request. |
| `kraken_bot_storage_class` | cluster default | Optional storage class. |
| `kraken_bot_service_type` | `ClusterIP` | Keep internal unless adding a secured ingress. |
| `kraken_bot_kubeconfig` | current context | Optional kubeconfig path. |
| `kraken_bot_kube_context` | current context | Optional kubeconfig context. |
| `kraken_bot_image_pull_policy` | `IfNotPresent` | Kubernetes image pull policy. |
| `kraken_bot_log_level` | `INFO` | Application log level. |
Eric O'Neill Meehan The role rejects multiple replicas, short or missing dashboard passwords, and
live strategies unless live trading is explicitly enabled and credentials are
present. Trading remains in paper mode by default.
## Network and secrets
The Service is `ClusterIP`; the dashboard requires HTTP Basic authentication.
Use TLS at any ingress or reverse proxy; do not expose Basic Auth over plain HTTP.
`/healthz` is unauthenticated for Kubernetes probes. Kubernetes Secret values are
stored in etcd, so configure encryption at rest and restrict namespace access.
Secret rotation requires restarting the Deployment so environment variables
refresh.
## Configuration updates
Re-run the role after editing `kraken_bot_config`. A checksum annotation rolls
pods when the strategy config changes. The SQLite database remains on the PVC;
changing a strategy ID creates a separate portfolio rather than resetting state.
## License
BSD-3-Clause
+34 -8
View File
@@ -1,9 +1,35 @@
--- ---
# defaults file for ansible-role-kraken-bot kraken_bot_namespace: kraken-trader
kraken_bot_namespace: "kraken" kraken_bot_name: kraken-trader
kraken_bot_schedule: "0 0 * * *" kraken_bot_image: ""
kraken_bot_r_value_target: ".33" kraken_bot_image_pull_policy: IfNotPresent
kraken_bot_investment_count: "3"
kraken_bot_investment_volume: "10" kraken_bot_replicas: 1
kraken_bot_target_gain: ".15" kraken_bot_storage_size: 1Gi
kraken_bot_max_loss: ".05" kraken_bot_storage_class: ""
kraken_bot_service_type: ClusterIP
kraken_bot_kubeconfig: ""
kraken_bot_kube_context: ""
kraken_bot_api_key: ""
kraken_bot_api_secret: ""
kraken_bot_dashboard_username: admin
kraken_bot_dashboard_password: ""
kraken_bot_live_trading_enabled: false
kraken_bot_log_level: INFO
kraken_bot_config:
poll_interval_seconds: 60
snapshot_interval_seconds: 300
fee_rate: 0.004
slippage: 0.0005
min_trade_fraction: 0.01
strategies:
- id: hodl-btc
type: buy_and_hold
mode: paper
starting_capital: 1000
pairs:
- XBTUSD
interval: 1440
-2
View File
@@ -1,2 +0,0 @@
---
# handlers file for ansible-role-kraken-bot
+12 -49
View File
@@ -1,52 +1,15 @@
---
galaxy_info: galaxy_info:
author: your name author: Eric O'Neill Meehan
description: your role description description: Deploy the continuous Kraken Trader portfolio manager to Kubernetes
company: your company (optional) license: BSD-3-Clause
min_ansible_version: "2.14"
# If the issue tracker for your role is not on github, uncomment the platforms: []
# next line and provide a value galaxy_tags:
# issue_tracker_url: http://example.com/issue/tracker - kubernetes
- kraken
# Choose a valid license ID from https://spdx.org - some suggested licenses: - cryptocurrency
# - BSD-3-Clause (default) - trading
# - MIT
# - GPL-2.0-or-later
# - GPL-3.0-only
# - Apache-2.0
# - CC-BY-4.0
license: license (GPL-2.0-or-later, MIT, etc)
min_ansible_version: 2.1
# If this a Container Enabled role, provide the minimum Ansible Container version.
# min_ansible_container_version:
#
# Provide a list of supported platforms, and for each platform a list of versions.
# If you don't wish to enumerate all versions for a particular platform, use 'all'.
# To view available platforms and versions (or releases), visit:
# https://galaxy.ansible.com/api/v1/platforms/
#
# platforms:
# - name: Fedora
# versions:
# - all
# - 25
# - name: SomePlatform
# versions:
# - all
# - 1.0
# - 7
# - 99.99
galaxy_tags: []
# List tags for your role here, one per line. A tag is a keyword that describes
# and categorizes the role. Users find roles by searching for tags. Be sure to
# remove the '[]' above, if you add tags to this list.
#
# NOTE: A tag is limited to a single word comprised of alphanumeric characters.
# Maximum 20 tags per role.
dependencies: [] dependencies: []
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
# if you add dependencies to this list.
+2
View File
@@ -0,0 +1,2 @@
---
requires_ansible: ">=2.14.0"
+4
View File
@@ -0,0 +1,4 @@
---
collections:
- name: kubernetes.core
version: ">=3.0.0,<7.0.0"
+208 -31
View File
@@ -1,49 +1,226 @@
--- ---
# tasks file for ansible-role-kraken-bot - name: Validate deployment settings
- name: Create kraken-bot namespace ansible.builtin.assert:
k8s: that:
- kraken_bot_image | length > 0
- kraken_bot_dashboard_password | length >= 16
- kraken_bot_replicas | int == 1
- kraken_bot_config is mapping
- kraken_bot_config.strategies is defined
- >-
(not (kraken_bot_live_trading_enabled | bool)) or
((kraken_bot_api_key | length > 0) and (kraken_bot_api_secret | length > 0))
- >-
(kraken_bot_live_trading_enabled | bool) or
(kraken_bot_config.strategies | selectattr('mode', 'defined')
| selectattr('mode', 'equalto', 'live') | list | length == 0)
fail_msg: >-
Set kraken_bot_image and a dashboard password of at least 16 characters.
Live mode additionally requires kraken_bot_live_trading_enabled=true and
Kraken API credentials. This role only supports one replica because all
strategies share the same account and persistent state.
- name: Create application namespace
kubernetes.core.k8s:
state: present state: present
definition: definition:
apiVersion: v1 apiVersion: v1
kind: Namespace kind: Namespace
metadata: metadata:
name: "{{ kraken_bot_namespace }}" name: "{{ kraken_bot_namespace }}"
labels:
app.kubernetes.io/name: "{{ kraken_bot_name }}"
app.kubernetes.io/managed-by: ansible
kubeconfig: "{{ kraken_bot_kubeconfig | default(omit, true) }}"
context: "{{ kraken_bot_kube_context | default(omit, true) }}"
- name: Create a cron job for kraken-bot - name: Create persistent application data volume
k8s: kubernetes.core.k8s:
state: present state: present
definition: definition:
apiVersion: batch/v1 apiVersion: v1
kind: CronJob kind: PersistentVolumeClaim
metadata: metadata:
name: kraken-bot name: "{{ kraken_bot_name }}-data"
namespace: "{{ kraken_bot_namespace }}" namespace: "{{ kraken_bot_namespace }}"
spec: spec:
schedule: "{{ kraken_bot_schedule }}" accessModes:
jobTemplate: - ReadWriteOnce
resources:
requests:
storage: "{{ kraken_bot_storage_size }}"
storageClassName: "{{ kraken_bot_storage_class | default(omit, true) }}"
kubeconfig: "{{ kraken_bot_kubeconfig | default(omit, true) }}"
context: "{{ kraken_bot_kube_context | default(omit, true) }}"
- name: Create strategy configuration
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: ConfigMap
metadata:
name: "{{ kraken_bot_name }}-config"
namespace: "{{ kraken_bot_namespace }}"
data:
strategies.yaml: "{{ kraken_bot_config | to_nice_yaml(indent=2, sort_keys=false) }}"
kubeconfig: "{{ kraken_bot_kubeconfig | default(omit, true) }}"
context: "{{ kraken_bot_kube_context | default(omit, true) }}"
- name: Create credentials secret
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Secret
metadata:
name: "{{ kraken_bot_name }}-secrets"
namespace: "{{ kraken_bot_namespace }}"
type: Opaque
stringData:
KRAKEN_API_KEY: "{{ kraken_bot_api_key }}"
KRAKEN_API_SECRET: "{{ kraken_bot_api_secret }}"
DASHBOARD_USERNAME: "{{ kraken_bot_dashboard_username }}"
DASHBOARD_PASSWORD: "{{ kraken_bot_dashboard_password }}"
kubeconfig: "{{ kraken_bot_kubeconfig | default(omit, true) }}"
context: "{{ kraken_bot_kube_context | default(omit, true) }}"
no_log: true
- name: Deploy continuous trading service
kubernetes.core.k8s:
state: present
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: "{{ kraken_bot_name }}"
namespace: "{{ kraken_bot_namespace }}"
labels:
app.kubernetes.io/name: "{{ kraken_bot_name }}"
spec: spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app.kubernetes.io/name: "{{ kraken_bot_name }}"
template: template:
metadata:
labels:
app.kubernetes.io/name: "{{ kraken_bot_name }}"
annotations:
kraken-bot/config-sha256: "{{ kraken_bot_config | to_nice_yaml(indent=2, sort_keys=false) | hash('sha256') }}"
spec: spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers: containers:
- name: bot - name: kraken-trader
image: ericomeehan/kraken-bot image: "{{ kraken_bot_image }}"
imagePullPolicy: Always imagePullPolicy: "{{ kraken_bot_image_pull_policy }}"
ports:
- name: http
containerPort: 8080
protocol: TCP
env: env:
- name: KRAKEN_API_TOKEN - name: CONFIG_PATH
value: "{{ kraken_api_token }}" value: /config/strategies.yaml
- name: KRAKEN_API_SEC - name: DATA_DIR
value: "{{ kraken_api_sec }}" value: /data
- name: R_VALUE_TARGET - name: HOST
value: "{{ kraken_bot_r_value_target }}" value: 0.0.0.0
- name: INVESTMENT_COUNT - name: PORT
value: "{{ kraken_bot_investment_count }}" value: "8080"
- name: INVESTMENT_VOLUME - name: LIVE_TRADING_ENABLED
value: "{{ kraken_bot_investment_volume }}" value: "{{ kraken_bot_live_trading_enabled | bool | string | lower }}"
- name: TARGET_GAIN - name: LOG_LEVEL
value: "{{ kraken_bot_target_gain }}" value: "{{ kraken_bot_log_level }}"
- name: MAX_LOSS - name: KRAKEN_API_KEY
value: "{{ kraken_bot_max_loss }}" valueFrom:
command: secretKeyRef:
- /usr/bin/python3 name: "{{ kraken_bot_name }}-secrets"
- /usr/local/src/kraken-bot/app.py key: KRAKEN_API_KEY
restartPolicy: OnFailure - name: KRAKEN_API_SECRET
valueFrom:
secretKeyRef:
name: "{{ kraken_bot_name }}-secrets"
key: KRAKEN_API_SECRET
- name: DASHBOARD_USERNAME
valueFrom:
secretKeyRef:
name: "{{ kraken_bot_name }}-secrets"
key: DASHBOARD_USERNAME
- name: DASHBOARD_PASSWORD
valueFrom:
secretKeyRef:
name: "{{ kraken_bot_name }}-secrets"
key: DASHBOARD_PASSWORD
volumeMounts:
- name: data
mountPath: /data
- name: config
mountPath: /config
readOnly: true
- name: tmp
mountPath: /tmp
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 30
periodSeconds: 30
failureThreshold: 3
readinessProbe:
httpGet:
path: /healthz
port: http
periodSeconds: 15
resources:
requests:
cpu: 50m
memory: 256Mi
limits:
memory: 512Mi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
volumes:
- name: data
persistentVolumeClaim:
claimName: "{{ kraken_bot_name }}-data"
- name: config
configMap:
name: "{{ kraken_bot_name }}-config"
- name: tmp
emptyDir: {}
kubeconfig: "{{ kraken_bot_kubeconfig | default(omit, true) }}"
context: "{{ kraken_bot_kube_context | default(omit, true) }}"
- name: Expose dashboard inside the cluster
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Service
metadata:
name: "{{ kraken_bot_name }}"
namespace: "{{ kraken_bot_namespace }}"
spec:
type: "{{ kraken_bot_service_type }}"
selector:
app.kubernetes.io/name: "{{ kraken_bot_name }}"
ports:
- name: http
port: 80
targetPort: http
protocol: TCP
kubeconfig: "{{ kraken_bot_kubeconfig | default(omit, true) }}"
context: "{{ kraken_bot_kube_context | default(omit, true) }}"
-2
View File
@@ -1,2 +0,0 @@
localhost
-5
View File
@@ -1,5 +0,0 @@
---
- hosts: localhost
remote_user: root
roles:
- ansible-role-kraken-bot
-2
View File
@@ -1,2 +0,0 @@
---
# vars file for ansible-role-kraken-bot