Compare commits
10
Commits
development
...
v1.1.19
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32d127fd84 | ||
|
|
3cb69c7520 | ||
|
|
47e897d038 | ||
|
|
126ec169ea | ||
|
|
84953df12c | ||
|
|
e8322183ec | ||
|
|
30ba7ecd19 | ||
|
|
db6b34c6a8 | ||
|
|
4dbafcab91 | ||
|
|
9fd7f8a0a6 |
No files matched your search
@@ -0,0 +1,135 @@
|
||||
FROM ubuntu:latest
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ENV TZ=America/New_York
|
||||
|
||||
# light deps (should be quick to download/install)
|
||||
RUN apt-get update && apt-get install -y \
|
||||
ca-certificates \
|
||||
curl \
|
||||
gnupg \
|
||||
liblttng-ust-dev \
|
||||
libpcap0.8-dev \
|
||||
locales \
|
||||
ninja-build \
|
||||
software-properties-common \
|
||||
sqlite3 \
|
||||
tzdata
|
||||
|
||||
# clang
|
||||
RUN apt-get update && apt-get install -y \
|
||||
clang-13 \
|
||||
clang-format-13 \
|
||||
clangd-13 \
|
||||
clang-15 \
|
||||
clang-format-15 \
|
||||
clangd-15
|
||||
|
||||
# g++
|
||||
RUN apt-get update && apt-get install -y \
|
||||
g++-11 \
|
||||
g++-12
|
||||
|
||||
# python
|
||||
RUN apt-get update && apt-get install -y \
|
||||
python3-pip \
|
||||
python3-venv \
|
||||
&& python3 -m pip install --upgrade pip
|
||||
|
||||
# dotnet
|
||||
RUN apt-get update && apt-get install -y \
|
||||
dotnet7
|
||||
|
||||
# ssh
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libssh2-1-dev \
|
||||
openssh-server
|
||||
|
||||
# cmake
|
||||
RUN \
|
||||
curl -fsSL https://apt.kitware.com/keys/kitware-archive-latest.asc 2> /dev/null \
|
||||
| gpg --dearmor - | tee /etc/apt/trusted.gpg.d/kitware.gpg > /dev/null \
|
||||
&& apt-add-repository "deb https://apt.kitware.com/ubuntu/ $(lsb_release -cs) main" \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y cmake
|
||||
|
||||
# node
|
||||
ARG node_major=18
|
||||
RUN mkdir -p /etc/apt/keyrings \
|
||||
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \
|
||||
| gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
|
||||
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$node_major.x nodistro main" \
|
||||
| tee /etc/apt/sources.list.d/nodesource.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y nodejs
|
||||
|
||||
# ccache is done here so that ccache sees clang++ installed, not clang-10
|
||||
RUN apt-get update && apt-get install -y \
|
||||
ccache
|
||||
|
||||
# emumba (light deps)
|
||||
RUN apt-get update && apt-get install -y \
|
||||
build-essential \
|
||||
doxygen \
|
||||
python3-sphinx \
|
||||
graphviz \
|
||||
libgtest-dev \
|
||||
libssl-dev \
|
||||
rapidjson-dev \
|
||||
zlib1g-dev \
|
||||
pkg-config
|
||||
|
||||
# emumba (boost)
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libboost-filesystem-dev \
|
||||
libboost-program-options-dev \
|
||||
libboost-test-dev \
|
||||
libboost-date-time-dev
|
||||
|
||||
# dev utils
|
||||
RUN apt-get update && apt-get install -y \
|
||||
fish \
|
||||
git-all \
|
||||
sudo \
|
||||
vim \
|
||||
wget
|
||||
|
||||
# set up default executables (update-alternatives)
|
||||
RUN \
|
||||
update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-13 100; \
|
||||
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-13 100; \
|
||||
update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-13 100; \
|
||||
update-alternatives --install /usr/bin/clangd clangd /usr/bin/clangd-13 100; \
|
||||
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 100; \
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100
|
||||
|
||||
# Add compiler symlinks for ccache
|
||||
RUN \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/clang; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/clang++; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/clang-13; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/clang++-13; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/clang-15; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/clang++-15; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/gcc; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/g++; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/gcc-11; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/g++-11; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/gcc-12; \
|
||||
ln -s /usr/bin/ccache /usr/local/bin/g++-12
|
||||
|
||||
RUN locale-gen en_US.UTF-8
|
||||
|
||||
ARG docker_user="bts"
|
||||
ARG docker_uid="1000"
|
||||
ARG docker_pass="bts"
|
||||
|
||||
RUN \
|
||||
groupadd -g "$docker_uid" "$docker_user" \
|
||||
&& useradd \
|
||||
-m \
|
||||
-u "$docker_uid" \
|
||||
-g "$docker_uid" \
|
||||
-G sudo \
|
||||
--password "$(openssl passwd -1 "$docker_pass")" \
|
||||
"$docker_user"
|
||||
@@ -0,0 +1,64 @@
|
||||
[dev containers]: https://code.visualstudio.com/docs/devcontainers/containers
|
||||
[dev containers ext]: https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers
|
||||
[docker repo sparkd-dev]: https://hub.docker.com/repository/docker/bluetradingsystems/sparkd-dev/general
|
||||
# Sparkd Dev Container
|
||||
## How to create a VS Code [Dev Container][dev containers]
|
||||
|
||||
1. Make sure the **[Dev Containers][dev containers ext]** extension is installed
|
||||
1. Make sure your Docker image is up to date
|
||||
```
|
||||
docker pull bluetradingsystems/sparkd-dev:latest
|
||||
```
|
||||
1. Copy **devcontainer.json** from this folder to **$workspaceFolder/.devcontainer/**.
|
||||
|
||||
1. In the VS Code Command Palette, run **Dev Containers: Reopen in Container**
|
||||
|
||||
---
|
||||
## How to create a Sparkd Container in the terminal (without VS Code)
|
||||
|
||||
1. Make sure your Docker image is up to date
|
||||
```
|
||||
docker pull bluetradingsystems/sparkd-dev:latest
|
||||
```
|
||||
2. Start the container
|
||||
```
|
||||
# Replace <cont_name> with a name for the container. The container's
|
||||
# "$HOME" dir will be mounted to "$HOME/.sparkd-dev/mounts/<cont_name> on
|
||||
# the host
|
||||
|
||||
./start_docker.sh --container <cont_name> --mount-existing
|
||||
```
|
||||
* *Replace <cont_name> with a name for the container*
|
||||
* *The container's `$HOME` dir will be mounted to
|
||||
`$HOME/.sparkd-dev/mounts<cont_name>` on the host*<br>
|
||||
* *Run: `./start_docker.sh --help` to see additional options*
|
||||
3. *(Optional) Attach VS Code to the running container.*<br>
|
||||
Run **Dev Containers: Attach to Running Container** from the command palette,
|
||||
then select your container in the dropdown
|
||||
|
||||
---
|
||||
## How to update the Docker image
|
||||
|
||||
1. Change the image by modifying the Dockerfile (don't tag arbitrary
|
||||
images on your machine and push them to our repo on Docker Hub)
|
||||
|
||||
1. After modifying the Dockerfile, (re)build the image
|
||||
```
|
||||
# Run in this dir
|
||||
docker build -t bluetradingsystems/sparkd-dev:latest .
|
||||
```
|
||||
***Note:** Your docker login must have write permissions to push to the
|
||||
[sparkd-dev][docker repo sparkd-dev] repository*
|
||||
|
||||
2. Commit the changes to Dockerfile in git
|
||||
|
||||
3. Push the new image to Docker Hub
|
||||
|
||||
```
|
||||
docker push bluetradingsystems/sparkd-dev:latest
|
||||
```
|
||||
***Note:** The image doesn't have to be on Docker Hub, we can simply build
|
||||
it from scratch on our local machines (with the Dockerfile). However, this
|
||||
takes time. It is significantly quicker to download a prebuilt image, so we
|
||||
store it on a remote repository to save time when setting up a new dev
|
||||
machine.*
|
||||
@@ -0,0 +1,123 @@
|
||||
# ~/.bashrc: executed by bash(1) for non-login shells.
|
||||
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
|
||||
# for examples
|
||||
|
||||
# If not running interactively, don't do anything
|
||||
case $- in
|
||||
*i*) ;;
|
||||
*) return;;
|
||||
esac
|
||||
|
||||
# don't put duplicate lines or lines starting with space in the history.
|
||||
# See bash(1) for more options
|
||||
HISTCONTROL=ignoreboth
|
||||
|
||||
# append to the history file, don't overwrite it
|
||||
shopt -s histappend
|
||||
|
||||
# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
|
||||
HISTSIZE=1000
|
||||
HISTFILESIZE=2000
|
||||
|
||||
# check the window size after each command and, if necessary,
|
||||
# update the values of LINES and COLUMNS.
|
||||
shopt -s checkwinsize
|
||||
|
||||
# If set, the pattern "**" used in a pathname expansion context will
|
||||
# match all files and zero or more directories and subdirectories.
|
||||
#shopt -s globstar
|
||||
|
||||
# make less more friendly for non-text input files, see lesspipe(1)
|
||||
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
|
||||
|
||||
# set variable identifying the chroot you work in (used in the prompt below)
|
||||
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
|
||||
debian_chroot=$(cat /etc/debian_chroot)
|
||||
fi
|
||||
|
||||
# set a fancy prompt (non-color, unless we know we "want" color)
|
||||
case "$TERM" in
|
||||
xterm-color|*-256color) color_prompt=yes;;
|
||||
esac
|
||||
|
||||
# uncomment for a colored prompt, if the terminal has the capability; turned
|
||||
# off by default to not distract the user: the focus in a terminal window
|
||||
# should be on the output of commands, not on the prompt
|
||||
#force_color_prompt=yes
|
||||
|
||||
if [ -n "$force_color_prompt" ]; then
|
||||
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
|
||||
# We have color support; assume it's compliant with Ecma-48
|
||||
# (ISO/IEC-6429). (Lack of such support is extremely rare, and such
|
||||
# a case would tend to support setf rather than setaf.)
|
||||
color_prompt=yes
|
||||
else
|
||||
color_prompt=
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$color_prompt" = yes ]; then
|
||||
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
|
||||
else
|
||||
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
|
||||
fi
|
||||
unset color_prompt force_color_prompt
|
||||
|
||||
# If this is an xterm set the title to user@host:dir
|
||||
case "$TERM" in
|
||||
xterm*|rxvt*)
|
||||
PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
# enable color support of ls and also add handy aliases
|
||||
if [ -x /usr/bin/dircolors ]; then
|
||||
test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
|
||||
alias ls='ls --color=auto'
|
||||
#alias dir='dir --color=auto'
|
||||
#alias vdir='vdir --color=auto'
|
||||
|
||||
alias grep='grep --color=auto'
|
||||
alias fgrep='fgrep --color=auto'
|
||||
alias egrep='egrep --color=auto'
|
||||
fi
|
||||
|
||||
# colored GCC warnings and errors
|
||||
#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
|
||||
|
||||
# some more ls aliases
|
||||
alias ll='ls -alF'
|
||||
alias la='ls -A'
|
||||
alias l='ls -CF'
|
||||
|
||||
# Add an "alert" alias for long running commands. Use like so:
|
||||
# sleep 10; alert
|
||||
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
|
||||
|
||||
# Alias definitions.
|
||||
# You may want to put all your additions into a separate file like
|
||||
# ~/.bash_aliases, instead of adding them here directly.
|
||||
# See /usr/share/doc/bash-doc/examples in the bash-doc package.
|
||||
|
||||
if [ -f ~/.bash_aliases ]; then
|
||||
. ~/.bash_aliases
|
||||
fi
|
||||
|
||||
# enable programmable completion features (you don't need to enable
|
||||
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
|
||||
# sources /etc/bash.bashrc).
|
||||
if ! shopt -oq posix; then
|
||||
if [ -f /usr/share/bash-completion/bash_completion ]; then
|
||||
. /usr/share/bash-completion/bash_completion
|
||||
elif [ -f /etc/bash_completion ]; then
|
||||
. /etc/bash_completion
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -v XDG_CONFIG_HOME ]]; then
|
||||
export XDG_CONFIG_HOME="$HOME/.config"
|
||||
fi
|
||||
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
@@ -0,0 +1,25 @@
|
||||
if ! set -q XDG_CONFIG_HOME
|
||||
set -g XDG_CONFIG_HOME "$HOME/.config"
|
||||
end
|
||||
set fish_config_home $XDG_CONFIG_HOME/fish
|
||||
if test -f "$fish_config_home/aliases.fish"
|
||||
source "$fish_config_home/aliases.fish"
|
||||
end
|
||||
# enable general colors and case insensitive search in 'less'
|
||||
set -x LESS "$LESS -R -Q -I"
|
||||
set -x LC_ALL en_US.UTF-8
|
||||
# enable syntax highlighting in 'less'
|
||||
# set highlight color scheme in 'less' (fg)(bg)
|
||||
set -g LESS_TERMCAP_md (tput bold)(tput setaf 36)
|
||||
set -g LESS_TERMCAP_me (tput sgr0)
|
||||
set -g LESS_TERMCAP_so (tput setaf 232)(tput setab 49)
|
||||
|
||||
set -q TERM
|
||||
if test $status != 0; or test $TERM = 'xterm'
|
||||
set -g TERM 'xterm-256color'
|
||||
end
|
||||
|
||||
# Fix impossible-to-see navy blue command text color
|
||||
set fish_color_command F8F8F2
|
||||
|
||||
set -g PATH "$HOME/.local/bin" "$PATH"
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"image": "bluetradingsystems/sparkd-dev:latest",
|
||||
"initializeCommand": "${localWorkspaceFolder}/docker/sparkd/dev/devcontainer_initialize.sh",
|
||||
"hostRequirements": {
|
||||
"memory": "4gb",
|
||||
"storage": "6gb"
|
||||
},
|
||||
"postCreateCommand": "/home/bts/spark/docker/sparkd/dev/devcontainer_post_create.sh",
|
||||
"containerUser": "bts",
|
||||
"workspaceMount": "source=${localWorkspaceFolder},target=/home/bts/spark,type=bind",
|
||||
"workspaceFolder": "/home/bts/spark",
|
||||
"updateRemoteUserUID": true,
|
||||
"shutdownAction": "none",
|
||||
// Note: If running multiple containers on the same host, each must use a
|
||||
// unique set of ports. Usually we just add 1000 to each port as needed. For
|
||||
// example, if 8000-8003 is taken, go with 9000-9003, then 10000-10003, etc.
|
||||
"runArgs": [
|
||||
"--shm-size",
|
||||
"2gb",
|
||||
"-p",
|
||||
"8000:8000",
|
||||
"-p",
|
||||
"8001:8001",
|
||||
"-p",
|
||||
"8002:8002",
|
||||
"-p",
|
||||
"8003:8003"
|
||||
],
|
||||
"mounts": [
|
||||
"source=${localEnv:HOME}/.sparkd-dev/mounts/sparkd-dev,target=/home/bts,type=bind"
|
||||
]
|
||||
}
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
mkdir -p "$HOME/.sparkd-dev/mounts/sparkd-dev"
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
this_dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
|
||||
repo_dir="$(cd "$this_dir" && git rev-parse --show-toplevel)"
|
||||
tools_dir="$repo_dir/tools"
|
||||
gen_tag="GENERATED BY $(basename "${BASH_SOURCE[0]}")"
|
||||
|
||||
function copy_if_not_exist() {
|
||||
src="$1"
|
||||
dest="$2"
|
||||
if [[ ! -e "$src" ]]; then
|
||||
echo "Unable to copy $src to $dest. Source file doesn't exist"
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -e "$dest" ]]; then
|
||||
cp "$src" "$dest"
|
||||
fi
|
||||
}
|
||||
|
||||
# Init bashrc
|
||||
echo "Initializing bashrc"
|
||||
copy_if_not_exist "$this_dir/bashrc" "$HOME/.bashrc"
|
||||
|
||||
# Init fish config
|
||||
echo "Initializing fish config"
|
||||
fish_cfg_dir="$HOME/.config/fish/"
|
||||
mkdir -p "$fish_cfg_dir"
|
||||
exec "$tools_dir/write_tag_block.sh" \
|
||||
--tag "$gen_tag" \
|
||||
--replace \
|
||||
--in-place \
|
||||
--file "$fish_cfg_dir/config.fish" \
|
||||
--text "$(cat "$this_dir/config.fish")"
|
||||
Executable
+250
@@ -0,0 +1,250 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
function _exit() {
|
||||
if [[ "$#" -gt 1 ]]; then
|
||||
# When there are multiple args, the last arg is the exit code.
|
||||
echo "${@:1:($# - 1)}"
|
||||
exit "${@: -1}"
|
||||
fi
|
||||
echo "$1"
|
||||
exit 0
|
||||
}
|
||||
|
||||
help_text="\
|
||||
Add or update a user's uid
|
||||
|
||||
This script is intended to be run in a Docker container as a means to obtain
|
||||
file permissions in mounted directories owned by the specified UID.
|
||||
|
||||
Usage: set_user.sh [OPTIONS] USER UID
|
||||
|
||||
Positionals:
|
||||
USER The name of the user
|
||||
|
||||
Options:
|
||||
--user USER The name of the user (defaults to 'bts')
|
||||
--uid UID The new uid to give USER (defaults to '1000')
|
||||
--group GROUP The primary group to give USER (defaults to USER)
|
||||
--gid GID The primary gid to give USER (defaults to UID)
|
||||
--pass PASSWORD The password to give USER (defaults to USER, only applies
|
||||
if USER hasn't been created yet)
|
||||
--modify-existing Update UID/GID even if USER already exists
|
||||
-h,--help Print this help message and exit
|
||||
|
||||
"
|
||||
|
||||
function _add_group() {
|
||||
if [[ "$#" -ne 2 ]]; then
|
||||
_exit "'_add_group' requires exactly 2 args (group, gid). Received $#." 1
|
||||
fi
|
||||
local group="$1"
|
||||
local gid="$2"
|
||||
# Check if gid already exists
|
||||
local existing_group=""
|
||||
existing_group="$(getent group "$gid" | cut -d : -f 1)"
|
||||
if [[ -n "$existing_group" ]]; then
|
||||
if [[ "$existing_group" != "$group" ]]; then
|
||||
echo "\
|
||||
The gid '$gid' is already in use by group '$existing_group' (will not rename \
|
||||
the group)"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
# Check if group with same name already exists
|
||||
local existing_gid=""
|
||||
existing_gid="$(getent group "$group" | cut -d : -f 3)"
|
||||
if [[ -n "$existing_gid" ]]; then
|
||||
# The group already exists. Only modify if --modify-existing was specified.
|
||||
if [[ "$modify_existing" -eq 0 ]]; then
|
||||
_exit "\
|
||||
The group '$group' already exists with gid '$existing_gid'.
|
||||
Specify --modify-existing to change the gid anyways." 1
|
||||
fi
|
||||
echo "Changing gid for group '$group' from '$existing_gid' to '$gid'"
|
||||
groupmod -g "$gid" "$group"
|
||||
else
|
||||
# The group doesn't exist. Add it
|
||||
echo "Adding group '$group:$gid'"
|
||||
groupadd -g "$gid" "$group"
|
||||
fi
|
||||
}
|
||||
|
||||
function _add_user() {
|
||||
local user="$1"
|
||||
local uid="$2"
|
||||
local group="$3"
|
||||
local gid="$4"
|
||||
local pass="$5"
|
||||
if [[ "$#" -ne 5 ]]; then
|
||||
_exit "'_add_user' requires exactly 5 args (user, uid, group, gid, pass). \
|
||||
Received $#." 1
|
||||
fi
|
||||
|
||||
# Check if uid is being used by another user
|
||||
local existing_user=""
|
||||
existing_user="$(getent passwd "$uid" | cut -d : -f 1)"
|
||||
if [[ -n "$existing_user" ]]; then
|
||||
if [[ "$existing_user" != "$user" ]]; then
|
||||
_exit "The uid '$uid' is already in use by user '$existing_user'." 1
|
||||
fi
|
||||
|
||||
echo "The user '$user' already has the specified uid '$uid'."
|
||||
# Modify gid if necessary
|
||||
local existing_gid=""
|
||||
existing_gid="$(id -g "$user" 2> /dev/null)"
|
||||
if [[ "$existing_gid" = "$gid" ]]; then
|
||||
echo "The user '$user' already has the specified gid '$gid'."
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$modify_existing" -eq 0 ]]; then
|
||||
_exit "\
|
||||
The user '$user' is associated with the primary group '$existing_gid'.
|
||||
Specify --modify-existing to change the gid anyways." 1
|
||||
fi
|
||||
|
||||
echo "Changing primary group for '$user' from '$existing_gid' to '$gid'."
|
||||
usermod -g "$gid" "$user"
|
||||
return
|
||||
fi
|
||||
|
||||
# Check if user with same name already exists
|
||||
local existing_uid=""
|
||||
existing_uid="$(id -u "$user" 2> /dev/null)"
|
||||
if [[ -n "$existing_uid" ]]; then
|
||||
if [[ "$existing_uid" = "$uid" ]]; then
|
||||
_exit "The user '$user' already has the specified uid '$uid'" 0
|
||||
fi
|
||||
|
||||
if [[ "$modify_existing" -eq 0 ]]; then
|
||||
_exit "\
|
||||
User '$user' already exists with uid '$existing_uid'.
|
||||
Specify --modify-existing to change the uid anyways." 1
|
||||
fi
|
||||
|
||||
echo "Changing uid for user '$user' from '$existing_uid' to '$uid'"
|
||||
if ! usermod -u "$uid" -g "$gid" "$user"; then
|
||||
_exit "usermod exited with code $?" 1
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
# The user doesn't exist. Add it
|
||||
echo "Adding user '$user:$uid' (group='$group:$gid', password='$pass')"
|
||||
useradd -m -u "$uid" -g "$gid" -G sudo --password "$(openssl passwd -1 "$pass")" "$user"
|
||||
}
|
||||
|
||||
function _update_fish_dir_perms() {
|
||||
local user="$1"
|
||||
local uid="$2"
|
||||
local gid="$3"
|
||||
# When a fish shell session starts, a temp dir named '/tmp/fish.<user>' is
|
||||
# created with the user's current uid/gid. If such a dir exists for the
|
||||
# specified USER, make sure the uid/gid match the new values.
|
||||
fish_dir="/tmp/fish.$user"
|
||||
if [[ -d "$fish_dir" ]]; then
|
||||
perms="$uid:$gid"
|
||||
existing_perms="$(stat -c %u:%g "$fish_dir")"
|
||||
if [[ "$existing_perms" != "$perms" ]]; then
|
||||
echo "Changing uid:gid for dir '$fish_dir' from '$existing_perms' to '$perms'"
|
||||
chown -R "$perms" "$fish_dir"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
user=""
|
||||
group=""
|
||||
pass=""
|
||||
declare -i uid=0
|
||||
declare -i gid=0
|
||||
declare -i modify_existing=0
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--user)
|
||||
if [[ -z "$2" ]]; then
|
||||
_exit "USER (--user) must be non-empty" 1
|
||||
elif [[ "$2" = "root" ]]; then
|
||||
_exit "USER (--user) cannot be 'root'" 1
|
||||
fi
|
||||
user="$2"
|
||||
shift 2
|
||||
;;
|
||||
--group)
|
||||
if [[ -z "$2" ]]; then
|
||||
_exit "GROUP (--group) must be non-empty" 1
|
||||
elif [[ "$2" = "root" ]]; then
|
||||
_exit "GROUP (--group) cannot be 'root'" 1
|
||||
fi
|
||||
group="$2"
|
||||
shift 2
|
||||
;;
|
||||
--uid)
|
||||
if [[ -z "$2" ]]; then
|
||||
_exit "UID (--uid) must be non-empty" 1
|
||||
fi
|
||||
uid="$2"
|
||||
shift 2
|
||||
;;
|
||||
--gid)
|
||||
if [[ -z "$2" ]]; then
|
||||
_exit "GID (--gid) must be non-empty" 1
|
||||
fi
|
||||
gid="$2"
|
||||
shift 2
|
||||
;;
|
||||
--pass)
|
||||
if [[ -z "$2" ]]; then
|
||||
_exit "PASS (--pass) must be non-empty" 1
|
||||
fi
|
||||
pass="$2"
|
||||
shift 2
|
||||
;;
|
||||
--modify-existing)
|
||||
modify_existing=1
|
||||
shift 1
|
||||
;;
|
||||
-h)
|
||||
;&
|
||||
--help)
|
||||
_exit "$help_text" 0
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$1" ]]; then
|
||||
_exit "Invalid positional argument '$1'" 1
|
||||
elif [[ -z "$user" ]]; then
|
||||
user="$1"
|
||||
elif [[ -z "$uid" ]]; then
|
||||
uid="$1"
|
||||
else
|
||||
_exit "Unexpected argument '$1'" 1
|
||||
fi
|
||||
shift 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$user" ]]; then
|
||||
user="bts"
|
||||
fi
|
||||
|
||||
if [[ $uid -eq 0 ]]; then
|
||||
uid=1000
|
||||
fi
|
||||
|
||||
# In general, a user's primary group/gid matches the user's name/uid.
|
||||
# If group/gid args weren't specified, set them to name/uid.
|
||||
if [[ -z "$group" ]]; then
|
||||
group="$user"
|
||||
fi
|
||||
if [[ $gid -eq 0 ]]; then
|
||||
gid=$uid
|
||||
fi
|
||||
if [[ -z "$pass" ]]; then
|
||||
pass="$user"
|
||||
fi
|
||||
|
||||
_add_group "$group" "$gid"
|
||||
_add_user "$user" "$uid" "$group" "$gid" "$pass"
|
||||
_update_fish_dir_perms "$user" "$uid" "$gid"
|
||||
Executable
+230
@@ -0,0 +1,230 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
function _exit() {
|
||||
if [[ "$#" -gt 1 ]]; then
|
||||
# When there are multiple args, the last arg is the exit code.
|
||||
echo "${@:1:($# - 1)}"
|
||||
exit "${@: -1}"
|
||||
fi
|
||||
echo "$1"
|
||||
exit 0
|
||||
}
|
||||
|
||||
help_text="\
|
||||
Start a docker dev container
|
||||
Usage: start_docker.sh [OPTIONS] image container
|
||||
|
||||
Positionals:
|
||||
image The image to instantiate
|
||||
container The name for the container
|
||||
|
||||
Options:
|
||||
-u,--user USER The user to add in the container (defaults to 'bts')
|
||||
-p,--pass PASS The password for USER in the container (defaults to USER)
|
||||
-i,--image IMAGE The image to instantiate (defaults to 'bluetradingsystems/sparkd-dev:latest')
|
||||
-c,--container CONT The name for the container
|
||||
-m,--mount MOUNT The name for the folder (outside the container) to create/mount to the home
|
||||
directory in the container. Mount folders are added here: $mount_prefix
|
||||
--mount-existing Mount the specified dir even if it already exists
|
||||
--port <cont_port>:<host_port> OR [<cont_port>:<host_port>,...] Port mapping(s). May be
|
||||
specified multiple times.
|
||||
-h,--help Print this help message and exit
|
||||
|
||||
"
|
||||
|
||||
mount_prefix="$HOME/.sparkd-dev/mounts"
|
||||
declare docker_user=""
|
||||
declare docker_pass=""
|
||||
declare image_name=""
|
||||
declare cont_name=""
|
||||
declare mount_name=""
|
||||
declare -i mount_existing=0
|
||||
declare -a port_mappings=()
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-u)
|
||||
;&
|
||||
--user)
|
||||
if [[ -z "$2" ]]; then
|
||||
_exit "User (-u,--user) must be non-empty" 1
|
||||
fi
|
||||
docker_user="$2"
|
||||
shift 2
|
||||
;;
|
||||
-p)
|
||||
;&
|
||||
--pass)
|
||||
if [[ -z "$2" ]]; then
|
||||
_exit "Password (-p,--pass) must be non-empty" 1
|
||||
fi
|
||||
docker_pass="$2"
|
||||
shift 2
|
||||
;;
|
||||
-i)
|
||||
;&
|
||||
--image)
|
||||
if [[ -n "$image_name" ]]; then
|
||||
_exit "Unexpected argument '$1'" 1
|
||||
elif [[ -z "$2" ]]; then
|
||||
_exit "Image (-i,--image) must be non-empty" 1
|
||||
fi
|
||||
image_name="$2"
|
||||
shift 2
|
||||
;;
|
||||
-c)
|
||||
;&
|
||||
--container)
|
||||
if [[ -n "$cont_name" ]]; then
|
||||
_exit "Unexpected argument '$1'" 1
|
||||
elif [[ -z "$2" ]]; then
|
||||
_exit "Container (-c,--container) must be non-empty" 1
|
||||
fi
|
||||
cont_name="$2"
|
||||
shift 2
|
||||
;;
|
||||
--port)
|
||||
# Parse port(s) arg, and append the result to 'port_mappings'
|
||||
# Note: port(s) may be specified in bracketed notation:
|
||||
# [a:b,c:d,...,y:z]
|
||||
next_ports="$(echo "$2" | sed -e 's/\[//g' -e 's/\]//g' -e 's/,/ /g')"
|
||||
if [[ "${#port_mappings[@]}" -gt 0 ]]; then
|
||||
next_ports=" $next_ports"
|
||||
fi
|
||||
port_mappings+=("$next_ports")
|
||||
shift 2
|
||||
;;
|
||||
-m)
|
||||
;&
|
||||
--mount)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "Mount (-m,--mount) must be non-empty"
|
||||
fi
|
||||
mount_name="$2"
|
||||
shift 2
|
||||
;;
|
||||
--mount-existing)
|
||||
mount_existing=1
|
||||
shift 1
|
||||
;;
|
||||
-h)
|
||||
;&
|
||||
--help)
|
||||
_exit "$help_text" 0
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$1" ]]; then
|
||||
_exit "Invalid positional argument '$1'" "$help_text" 1
|
||||
elif [[ -z "$image_name" ]]; then
|
||||
image_name="$1"
|
||||
elif [[ -z "$cont_name" ]]; then
|
||||
cont_name="$1"
|
||||
else
|
||||
_exit "Unexpected argument '$1'" "$help_text" 1
|
||||
fi
|
||||
shift 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
if [[ -z "$image_name" ]]; then
|
||||
image_name="bluetradingsystems/sparkd-dev:latest"
|
||||
fi
|
||||
|
||||
host_name="$(hostname)"
|
||||
if [[ -n "$cont_name" ]]; then
|
||||
host_name="$host_name.$cont_name"
|
||||
fi
|
||||
|
||||
if [[ -z "$mount_name" ]]; then
|
||||
mount_name="$cont_name"
|
||||
fi
|
||||
|
||||
if [[ -z "$mount_name" && -z "$cont_name" ]]; then
|
||||
_exit "Please specify mount (-m,--mount) and/or container (-c,--container)" 1
|
||||
fi
|
||||
|
||||
if [[ -z "$docker_user" ]]; then
|
||||
docker_user="bts"
|
||||
fi
|
||||
|
||||
if [[ "$docker_user" = "root" ]]; then
|
||||
_exit "USER (--user) cannot be 'root'" 1
|
||||
fi
|
||||
|
||||
cont_home="/home/$docker_user"
|
||||
home_src="$mount_prefix/$mount_name"
|
||||
home_tgt="$cont_home"
|
||||
this_dir="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
|
||||
repo_src="$(cd "$this_dir" && git rev-parse --show-toplevel)"
|
||||
repo_tgt="$cont_home/spark"
|
||||
working_dir="$cont_home"
|
||||
|
||||
if [[ $mount_existing -eq 0 && -e "$home_src" ]]; then
|
||||
# Don't overwrite existing dir (might be mounted in another container)
|
||||
_exit "Mount dir '$home_src' already exists (use --mount-existing to mount \
|
||||
an existing directory)" 1
|
||||
fi
|
||||
|
||||
mkdir -p "$home_src"
|
||||
|
||||
if [[ "${#port_mappings[@]}" -eq 0 ]]; then
|
||||
port_mappings=("8000:8000" "8001:8001" "8002:8002" "8003:8003")
|
||||
fi
|
||||
|
||||
# Adapt port_mappings into args for `docker run` by prepending "-p " to each
|
||||
# item
|
||||
declare -a docker_port_args=()
|
||||
for port_mapping in "${port_mappings[@]}"; do
|
||||
docker_port_args+=("-p $port_mapping")
|
||||
done
|
||||
|
||||
echo "Starting container '$cont_name'
|
||||
image_name : $image_name
|
||||
cont_name : $cont_name
|
||||
home_src : $home_src
|
||||
home_tgt : $home_tgt
|
||||
repo_src : $repo_src
|
||||
repo_tgt : $repo_tgt
|
||||
working dir : $working_dir
|
||||
ports : ${port_mappings[*]}
|
||||
"
|
||||
|
||||
# DON'T wrap $docker_port_args in quotes. We want bash to split args by
|
||||
# whitespace
|
||||
docker run \
|
||||
--net bridge \
|
||||
-h "$host_name" \
|
||||
-e "TZ=America/New_York" \
|
||||
--cap-add=SYS_PTRACE \
|
||||
--security-opt seccomp=unconfined \
|
||||
--name "$cont_name" \
|
||||
-v "$home_src:$home_tgt" \
|
||||
-v "$repo_src:$repo_tgt" \
|
||||
-w "$working_dir" \
|
||||
${docker_port_args[*]} \
|
||||
--shm-size=4024m \
|
||||
-t \
|
||||
-i \
|
||||
-d \
|
||||
-e DISPLAY="$DISPLAY" \
|
||||
"$image_name"
|
||||
|
||||
# Add user w/ same uid and gid as the current user outside of docker, so the
|
||||
# user inside the docker has file permissions in the mounted dir.
|
||||
docker_group="$docker_user"
|
||||
docker_uid="$(id -u "$USER")"
|
||||
docker_gid="$(id -g "$USER")"
|
||||
if [[ -z "$docker_pass" ]]; then
|
||||
docker_pass="$docker_user"
|
||||
fi
|
||||
docker exec -u root -it "$cont_name" "$repo_tgt/docker/sparkd/dev/set_user.sh" \
|
||||
--user "$docker_user" \
|
||||
--uid "$docker_uid" \
|
||||
--group "$docker_group" \
|
||||
--gid "$docker_gid" \
|
||||
--pass "$docker_pass"
|
||||
|
||||
# Run same script VS Code runs to initialize the container
|
||||
docker exec -u "$docker_user" -it "$cont_name" "$repo_tgt/docker/sparkd/dev/devcontainer_post_create.sh"
|
||||
Executable
+64
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
function print_help_and_exit() {
|
||||
cat <<EOF
|
||||
Open a shell inside the docker container
|
||||
Usage: docker_shell.sh [OPTIONS] [CONTAINER]
|
||||
|
||||
Options:
|
||||
-u,--user The user to login with in the container (defaults to the current user)
|
||||
-c,--container The container name (may also be specified as positional arg)
|
||||
-h,--help Print this help message and exit
|
||||
|
||||
EOF
|
||||
exit "$1"
|
||||
}
|
||||
|
||||
|
||||
declare docker_user=""
|
||||
declare cont_name=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-u)
|
||||
;&
|
||||
--user)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "User (-u,--user) must be non-empty"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
docker_user="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h)
|
||||
;&
|
||||
--help)
|
||||
print_help_and_exit 0
|
||||
;;
|
||||
-c)
|
||||
;&
|
||||
--container)
|
||||
shift 1
|
||||
;&
|
||||
*)
|
||||
if [[ ! -z "$cont_name" ]]; then
|
||||
echo "Unexpected argument '$1'"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
cont_name="$1"
|
||||
shift 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
if [[ -z "$cont_name" ]]; then
|
||||
echo "Please specify the container ([-c,--container])"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$docker_user" ]]; then
|
||||
docker_user="$USER"
|
||||
fi
|
||||
|
||||
docker exec -u "$docker_user" -it "$cont_name" bash
|
||||
Executable
+233
@@ -0,0 +1,233 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
mount_prefix="$HOME/.spark-dev/mounts"
|
||||
|
||||
function print_help_and_exit() {
|
||||
cat <<EOF
|
||||
Start a docker dev container
|
||||
Usage: start_docker.sh [OPTIONS] image container
|
||||
|
||||
Positionals:
|
||||
image The image to instantiate
|
||||
container The name for the container
|
||||
|
||||
Options:
|
||||
-u,--user USER The user to add in the container (defaults to the current user)
|
||||
-p,--pass PASS The password for USER in the container (defaults to USER)
|
||||
-i,--image IMAGE The image to instantiate
|
||||
-c,--container CONT The name for the container
|
||||
-m,--mount MOUNT The name for the folder (outside the container) to create/mount to the home
|
||||
directory in the container. Mount folders are added here: $mount_prefix
|
||||
--mount-existing Mount the specified dir even if it already exists
|
||||
--port <cont_port>:<host_port> OR [<cont_port>:<host_port>,...] Port mapping(s). May be
|
||||
specified multiple times.
|
||||
-h,--help Print this help message and exit
|
||||
|
||||
EOF
|
||||
exit "$1"
|
||||
}
|
||||
|
||||
|
||||
declare docker_user=""
|
||||
declare docker_pass=""
|
||||
declare image_name=""
|
||||
declare cont_name=""
|
||||
declare mount_name=""
|
||||
declare -i mount_existing=0
|
||||
declare -a port_mappings=()
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-u)
|
||||
;&
|
||||
--user)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "User (-u,--user) must be non-empty"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
docker_user="$2"
|
||||
shift 2
|
||||
;;
|
||||
-p)
|
||||
;&
|
||||
--pass)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "Password (-p,--pass) must be non-empty"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
docker_pass="$2"
|
||||
shift 2
|
||||
;;
|
||||
-i)
|
||||
;&
|
||||
--image)
|
||||
if [[ -n "$image_name" ]]; then
|
||||
echo "Unexpected argument '$1'"
|
||||
print_help_and_exit 1
|
||||
elif [[ -z "$2" ]]; then
|
||||
echo "Image (-i,--image) must be non-empty"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
image_name="$2"
|
||||
shift 2
|
||||
;;
|
||||
-c)
|
||||
;&
|
||||
--container)
|
||||
if [[ -n "$cont_name" ]]; then
|
||||
echo "Unexpected argument '$1'"
|
||||
print_help_and_exit 1
|
||||
elif [[ -z "$2" ]]; then
|
||||
echo "Container (-c,--container) must be non-empty"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
cont_name="$2"
|
||||
shift 2
|
||||
;;
|
||||
--port)
|
||||
# Parse port(s) arg, and append the result to 'port_mappings'
|
||||
# Note: port(s) may be specified in bracketed notation:
|
||||
# [a:b,c:d,...,y:z]
|
||||
next_ports="$(echo "$2" | sed -e 's/\[//g' -e 's/\]//g' -e 's/,/ /g')"
|
||||
if [[ "${#port_mappings[@]}" -gt 0 ]]; then
|
||||
next_ports=" $next_ports"
|
||||
fi
|
||||
port_mappings+=("$next_ports")
|
||||
shift 2
|
||||
;;
|
||||
-m)
|
||||
;&
|
||||
--mount)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "Mount (-m,--mount) must be non-empty"
|
||||
fi
|
||||
mount_name="$2"
|
||||
shift 2
|
||||
;;
|
||||
--mount-existing)
|
||||
mount_existing=1
|
||||
shift 1
|
||||
;;
|
||||
-h)
|
||||
;&
|
||||
--help)
|
||||
print_help_and_exit 0
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$1" ]]; then
|
||||
echo "Invalid positional argument '$1'"
|
||||
print_help_and_exit 1
|
||||
elif [[ -z "$image_name" ]]; then
|
||||
image_name="$1"
|
||||
elif [[ -z "$cont_name" ]]; then
|
||||
cont_name="$1"
|
||||
else
|
||||
echo "Unexpected argument '$1'"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
shift 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
if [[ -z "$image_name" ]]; then
|
||||
image_name="bluetradingsystems/spark-crypto-dev"
|
||||
fi
|
||||
|
||||
host_name="$(hostname)"
|
||||
if [[ -n "$cont_name" ]]; then
|
||||
host_name="$host_name.$cont_name"
|
||||
fi
|
||||
|
||||
if [[ -z "$mount_name" ]]; then
|
||||
mount_name="$cont_name"
|
||||
fi
|
||||
|
||||
if [[ -z "$mount_name" && -z "$cont_name" ]]; then
|
||||
echo "Please specify mount (-m,--mount) and/or container (-c,--container)"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$docker_user" ]]; then
|
||||
docker_user="$USER"
|
||||
fi
|
||||
|
||||
home_src=$mount_prefix/$mount_name
|
||||
home_tgt=$HOME
|
||||
repo_src=$(git rev-parse --show-superproject-working-tree)
|
||||
repo_tgt=$HOME/$(basename "$repo_src")
|
||||
working_dir=$HOME
|
||||
|
||||
if [[ $mount_existing -eq 0 && -e "$home_src" ]]; then
|
||||
# Don't overwrite existing dir (might be mounted in another container)
|
||||
echo "Mount dir '$home_src' already exists (use --mount-existing to mount an existing directory)"
|
||||
print_help_and_exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$home_src"
|
||||
|
||||
if [[ "${#port_mappings[@]}" -eq 0 ]]; then
|
||||
port_mappings=("8000:8000" "8001:8001" "8002:8002" "8003:8003")
|
||||
fi
|
||||
|
||||
# Adapt port_mappings into args for `docker run` by prepending "-p " to each
|
||||
# item
|
||||
declare -a docker_port_args=()
|
||||
for port_mapping in "${port_mappings[@]}"; do
|
||||
docker_port_args+=("-p $port_mapping")
|
||||
done
|
||||
|
||||
echo "Starting container '$cont_name'
|
||||
image_name : $image_name
|
||||
cont_name : $cont_name
|
||||
home_src : $home_src
|
||||
home_tgt : $home_tgt
|
||||
repo_src : $repo_src
|
||||
repo_tgt : $repo_tgt
|
||||
working dir : $working_dir
|
||||
ports : ${port_mappings[*]}
|
||||
"
|
||||
|
||||
# DON'T wrap $docker_port_args in quotes. We want bash to split args by
|
||||
# whitespace
|
||||
docker run \
|
||||
--net bridge \
|
||||
-h "$host_name" \
|
||||
-e "TZ=America/New_York" \
|
||||
--cap-add=SYS_PTRACE \
|
||||
--security-opt seccomp=unconfined \
|
||||
--name "$cont_name" \
|
||||
-v "$home_src:$home_tgt" \
|
||||
-v "$repo_src:$repo_tgt" \
|
||||
-w "$working_dir" \
|
||||
${docker_port_args[*]} \
|
||||
--shm-size=4024m \
|
||||
-t \
|
||||
-i \
|
||||
-d \
|
||||
-e DISPLAY="$DISPLAY" \
|
||||
"$image_name" \
|
||||
bash
|
||||
|
||||
# Add user w/ same uid and gid as the current user outside of docker, so the
|
||||
# user inside the docker has file permissions in the mounted dir.
|
||||
docker_group="$docker_user"
|
||||
docker_user_uid="$(id -u "$USER")"
|
||||
docker_user_gid="$(id -g "$USER")"
|
||||
|
||||
echo "Adding user in container '$cont_name'
|
||||
name : $docker_user
|
||||
uid : $docker_user_uid
|
||||
gid : $docker_user_gid"
|
||||
|
||||
if [[ -z "$docker_pass" ]]; then
|
||||
docker_pass="$docker_user"
|
||||
echo "Giving user '$docker_user' a secure password: $docker_pass"
|
||||
fi
|
||||
|
||||
docker exec -u root -it "$cont_name" groupadd -g "$docker_user_gid" "$docker_group"
|
||||
docker exec -u root -it "$cont_name" useradd -u "$docker_user_uid" -g "$docker_user_gid" "$docker_user"
|
||||
docker exec -u root -it "$cont_name" usermod --password "$(openssl passwd -1 "$docker_pass")" "$docker_user"
|
||||
docker exec -u root -it "$cont_name" usermod -aG sudo "$docker_user"
|
||||
docker exec -u root -it "$cont_name" "$repo_tgt/externals/src/emumba/install_emumba-deribit-connector.sh"
|
||||
+1
-1
Submodule externals updated: 4a3e40193e...101e97a20c.
+2
-2
@@ -14,7 +14,7 @@ namespace mbts.spark {
|
||||
class VolSurfaceAdvancedSettingsEditorVM : ViewModelBase {
|
||||
readonly VolSurfaceAdvancedSettings _settings;
|
||||
|
||||
public VolSurfaceAdvancedSettingsEditorVM(VolSurfaceAdvancedSettings settings, string vs_name, Action<VolSurfaceAdvancedSettings> commit) {
|
||||
public VolSurfaceAdvancedSettingsEditorVM(VolSurfaceAdvancedSettings settings, string vs_name) {
|
||||
_settings = settings;
|
||||
_settings.theo_handle_measure = VolSurfaceEnums.GeneralChangeMeasure.Absolute;
|
||||
_settings.slide_handle_measure = VolSurfaceEnums.GeneralChangeMeasure.Percentage;
|
||||
@@ -23,7 +23,7 @@ namespace mbts.spark {
|
||||
if (_settings.precomputed_theo_handles == null)
|
||||
_settings.precomputed_theo_handles = new List<double>();
|
||||
vol_surface_name = vs_name;
|
||||
this.commit = new DelegateCommand(() => request_commit?.Invoke(ProtobufSerializationHelper.clone(_settings)));
|
||||
commit = new DelegateCommand(() => request_commit?.Invoke(ProtobufSerializationHelper.clone(_settings)));
|
||||
}
|
||||
|
||||
public event Action<VolSurfaceAdvancedSettings> request_commit;
|
||||
|
||||
@@ -121,21 +121,24 @@ namespace mbts.spark {
|
||||
if (settings == null) {
|
||||
return null;
|
||||
}
|
||||
// return new VolSurfaceAdvancedSettingsEditor {
|
||||
// DataContext = new VolSurfaceAdvancedSettingsEditorVM(
|
||||
// VolSurfaceUtilityFunctions.clone_proto(settings.advanced_settings),
|
||||
// settings.core_settings.vol_surface_name,
|
||||
// advanced_settings => {
|
||||
// var current_settings = _get_volsurface_settings();
|
||||
// if (current_settings == null) BTSMessageBox.Show("Error changing settings", "VS Settings", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error, true, true, true);
|
||||
// else {
|
||||
// current_settings.advanced_settings = advanced_settings;
|
||||
// _model.set_candidate_settings(current_settings);
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
// };
|
||||
return null;
|
||||
var vm = new VolSurfaceAdvancedSettingsEditorVM(
|
||||
VolSurfaceUtilityFunctions.clone_proto(settings.advanced_settings),
|
||||
settings.core_settings.vol_surface_name);
|
||||
vm.request_commit += commit_advanced_settings;
|
||||
return new VolSurfaceAdvancedSettingsEditor {
|
||||
DataContext = vm
|
||||
};
|
||||
|
||||
async void commit_advanced_settings(VolSurfaceAdvancedSettings value) {
|
||||
var current_settings = await _get_volsurface_settings();
|
||||
if (current_settings == null) {
|
||||
BTSMessageBox.Show("Error changing settings", "VS Settings");
|
||||
}
|
||||
else {
|
||||
current_settings.advanced_settings = value;
|
||||
_model.set_candidate_settings(current_settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Window? mk_grid_settings_win() {
|
||||
|
||||
@@ -9,26 +9,34 @@ namespace mbts.analytics
|
||||
[ProtoBuf.ProtoContract(Name = "VolSurfaceAdvancedSettings")]
|
||||
public class VolSurfaceAdvancedSettings
|
||||
{
|
||||
public static VolSurfaceAdvancedSettings Default =>
|
||||
new() {
|
||||
number_of_cashed_saved_days = double.NaN,
|
||||
precomputed_theo_handles = new(0),
|
||||
theo_handle_measure = VolSurfaceEnums.GeneralChangeMeasure.Absolute,
|
||||
precomputed_slide_handles = new(0),
|
||||
slide_handle_measure = VolSurfaceEnums.GeneralChangeMeasure.Percentage,
|
||||
publish_from_client = false,
|
||||
publish_from_client_user = string.Empty,
|
||||
strike_normalization_axis = VolSurfaceEnums.VolCurveStrikeAxis.RawStrike,
|
||||
};
|
||||
|
||||
[ProtoBuf.ProtoMember(1)]
|
||||
[System.ComponentModel.DefaultValue(Double.NaN)]
|
||||
public double number_of_cashed_saved_days;
|
||||
|
||||
[ProtoBuf.ProtoMember(10)]
|
||||
public List<double> precomputed_theo_handles;
|
||||
|
||||
[ProtoBuf.ProtoMember(15)]
|
||||
[System.ComponentModel.DefaultValue(VolSurfaceEnums.GeneralChangeMeasure.Absolute)]
|
||||
public VolSurfaceEnums.GeneralChangeMeasure theo_handle_measure;
|
||||
|
||||
[ProtoBuf.ProtoMember(20)]
|
||||
public List<double> precomputed_slide_handles;
|
||||
|
||||
[ProtoBuf.ProtoMember(25)]
|
||||
[System.ComponentModel.DefaultValue(VolSurfaceEnums.GeneralChangeMeasure.Percentage)]
|
||||
public VolSurfaceEnums.GeneralChangeMeasure slide_handle_measure;
|
||||
|
||||
[ProtoBuf.ProtoMember(30)]
|
||||
//[System.ComponentModel.DefaultValue(false)]
|
||||
public bool publish_from_client;
|
||||
|
||||
[ProtoBuf.ProtoMember(40)]
|
||||
@@ -38,5 +46,11 @@ namespace mbts.analytics
|
||||
[ProtoBuf.ProtoMember(50)]
|
||||
[System.ComponentModel.DefaultValue(VolSurfaceEnums.VolCurveStrikeAxis.RawStrike)]
|
||||
public VolSurfaceEnums.VolCurveStrikeAxis strike_normalization_axis;
|
||||
|
||||
public void ensure_no_nulls() {
|
||||
precomputed_theo_handles ??= new(0);
|
||||
precomputed_slide_handles ??= new(0);
|
||||
publish_from_client_user ??= string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,8 @@ namespace mbts.analytics {
|
||||
volpath_settings = new(),
|
||||
safety_limits = new(),
|
||||
},
|
||||
polynomial_model_settings = new()
|
||||
polynomial_model_settings = new(),
|
||||
advanced_settings = VolSurfaceAdvancedSettings.Default
|
||||
};
|
||||
|
||||
[ProtoMember(10)]
|
||||
@@ -79,6 +80,8 @@ namespace mbts.analytics {
|
||||
if (core_settings.time_settings == null) {
|
||||
core_settings.time_settings = new VolSurfaceTimeSettings();
|
||||
}
|
||||
advanced_settings ??= VolSurfaceAdvancedSettings.Default;
|
||||
advanced_settings.ensure_no_nulls();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,10 +40,8 @@ namespace mbts.analytics {
|
||||
/// last computed min/max driver for each exp
|
||||
private IDictionary<VolSurfaceExpiration, Tuple<double, double>>
|
||||
_computed_driver_range_map = new Dictionary<VolSurfaceExpiration, Tuple<double, double>>();
|
||||
private readonly IEnumerable<double> _default_abs_moves =
|
||||
new double[] { 300, -200, -125, -100, -75, -50, -25, 0, 25, 50, 75, 100, 125, 200, 300 };
|
||||
private readonly IEnumerable<double> _default_abs_moves = config.Configuration.get<bts.config.VolSurface>().default_abs_moves;
|
||||
private readonly double _cushion_percentage = 25.0;
|
||||
|
||||
public event
|
||||
Action<IDictionary<VolSurfaceExpiration, IEnumerable<Theos>>, double, double> on_theos =
|
||||
(o, vt, tt) => { };
|
||||
@@ -52,7 +50,7 @@ namespace mbts.analytics {
|
||||
(o, vt, tt) => { };
|
||||
|
||||
public VolSurfaceInterpGreeksProducer(int vs_id) {
|
||||
Log.Gen.info($"VolSurfaceInterpGreeksProducer.cons vs_id: {vs_id}");
|
||||
Log.Gen.info($"VolSurfaceInterpGreeksProducer.cons vs_id: {vs_id}, _default_abs_moves: {string.Join(", ", _default_abs_moves)}");
|
||||
_vs_id = vs_id;
|
||||
}
|
||||
|
||||
@@ -72,7 +70,7 @@ namespace mbts.analytics {
|
||||
}
|
||||
|
||||
public void set_vol_data(VolSurfaceInput vs_input) {
|
||||
Log.Gen.info($"VolSurfaceInterpGreeksProducer.set_vol_data vs_id: {_vs_id}");
|
||||
Log.Gen.info($"VolSurfaceInterpGreeksProducer.set_vol_data vs_id: {_vs_id} precomputed_theo_handles?: {string.Join(", ", (vs_input?.surface_settings?.advanced_settings?.precomputed_theo_handles).empty_if_null())}");
|
||||
_vs_input = vs_input != null ? VolSurfaceUtilityFunctions.clone_proto(vs_input) : null;
|
||||
_compute_and_fire(InputType.Vol);
|
||||
}
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<Window
|
||||
x:Class="mbts.ui.shell.VolSurfaceAdvancedSettingsEditor"
|
||||
x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:cv="clr-namespace:mbts.ui.converters"
|
||||
Title="{Binding vol_surface_name, StringFormat='{}Advanced Settings \'{0}\''}"
|
||||
Style="{StaticResource BTSDialog}"
|
||||
SizeToContent="WidthAndHeight"
|
||||
TextElement.FontSize="10">
|
||||
<StackPanel>
|
||||
<Grid Margin="5" Grid.IsSharedSizeScope="True">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="label"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<GroupBox Grid.ColumnSpan="2" Padding="3">
|
||||
<GroupBox.Header>
|
||||
<TextBlock Text="Theos" FontWeight="Bold"/>
|
||||
</GroupBox.Header>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="label"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<c:RadioButtonsControl Grid.ColumnSpan="2" Margin="0,0,0,5" ItemsSource="{Binding general_change_measures}" SelectedItem="{Binding theo_handle_measure}" IsEnabled="False"/>
|
||||
<TextBlock Grid.Row="1" Text="Precomputed Handles" VerticalAlignment="Center"/>
|
||||
<TextBox
|
||||
x:Name="PART_Handles"
|
||||
Validation.ErrorTemplate="{x:Null}"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="160"
|
||||
Margin="5,0,0,0"
|
||||
Text="{Binding precomputed_theo_handles, Converter={x:Static cv:EnumerableDoubleStringConverter.Instance}}" />
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
<TextBlock Visibility="Collapsed" Grid.Row="1" VerticalAlignment="Center" Margin="5,0,0,0" Text="Num Cash Saved Days" />
|
||||
<c:TickUpDownDouble Visibility="Collapsed" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch" Margin="14,5,5,5" Value="{Binding number_of_cash_saved_days}"/>
|
||||
<TextBlock Grid.Row="2" VerticalAlignment="Center" Margin="5,0,0,0" Text="Strike Normalization Axis"/>
|
||||
<ComboBox Grid.Row="2" Grid.Column="1" HorizontalAlignment="Stretch" Margin="14,5,5,5" ItemsSource="{Binding strike_normalization_axes}" SelectedValue="{Binding selected_strike_normalization_axis}"/>
|
||||
<CheckBox Grid.Row="3" Margin="7,0,0,0" Content="Publish From Client" IsChecked="{Binding publish_from_client}"/>
|
||||
</Grid>
|
||||
<c:DialogSkirtOKCancel OKCommand="{Binding commit}" />
|
||||
</StackPanel>
|
||||
</Window>
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
namespace mbts.ui.shell {
|
||||
using System.Windows;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction logic for VolSurfaceAdvancedSettingsEditor.xaml
|
||||
/// </summary>
|
||||
partial class VolSurfaceAdvancedSettingsEditor : Window {
|
||||
public VolSurfaceAdvancedSettingsEditor() =>
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
VolSurface {
|
||||
value {
|
||||
default_abs_moves: -600
|
||||
default_abs_moves: -300
|
||||
default_abs_moves: -200
|
||||
default_abs_moves: -125
|
||||
default_abs_moves: -100
|
||||
default_abs_moves: -75
|
||||
default_abs_moves: -50
|
||||
default_abs_moves: -25
|
||||
default_abs_moves: 0
|
||||
default_abs_moves: 25
|
||||
default_abs_moves: 50
|
||||
default_abs_moves: 75
|
||||
default_abs_moves: 100
|
||||
default_abs_moves: 125
|
||||
default_abs_moves: 200
|
||||
default_abs_moves: 300
|
||||
default_abs_moves: 600
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
syntax = 'proto2';
|
||||
package bts.config;
|
||||
|
||||
message VolSurface {
|
||||
repeated double default_abs_moves = 1;
|
||||
}
|
||||
@@ -300,6 +300,9 @@ extern "C" void init_execution_tester_plugin(bts::SparkServices &srv) {
|
||||
extern "C" void init_task_eye_plugin(bts::SparkServices &srv) {
|
||||
DynamicOrderDescriptor tasking_eye_algo;
|
||||
tasking_eye_algo.set_dynamic_order_type("Tasking Eye");
|
||||
// if this is set to true, it waits for the existance of Signal File
|
||||
*tasking_eye_algo.add_param_desc() = param_bool("Wait For Signal");
|
||||
*tasking_eye_algo.add_param_desc() = param_string("Signal File");
|
||||
*tasking_eye_algo.add_param_desc() = param_string("Account");
|
||||
*tasking_eye_algo.add_param_desc() = param_double("Edge");
|
||||
*tasking_eye_algo.add_param_desc() = param_double("Shot Size");
|
||||
|
||||
@@ -23,10 +23,17 @@
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include <bits/stdint-uintn.h>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <chrono>
|
||||
#include <elf.h>
|
||||
#include <filesystem>
|
||||
#include <google/protobuf/timestamp.pb.h>
|
||||
#include <google/protobuf/util/time_util.h>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace bts::api::orderman;
|
||||
@@ -69,6 +76,11 @@ double eps = 0.0000001;
|
||||
bool eq(double d1, double d2) { return std::abs(d1 - d2) < eps; }
|
||||
bool greater_then_eq(double d1, double d2) { return eq(d1, d2) || d2 > d1; }
|
||||
|
||||
void sleep_log() {
|
||||
return;
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
}
|
||||
|
||||
// Returns the number of shots that are within the Shots Window
|
||||
size_t n_recent_shots(
|
||||
const std::vector<TEAShot> &shots, uint64_t window_nanos) {
|
||||
@@ -100,7 +112,91 @@ bool in_flight_orders(const std::vector<TEAShot> &shots) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::set<SecurityKey> parse_securities(const std::string &secs) {
|
||||
// returns the securities that match the criteria from the 2nd - 8th arg
|
||||
std::set<SecurityKey> match_securities(
|
||||
const std::vector<api::Security const *> &secs,
|
||||
api::Venue venue,
|
||||
const std::string &symbol,
|
||||
uint32_t exp_month,
|
||||
uint32_t exp_day,
|
||||
uint32_t exp_yr,
|
||||
std::tuple<double, double> strike_range,
|
||||
api::PutOrCall cp) {
|
||||
std::set<SecurityKey> rv = {};
|
||||
double strike_min = std::get<0>(strike_range);
|
||||
double strike_max = std::get<1>(strike_range);
|
||||
for (const auto &s : secs) {
|
||||
if (s->exchange() == venue && s->has_option() && s->symbol() == symbol) {
|
||||
const auto &oc = s->option();
|
||||
if (oc.call_put() == cp && oc.strike() >= strike_min &&
|
||||
oc.strike() <= strike_max && oc.expiration_year() == exp_yr &&
|
||||
oc.expiration_month() == exp_month &&
|
||||
oc.expiration_day() == exp_day)
|
||||
rv.insert(SecurityKey(s->id()));
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Parses the sec expressed as text in the secs string.
|
||||
// the format is symbol:exp:call/put:strike range, ex
|
||||
// BTC:08/14/2023:Call:25000-30000
|
||||
std::set<SecurityKey> parse_securities_from_range(
|
||||
const std::string &secs,
|
||||
const std::vector<api::Security const *> &sec_vec) {
|
||||
LOG(OrderMan, Info,
|
||||
"TaskingEyeAlgo::parse_securities_from_range secs: {} sec_vec: {}", secs,
|
||||
sec_vec.size());
|
||||
sleep_log();
|
||||
if (secs.empty())
|
||||
return {};
|
||||
auto parse_exp = [](const std::string &exp_str) {
|
||||
LOG(OrderMan, Info,
|
||||
"TaskingEyeAlgo::parse_securities_from_range parse_exp exp: {}",
|
||||
exp_str);
|
||||
std::vector<std::string> exp_parts;
|
||||
boost::split(exp_parts, exp_str, boost::is_any_of("/"));
|
||||
LOG(OrderMan, Info,
|
||||
"TaskingEyeAlgo::parse_securities_from_range exp_parts: {}",
|
||||
exp_parts.size());
|
||||
uint32_t mm = static_cast<std::uint32_t>(std::stoul(exp_parts[0]));
|
||||
uint32_t dd = static_cast<std::uint32_t>(std::stoul(exp_parts[1]));
|
||||
uint32_t yy = static_cast<std::uint32_t>(std::stoul(exp_parts[2]));
|
||||
LOG(OrderMan, Info,
|
||||
"TaskingEyeAlgo::parse_securities_from_range {} mm: {} dd: {} yy: {}",
|
||||
mm, dd, yy);
|
||||
return std::tuple<uint32_t, uint32_t, uint32_t>(mm, dd, yy);
|
||||
};
|
||||
auto parse_strike_range = [](const std::string &strike_range_str) {
|
||||
std::vector<std::string> strike_parts;
|
||||
boost::split(strike_parts, strike_range_str, boost::is_any_of("-"));
|
||||
double k_min = std::stod(strike_parts[0]);
|
||||
double k_max = std::stod(strike_parts[1]);
|
||||
return std::tuple<double, double>(k_min, k_max);
|
||||
};
|
||||
std::vector<std::string> parts;
|
||||
boost::split(parts, secs, boost::is_any_of(":"));
|
||||
const std::string &symbol = parts[0];
|
||||
const std::string &exp_str = parts[1];
|
||||
const std::string &cp_str = parts[2];
|
||||
const auto &strike_range_str = parts[3];
|
||||
LOG(OrderMan, Info,
|
||||
"TaskingEyeAlgo::parse_securities_from_range symbol: {} exp: {} cp: {} "
|
||||
"stike_range: {}",
|
||||
symbol, exp_str, cp_str, strike_range_str);
|
||||
// debugging
|
||||
sleep_log();
|
||||
auto [mm, dd, yy] = parse_exp(exp_str);
|
||||
auto strike_range = parse_strike_range(strike_range_str);
|
||||
api::PutOrCall cp = (cp_str[0] == 'C' || cp_str[0] == 'c')
|
||||
? api::PutOrCall::PUTORCALL_CALL
|
||||
: api::PutOrCall::PUTORCALL_PUT;
|
||||
|
||||
return match_securities(
|
||||
sec_vec, api::Venue::VENUE_DERIBIT, symbol, mm, dd, yy, strike_range,
|
||||
cp);
|
||||
}
|
||||
std::set<SecurityKey> parse_securities_from_keys(const std::string &secs) {
|
||||
std::set<SecurityKey> rv = {};
|
||||
std::vector<std::string> parts;
|
||||
boost::split(parts, secs, boost::is_any_of(","));
|
||||
@@ -113,10 +209,34 @@ std::set<SecurityKey> parse_securities(const std::string &secs) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
std::set<SecurityKey> parse_securities(
|
||||
const std::string &secs,
|
||||
const std::vector<api::Security const *> &sec_vec) {
|
||||
bts_assert(!secs.empty());
|
||||
LOG(OrderMan, Info, "TaskingEyeAlgo::parse_securities sec: {}", secs);
|
||||
sleep_log();
|
||||
std::set<SecurityKey> rv = {};
|
||||
if (secs.find("/") != std::string::npos &&
|
||||
secs.find(":") != std::string::npos) {
|
||||
rv = parse_securities_from_range(secs, sec_vec);
|
||||
} else {
|
||||
rv = parse_securities_from_keys(secs);
|
||||
}
|
||||
std::stringstream ss;
|
||||
for (auto k : rv) {
|
||||
ss << k << " ";
|
||||
}
|
||||
LOG(OrderMan, Info, "TaskingEyeAlgo::parse_securities sec: {} rv: {}", secs,
|
||||
ss.str());
|
||||
return rv;
|
||||
}
|
||||
|
||||
struct TaskingEyeAlgo ::PImpl {
|
||||
IAlgoServices *service_ptr = nullptr;
|
||||
analytics::ITheoCalculator const *theo_calc = nullptr;
|
||||
TEAState state = TEAState::ACTIVE;
|
||||
bool wait_for_signal = false;
|
||||
std::string signal_file = "";
|
||||
double edge = 100000;
|
||||
double shot_size = 0;
|
||||
double target_qty = 0;
|
||||
@@ -150,31 +270,62 @@ TaskingEyeAlgo::~TaskingEyeAlgo() {}
|
||||
OrderOpResult TaskingEyeAlgo::do_before_launch(
|
||||
IBeforeLaunchAlgoServices &serv) {
|
||||
auto is_data_valid = [&]() {
|
||||
return greater_then_eq(0.1, me->shot_size) &&
|
||||
greater_then_eq(1, me->target_qty) &&
|
||||
greater_then_eq(1, me->max_shots_per_window) &&
|
||||
greater_then_eq(1000000000, me->window_size_nanos) &&
|
||||
me->side != api::SIDE_UNDEFINED;
|
||||
bool rv = greater_then_eq(0.1, me->shot_size) &&
|
||||
greater_then_eq(1, me->target_qty) &&
|
||||
greater_then_eq(1, me->max_shots_per_window) &&
|
||||
greater_then_eq(1000000000, me->window_size_nanos) &&
|
||||
me->side != api::SIDE_UNDEFINED;
|
||||
bool signal_correct = !me->wait_for_signal || !me->signal_file.empty();
|
||||
rv = rv && signal_correct;
|
||||
return rv;
|
||||
};
|
||||
LOG(OrderMan, Info,
|
||||
"TaskingEyeAlgo::do_before_launch order_key: {} security: {} "
|
||||
"definition: {}",
|
||||
serv.order_key().value(), utils::pb::to_string(serv.security()),
|
||||
utils::pb::to_string(serv.definition()));
|
||||
sleep_log();
|
||||
auto &def = serv.definition();
|
||||
if (def.has_custom_def() && def.custom_def().has_dynamic()) {
|
||||
const auto &d_params = def.custom_def().dynamic().order_params();
|
||||
auto wait_for_signal = get_if<bool>(d_params, "Wait For Signal");
|
||||
me->wait_for_signal =
|
||||
wait_for_signal.has_value() ? wait_for_signal.value() : false;
|
||||
auto signal_file = get_if<std::string>(d_params, "Signal File");
|
||||
me->signal_file = signal_file.has_value() ? signal_file.value() : "";
|
||||
me->edge = get_if<double>(d_params, "Edge").value();
|
||||
LOG(OrderMan, Info, "TaskingEyeAlgo::do_before_launch edge: {}",
|
||||
me->edge);
|
||||
sleep_log();
|
||||
me->shot_size = get_if<double>(d_params, "Shot Size").value();
|
||||
LOG(OrderMan, Info, "TaskingEyeAlgo::do_before_launch shot_size: {}",
|
||||
me->shot_size);
|
||||
sleep_log();
|
||||
me->target_qty = get_if<double>(d_params, "Target Quantity").value();
|
||||
LOG(OrderMan, Info, "TaskingEyeAlgo::do_before_launch target_qty: {}",
|
||||
me->target_qty);
|
||||
sleep_log();
|
||||
// TODO, make sure the side if right
|
||||
me->side = def.side();
|
||||
me->max_shots_per_window =
|
||||
get_if<uint32_t>(d_params, "Max Shots Per Window").value();
|
||||
LOG(OrderMan, Info,
|
||||
"TaskingEyeAlgo::do_before_launch max_shots_per_window: {}",
|
||||
me->max_shots_per_window);
|
||||
sleep_log();
|
||||
uint32_t window_size_secs =
|
||||
get_if<uint32_t>(d_params, "Window Size (secs)").value();
|
||||
LOG(OrderMan, Info,
|
||||
"TaskingEyeAlgo::do_before_launch window_size_secs: {}",
|
||||
window_size_secs);
|
||||
sleep_log();
|
||||
me->window_size_nanos = seconds_to_nanos(window_size_secs);
|
||||
auto sec_str = get_if<std::string>(d_params, "Securities").value();
|
||||
auto parsed_secs = parse_securities(sec_str);
|
||||
LOG(OrderMan, Info, "TaskingEyeAlgo::do_before_launch Securities: {}",
|
||||
sec_str);
|
||||
sleep_log();
|
||||
auto parsed_secs = parse_securities(sec_str, serv.known_securities());
|
||||
std::stringstream ss;
|
||||
std::vector<SecurityKey> final_secs;
|
||||
if (parsed_secs.empty()) {
|
||||
final_secs.push_back(SecurityKey{serv.security().id()});
|
||||
@@ -186,18 +337,21 @@ OrderOpResult TaskingEyeAlgo::do_before_launch(
|
||||
auto ct = get_tea_child_template(serv, sec_key, me->side);
|
||||
me->sec_map.insert({sec_key, {ct}});
|
||||
LOG(OrderMan, Info,
|
||||
"TaskingEyeAlgo::do_before_launch order_key: {} successfully "
|
||||
"TaskingEyeAlgo::do_before_launch order_key: {} "
|
||||
"successfully "
|
||||
"created ChildTemplate for sec_key: {}",
|
||||
serv.order_key(), sec_key);
|
||||
}
|
||||
LOG(OrderMan, Info,
|
||||
"TaskingEyeAlgo::do_before_launch algo constructed order_key: {} "
|
||||
"edge: {} shot_size: {} is_buy: {} target_qty: {} "
|
||||
"max_shots_per_window: {} window_size_nanos: {} n_secs: {}",
|
||||
"TaskingEyeAlgo::do_before_launch algo constructed order_key: "
|
||||
"{} "
|
||||
"edge: {} shot_size: {} side: {} target_qty: {} "
|
||||
"max_shots_per_window: {} window_size_nanos: {} n_secs: {} "
|
||||
"wait_for_signal: {} signal_file: {}",
|
||||
serv.order_key(), me->edge, me->shot_size,
|
||||
me->side == api::SIDE_BUY, me->target_qty,
|
||||
bts::api::Side_Name(me->side), me->target_qty,
|
||||
me->max_shots_per_window, me->window_size_nanos,
|
||||
me->sec_map.size());
|
||||
me->sec_map.size(), me->wait_for_signal, me->signal_file);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -213,7 +367,8 @@ void TaskingEyeAlgo::do_launch(IAlgoServices &serv) {
|
||||
for (const auto &kv : me->sec_map) {
|
||||
auto sec_key = kv.first;
|
||||
LOG(OrderMan, Info,
|
||||
"TaskingEyeAlgo::do_before_launch subscribing book for sec_key: {}",
|
||||
"TaskingEyeAlgo::do_before_launch subscribing book for sec_key: "
|
||||
"{}",
|
||||
sec_key);
|
||||
me->service_ptr->subscribe_book(sec_key);
|
||||
}
|
||||
@@ -315,9 +470,9 @@ void TaskingEyeAlgo::on_theos(
|
||||
if (!sec_data.driver_sec_key.has_value() ||
|
||||
sec_data.driver_sec_key->value() != driver.value()) {
|
||||
LOG(OrderMan, Info,
|
||||
"TaskingEyeAlgo::on_theos order_key: {} subscribing to driver "
|
||||
"sec_key: {}",
|
||||
me->service_ptr->order_key(), driver.value());
|
||||
"TaskingEyeAlgo::on_theos order_key: {} subscribing to driver for "
|
||||
"sec_key: {} driver_key: {}",
|
||||
me->service_ptr->order_key(), sec_key, driver.value());
|
||||
sec_data.driver_sec_key = SecurityKey(driver.value());
|
||||
sub_to_driver(SecurityKey(driver.value()));
|
||||
}
|
||||
@@ -331,8 +486,14 @@ void TaskingEyeAlgo::on_position(PositionUpdate const &pu) {
|
||||
}
|
||||
|
||||
void TaskingEyeAlgo::_evaluate_and_shoot() {
|
||||
if (me->state != TEAState::ACTIVE)
|
||||
if (me->state != TEAState::ACTIVE) {
|
||||
LOG(OrderMan, Warn,
|
||||
"TaskingEyeAlgo::_evaluate_and_shoot order_key: {} state is not "
|
||||
"active state: {} ",
|
||||
me->service_ptr->order_key(), me->state);
|
||||
return;
|
||||
}
|
||||
|
||||
auto max_shots_per_win_reached = [&]() {
|
||||
return n_recent_shots(me->shots, me->window_size_nanos) >=
|
||||
me->max_shots_per_window;
|
||||
@@ -343,12 +504,36 @@ void TaskingEyeAlgo::_evaluate_and_shoot() {
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
auto signal_condition_met = [&]() {
|
||||
if (me->wait_for_signal) {
|
||||
return std::filesystem::exists(me->signal_file);
|
||||
} else
|
||||
return true;
|
||||
};
|
||||
if (!signal_condition_met()) {
|
||||
// Log ?
|
||||
LOG(OrderMan, Warn,
|
||||
"TaskingEyeAlgo::_evaluate_and_shoot order_key: {} Waiting for "
|
||||
"Signal",
|
||||
me->service_ptr->order_key());
|
||||
return;
|
||||
}
|
||||
for (auto &kv : me->sec_map) {
|
||||
auto sec_key = kv.first;
|
||||
if (max_shots_per_win_reached())
|
||||
if (max_shots_per_win_reached()) {
|
||||
LOG(OrderMan, Warn,
|
||||
"TaskingEyeAlgo::_evaluate_and_shoot order_key: {} "
|
||||
"max_shots_per_win_reached",
|
||||
me->service_ptr->order_key());
|
||||
return;
|
||||
if (sec_has_active_order(sec_key))
|
||||
}
|
||||
if (sec_has_active_order(sec_key)) {
|
||||
LOG(OrderMan, Warn,
|
||||
"TaskingEyeAlgo::_evaluate_and_shoot sec_key: {} has working "
|
||||
"order, skipping",
|
||||
sec_key.value());
|
||||
continue;
|
||||
}
|
||||
Price price;
|
||||
Quantity qty;
|
||||
if (_in_shooting_zone(sec_key, price, qty)) {
|
||||
@@ -357,7 +542,8 @@ void TaskingEyeAlgo::_evaluate_and_shoot() {
|
||||
auto ch_ord_key =
|
||||
me->service_ptr->launch_child(*sec_data.order, price, qty);
|
||||
LOG(OrderMan, Info,
|
||||
"TakingEyeAlgo::_evaluate_and_shoot ord_key: {} child_ord_key: {} "
|
||||
"TakingEyeAlgo::_evaluate_and_shoot ord_key: {} child_ord_key: "
|
||||
"{} "
|
||||
"price: {} qty: {}",
|
||||
me->service_ptr->order_key(), ch_ord_key, price, qty.full_value());
|
||||
me->shots.push_back(TEAShot{sec_key, ch_ord_key});
|
||||
@@ -375,8 +561,13 @@ bool TaskingEyeAlgo::_in_shooting_zone(
|
||||
TEASecData const &sec_data = it->second;
|
||||
const md::PriceSize &driver_bid = sec_data.driver_book.bid().at(0);
|
||||
const md::PriceSize &driver_ask = sec_data.driver_book.ask().at(0);
|
||||
if (eq(driver_bid.size(), 0) || eq(driver_ask.size(), 0))
|
||||
if (eq(driver_bid.size(), 0) || eq(driver_ask.size(), 0)) {
|
||||
LOG(OrderMan, Warn,
|
||||
"TaskingEyeAlgo::_in_shooting_zone no driver for sec_key: {} "
|
||||
"driver_bid_qty: {} driver_ask_qty: {}",
|
||||
sec_key, driver_bid.size(), driver_ask.size());
|
||||
return false;
|
||||
}
|
||||
// finding weighted mean (TODO change it to mark price later)
|
||||
double driver = (driver_bid.price() * driver_bid.size() +
|
||||
driver_ask.price() * driver_ask.size()) /
|
||||
@@ -386,31 +577,55 @@ bool TaskingEyeAlgo::_in_shooting_zone(
|
||||
// TODO normalize theo
|
||||
double theo = ptp.theo();
|
||||
theo = theo / driver;
|
||||
if (me->side == api::SIDE_BUY) {
|
||||
// auto analyze_buy =
|
||||
auto analyze_buy = [&](const md::PriceSize &md_ask, double theo,
|
||||
size_t level, Price &price, Quantity &qty) -> bool {
|
||||
double ee_buy_pr = theo - me->edge;
|
||||
const md::PriceSize &md_ask = sec_data.sec_book.ask().at(0);
|
||||
LOG(OrderMan, Diag,
|
||||
"TaskingEyeAlgo::_in_shooting_zone sec_key: {} driver: {} theo: {} "
|
||||
"ee_price: {} md: {}/{}",
|
||||
sec_key, driver, theo, ee_buy_pr, md_ask.price(), md_ask.size());
|
||||
bool rv = false;
|
||||
if (greater_then_eq(1, md_ask.size()) &&
|
||||
greater_then_eq(md_ask.price(), ee_buy_pr)) {
|
||||
price = Price(md_ask.price());
|
||||
qty = std::min(static_cast<double>(md_ask.size()), me->shot_size);
|
||||
return true;
|
||||
qty = me->shot_size;
|
||||
rv = true;
|
||||
}
|
||||
} else {
|
||||
double ee_sell_pr = theo + me->edge;
|
||||
const md::PriceSize &md_bid = sec_data.sec_book.bid().at(0);
|
||||
LOG(OrderMan, Diag,
|
||||
"TaskingEyeAlgo::_in_shooting_zone sec_key: {} driver: {} theo: {} "
|
||||
"ee_price: {} md: {}/{}",
|
||||
sec_key, driver, theo, ee_sell_pr, md_bid.price(), md_bid.size());
|
||||
"TaskingEyeAlgo::_in_shooting_zone::analyze_buy sec_key: {} "
|
||||
"theo: {} level: {}"
|
||||
"ee_buy_pr: {} rv: {}",
|
||||
sec_key, level, theo, ee_buy_pr, rv);
|
||||
return rv;
|
||||
};
|
||||
auto analyze_sell = [&](const md::PriceSize &md_bid, double theo,
|
||||
size_t level, Price &price, Quantity &qty) -> bool {
|
||||
double ee_sell_pr = theo + me->edge;
|
||||
bool rv = false;
|
||||
if (greater_then_eq(1, md_bid.size()) &&
|
||||
greater_then_eq(ee_sell_pr, md_bid.price())) {
|
||||
price = Price(md_bid.price());
|
||||
qty = std::min(static_cast<double>(md_bid.size()), me->shot_size);
|
||||
return true;
|
||||
qty = me->shot_size;
|
||||
rv = true;
|
||||
}
|
||||
LOG(OrderMan, Diag,
|
||||
"TaskingEyeAlgo::_in_shooting_zone::analyze_sell sec_key: {} theo:"
|
||||
"{} level: {} ee_sell_pr: {} rv: {}",
|
||||
sec_key, theo, level, ee_sell_pr, rv);
|
||||
return rv;
|
||||
};
|
||||
if (me->side == api::SIDE_BUY) {
|
||||
const auto &ask_book = sec_data.sec_book.ask();
|
||||
for (int i = ask_book.size() - 1; i >= 0; i--) {
|
||||
const auto &md_ask = ask_book.at(i);
|
||||
if (analyze_buy(md_ask, theo, i, price, qty)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const auto &bid_book = sec_data.sec_book.bid();
|
||||
for (int i = bid_book.size() - 1; i >= 0; i--) {
|
||||
const md::PriceSize &md_bid = bid_book.at(i);
|
||||
if (analyze_sell(md_bid, theo, i, price, qty)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -24,7 +24,8 @@ class VisualStudioContext:
|
||||
def configure_libspark(self, build_dir) -> subprocess.CompletedProcess:
|
||||
with CWD(build_dir):
|
||||
res = subprocess.run(
|
||||
f'cmake.exe -G "Visual Studio {self.version} {self.year}" {SparkPaths.root()}'
|
||||
f'cmake.exe -G "Visual Studio {self.version} {self.year}" {SparkPaths.root()}',
|
||||
shell=True,
|
||||
)
|
||||
shutil.copy(
|
||||
SparkPaths.tools().joinpath('cpp.build.props'),
|
||||
|
||||
Reference in new issue
Block a user