commit 6b8e9be45f0779d7b70bfa68846f94c63d0d912a Author: Cryptoval Trading Technologies Date: Mon Jun 8 02:28:14 2026 +0000 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2215894 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +logs/ +*.hjson diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..dabe247 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,27 @@ + +services: + stat_pair_quant: + image: ${CVTT_DOCKER_REGISTRY}/stat_pair_quant:${SP_QUANT_VERSION} + container_name: sp_quant_${DOCKER_PAIR_NAME} + pull_policy: always + restart: unless-stopped + user: ${CVTT_USER:-1001:1001} + network_mode: host + environment: + APP_NAME: ${SP_QUANT_APP_NAME} + CONFIG_SERVICE: ${SP_QUANT_CONFIG_SERVICE} + CONFIG_FILE: ${SP_QUANT_CONFIG_FILE} + FGW_BASE_URL: ${SP_QUANT_FGW_BASE_URL} + # + LOG_DIR: ${SP_QUANT_LOG_DIR} + LOG_FILE: ${SP_QUANT_LOG_FILE} + ADD_ARGS: ${SP_QUANT_ADD_ARGS} + # + SP_QUANT_REST_HOST: ${SP_QUANT_REST_HOST} + SP_QUANT_REST_PORT: ${SP_QUANT_REST_PORT} + # + SP_QUANT_BOOK: ${SP_QUANT_BOOK} + SP_QUANT_INST_A: ${SP_QUANT_INST_A} + SP_QUANT_INST_B: ${SP_QUANT_INST_B} + volumes: + - ./logs:/logs diff --git a/env.sh b/env.sh new file mode 100755 index 0000000..736a1cc --- /dev/null +++ b/env.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash + +# ---- Functions +_env_usage() { + echo "Usage: $0 --config --pair " +} +get_tcp_port() { + local start="$1" + local end="$2" + local port + + for ((port=start; port<=end; port++)); do + if ! ss -ltn "sport = :$port" | grep -q ":$port"; then + echo "$port" + return 0 + fi + done + + echo "" + return 1 +} + +_extract_base_currency() { + local s="$1" + local rest + local _prefix + local base + local _quote + + rest="${s#*:}" + + if [[ "$rest" == "$s" ]]; then + echo "" + return 1 + fi + + IFS='-' read -r _prefix base _quote <<< "$rest" + + if [[ -z "$base" ]]; then + echo "" + return 1 + fi + + echo "$base" +} + +# Detect whether script is sourced +_env_is_sourced() { + [[ "${BASH_SOURCE[0]}" != "$0" ]] +} + +pair_to_project_name() { + local pair="$1" + pair="${pair,,}" # lowercase + pair="${pair//-/_}" # replace - with _ + echo "$pair" +} + +# ============================== +CONFIG="" +PAIR="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --config) + CONFIG="$2" + shift 2 + ;; + --pair) + PAIR="$2" + shift 2 + ;; + -h|--help) + _env_usage + _env_is_sourced && return 0 || exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + _env_usage >&2 + _env_is_sourced && return 1 || exit 1 + ;; + esac +done + +if [[ -z "$CONFIG" ]]; then + echo "Missing --config" >&2 + _env_usage >&2 + _env_is_sourced && return 1 || exit 1 +fi + +if [[ -z "$PAIR" ]]; then + echo "Missing --pair" >&2 + _env_usage >&2 + _env_is_sourced && return 1 || exit 1 +fi + +if [[ ! -f "$CONFIG" ]]; then + echo "Config file not found: $CONFIG" >&2 + _env_is_sourced && return 1 || exit 1 +fi + +required_commands="jq hjson" +for cmd in ${required_commands}; do + if ! command -v $cmd >/dev/null 2>&1; then + echo "Error: $cmd is required but not installed" >&2 + _env_is_sourced && return 1 || exit 1 + fi +done + +BOOK_VALUE="$(hjson -j "${CONFIG}" | jq -r '.book // empty' )" +INST_A_VALUE="$(hjson -j "${CONFIG}" | jq -r --arg pair "$PAIR" '.pairs[$pair].INST_A // empty' )" +INST_B_VALUE="$(hjson -j "${CONFIG}" | jq -r --arg pair "$PAIR" '.pairs[$pair].INST_B // empty' )" + +if [[ -z "$BOOK_VALUE" ]]; then + echo "Missing .book in config: $CONFIG" >&2 + _env_is_sourced && return 1 || exit 1 +fi + +if [[ -z "$INST_A_VALUE" || -z "$INST_B_VALUE" ]]; then + echo "Pair not found or incomplete: $PAIR" >&2 + _env_is_sourced && return 1 || exit 1 +fi + +export BOOK="$BOOK_VALUE" +export INST_A="$INST_A_VALUE" +export INST_B="$INST_B_VALUE" + +echo "BOOK=$BOOK" +echo "INST_A=$INST_A" +echo "INST_B=$INST_B" + +export DOCKER_PAIR_NAME="$(_extract_base_currency ${INST_A})-$(_extract_base_currency ${INST_B})" +export PROJECT_NAME=$(pair_to_project_name "${DOCKER_PAIR_NAME}") + +export SP_QUANT_BOOK="${BOOK}" +export SP_QUANT_INST_A="${INST_A}" +export SP_QUANT_INST_B="${INST_B}" + + +export CVTT_DOCKER_REGISTRY=cloud16.cvtt.vpn:5543 + +###################################### +export SP_QUANT_VERSION=2.3.5 +###################################### + +export SP_QUANT_APP_NAME=SPQ + +export SP_QUANT_LOG_DIR=/logs +export SP_QUANT_LOG_FILE=%T.sp_quant.${DOCKER_PAIR_NAME}.log +export SP_QUANT_ADD_ARGS= + +# CVTT settings +export SP_QUANT_CONFIG_SERVICE=cloud16.cvtt.vpn:6789 +export SP_QUANT_CONFIG_FILE=http://cloud16.cvtt.vpn:6789/apps/stat_pairs/stat_pair_quant +export SP_QUANT_FGW_BASE_URL=http://cld32-tester.cvtt.vpn:23456 + +# Local settings +export SP_QUANT_REST_HOST=$(hostname).cvtt.vpn +export SP_QUANT_REST_PORT=$(get_tcp_port 52000 53000) + +# ------------------- diff --git a/glance.sh b/glance.sh new file mode 100755 index 0000000..249c48c --- /dev/null +++ b/glance.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +cd $(realpath $(dirname $0)) +for prj in $(docker compose ls | grep 'running' | grep $(pwd) | awk '{print $1}'); do + docker compose -p $prj ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Service}}' | grep -v IMAGE +done diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..6bc8994 --- /dev/null +++ b/run.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash + + +#!/usr/bin/env bash + +CONFIG="" +COMMAND="" +SLEEP_SECONDS=0 + +usage() { + echo "Usage: $0 --config --command [--sleep default 0]" +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --config) + CONFIG="$2" + shift 2 + ;; + --command) + COMMAND="$2" + shift 2 + ;; + --sleep) + SLEEP_SECONDS="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +if [[ -z "$CONFIG" ]]; then + echo "Missing --config" >&2 + usage >&2 + exit 1 +fi + + +if [[ ! -f "$CONFIG" ]]; then + echo "Config file not found: $CONFIG" >&2 + exit 1 +fi + +if [[ -z "$COMMAND" ]]; then + echo "Missing --command" >&2 + usage >&2 + exit 1 +fi + +required_commands="jq hjson" +for cmd in ${required_commands}; do + if ! command -v $cmd >/dev/null 2>&1; then + echo "Error: $cmd is required but not installed" >&2 + _env_is_sourced && return 1 || exit 1 + fi +done + +pairs="$(hjson -j "${CONFIG}" | jq -r '.pairs | keys_unsorted | join(" ")')" + +echo "Pairs: ($pairs)" + +for pair in ${pairs[@]}; do + echo "Starting ${pair} ..." + source ./env.sh --config $CONFIG --pair ${pair} + echo "docker compose -p ${PROJECT_NAME} ${COMMAND}" + docker compose -p ${PROJECT_NAME} ${COMMAND} + # + echo "Sleeping ${SLEEP_SECONDS} seconds ..." + sleep ${SLEEP_SECONDS} +done diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..604fd33 --- /dev/null +++ b/start.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +CONFIG="" + +usage() { + echo "Usage: $0 --config " +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --config) + CONFIG="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +if [[ -z "$CONFIG" ]]; then + echo "Missing --config" >&2 + usage >&2 + exit 1 +fi + + +if [[ ! -f "$CONFIG" ]]; then + echo "Config file not found: $CONFIG" >&2 + exit 1 +fi + +cd $(realpath $(dirname $0)) +./run.sh --config $CONFIG --command "up -d" --sleep 10 \ No newline at end of file diff --git a/stop.sh b/stop.sh new file mode 100755 index 0000000..8e7b744 --- /dev/null +++ b/stop.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + + +cd $(realpath $(dirname $0)) +for prj in $(docker compose ls | grep 'running' | grep $(pwd) | awk '{print $1}'); do + cmd="docker compose -p $prj down" + echo $cmd + $cmd +done diff --git a/watch.sh b/watch.sh new file mode 100755 index 0000000..8d97786 --- /dev/null +++ b/watch.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +cd $(realpath $(dirname $0)) +while true; do + clear + ./glance.sh + sleep 5 +done