Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d02b313e2c
|
@@ -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
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
None.
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
- hosts: servers
|
||||
```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:
|
||||
- { 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
@@ -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
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
---
|
||||
# handlers file for ansible-role-kraken-bot
|
||||
+12
-49
@@ -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.
|
||||
|
||||
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
---
|
||||
requires_ansible: ">=2.14.0"
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
---
|
||||
collections:
|
||||
- name: kubernetes.core
|
||||
version: ">=3.0.0,<7.0.0"
|
||||
+208
-31
@@ -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:
|
||||
automountServiceAccountToken: false
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 10001
|
||||
runAsGroup: 10001
|
||||
fsGroup: 10001
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: bot
|
||||
image: ericomeehan/kraken-bot
|
||||
imagePullPolicy: Always
|
||||
- name: kraken-trader
|
||||
image: "{{ kraken_bot_image }}"
|
||||
imagePullPolicy: "{{ kraken_bot_image_pull_policy }}"
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
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
|
||||
- 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) }}"
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
localhost
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
- hosts: localhost
|
||||
remote_user: root
|
||||
roles:
|
||||
- ansible-role-kraken-bot
|
||||
@@ -1,2 +0,0 @@
|
||||
---
|
||||
# vars file for ansible-role-kraken-bot
|
||||
Reference in New Issue
Block a user