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
+77 -29
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: "..."
kraken_api_sec: "..."
## Example playbook
pykraken_namespace: pykraken
pykraken_schedule: "0 * * * * "
pykraken_r_value_target: .33
pykraken_investment_count: 3
pykraken_investment_volume: 100
```yaml
- name: Deploy Kraken Trader
hosts: localhost
gather_facts: false
vars:
kraken_bot_image: ghcr.io/example/kraken-trader:2.0.0
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('') }}"
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
roles:
- ansible-role-kraken-bot
```
Dependencies
------------
Store passwords and Kraken credentials in Ansible Vault or an external secret
manager. Do not commit plaintext credentials to playbooks or inventory.
None.
## Variables
Example Playbook
----------------
| 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. |
- hosts: servers
roles:
- { role: ericomeehan.kraken-bot }
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.
License
-------
## Network and secrets
BSD
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.
Author Information
------------------
## Configuration updates
Eric O'Neill Meehan
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"
kraken_bot_schedule: "0 0 * * *"
kraken_bot_r_value_target: ".33"
kraken_bot_investment_count: "3"
kraken_bot_investment_volume: "10"
kraken_bot_target_gain: ".15"
kraken_bot_max_loss: ".05"
kraken_bot_namespace: kraken-trader
kraken_bot_name: kraken-trader
kraken_bot_image: ""
kraken_bot_image_pull_policy: IfNotPresent
kraken_bot_replicas: 1
kraken_bot_storage_size: 1Gi
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:
author: your name
description: your role description
company: your company (optional)
# If the issue tracker for your role is not on github, uncomment the
# next line and provide a value
# issue_tracker_url: http://example.com/issue/tracker
# Choose a valid license ID from https://spdx.org - some suggested licenses:
# - BSD-3-Clause (default)
# - 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.
author: Eric O'Neill Meehan
description: Deploy the continuous Kraken Trader portfolio manager to Kubernetes
license: BSD-3-Clause
min_ansible_version: "2.14"
platforms: []
galaxy_tags:
- kubernetes
- kraken
- cryptocurrency
- trading
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"
+212 -35
View File
@@ -1,49 +1,226 @@
---
# tasks file for ansible-role-kraken-bot
- name: Create kraken-bot namespace
k8s:
- name: Validate deployment settings
ansible.builtin.assert:
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
definition:
apiVersion: v1
kind: Namespace
metadata:
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
k8s:
- name: Create persistent application data volume
kubernetes.core.k8s:
state: present
definition:
apiVersion: batch/v1
kind: CronJob
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: kraken-bot
name: "{{ kraken_bot_name }}-data"
namespace: "{{ kraken_bot_namespace }}"
spec:
schedule: "{{ kraken_bot_schedule }}"
jobTemplate:
accessModes:
- 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:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app.kubernetes.io/name: "{{ kraken_bot_name }}"
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:
template:
spec:
containers:
- name: bot
image: ericomeehan/kraken-bot
imagePullPolicy: Always
env:
- name: KRAKEN_API_TOKEN
value: "{{ kraken_api_token }}"
- name: KRAKEN_API_SEC
value: "{{ kraken_api_sec }}"
- name: R_VALUE_TARGET
value: "{{ kraken_bot_r_value_target }}"
- name: INVESTMENT_COUNT
value: "{{ kraken_bot_investment_count }}"
- name: INVESTMENT_VOLUME
value: "{{ kraken_bot_investment_volume }}"
- name: TARGET_GAIN
value: "{{ kraken_bot_target_gain }}"
- name: MAX_LOSS
value: "{{ kraken_bot_max_loss }}"
command:
- /usr/bin/python3
- /usr/local/src/kraken-bot/app.py
restartPolicy: OnFailure
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: kraken-trader
image: "{{ kraken_bot_image }}"
imagePullPolicy: "{{ kraken_bot_image_pull_policy }}"
ports:
- name: http
containerPort: 8080
protocol: TCP
env:
- name: CONFIG_PATH
value: /config/strategies.yaml
- name: DATA_DIR
value: /data
- name: HOST
value: 0.0.0.0
- name: PORT
value: "8080"
- name: LIVE_TRADING_ENABLED
value: "{{ kraken_bot_live_trading_enabled | bool | string | lower }}"
- name: LOG_LEVEL
value: "{{ kraken_bot_log_level }}"
- name: KRAKEN_API_KEY
valueFrom:
secretKeyRef:
name: "{{ kraken_bot_name }}-secrets"
key: KRAKEN_API_KEY
- 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