From 73541fd90338fd60794bc274787301488b77fdea Mon Sep 17 00:00:00 2001 From: olli Date: Sun, 7 May 2023 13:17:06 +0200 Subject: [PATCH] docker-non-root, bitpanda trading, home for .ssh for ssh-to-signal messages --- .gitignore | 9 ++-- Dockerfile | 2 + README.md | 2 +- dabo/analyze.conf | 0 dabo/dabo-bot.conf | 4 +- dabo/dabo-bot.sh | 9 +++- dabo/functions/api-calls.sh | 14 ------- dabo/functions/binance-api-call.sh | 4 +- dabo/functions/check_buy_conditions.sh | 43 ++++++++++--------- dabo/functions/do_trade.sh | 58 ++++++++++++++++---------- dabo/functions/get_assets.sh | 5 +++ dabo/functions/get_balances.sh | 4 ++ dabo/functions/market_performance.sh | 2 +- dabo/functions/webpage.sh | 2 +- docker-compose.yml | 3 ++ 15 files changed, 88 insertions(+), 73 deletions(-) mode change 100644 => 100755 dabo/analyze.conf mode change 100644 => 100755 dabo/dabo-bot.conf delete mode 100644 dabo/functions/api-calls.sh diff --git a/.gitignore b/.gitignore index 1c64919..bfb6b4b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ docker-compose.override.yml -htdocs/botdata -htdocs/index.html -tests -.binance-secrets -.bitpanda-secrets +data/botdata +data/index.html +dabo/.binance-secrets +dabo/.bitpanda-secrets diff --git a/Dockerfile b/Dockerfile index dc2daa6..1774ee1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,5 +3,7 @@ RUN apt-get update && \ apt-get -y install curl && \ curl https://gitea.ds9.dedyn.io/olli/debian.ansible.docker/raw/branch/main/build-debian-env.sh >build-debian-env.sh && \ bash -ex build-debian-env.sh +RUN addgroup --system --gid 10000 dabo +RUN adduser --system --disabled-password --disabled-login --gid 10000 --uid 10000 --home /dabo/home dabo ENV LANG en_US.utf8 ENTRYPOINT ["/dabo/dabo-bot.sh"] diff --git a/README.md b/README.md index a3c18a3..3d4cab2 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ docker -l warn compose --ansi never build --progress=plain --pull --no-cache --f Run: ``` -docker-compose down # if on old instance is running +docker-compose down # if an old instance is running docker-compose up -d ``` diff --git a/dabo/analyze.conf b/dabo/analyze.conf old mode 100644 new mode 100755 diff --git a/dabo/dabo-bot.conf b/dabo/dabo-bot.conf old mode 100644 new mode 100755 index 628cdae..9b4e979 --- a/dabo/dabo-bot.conf +++ b/dabo/dabo-bot.conf @@ -9,8 +9,8 @@ FEE="0.4" INTERVAL="150" ## Currency used for trading -CURRENCY="USDT" -TRANSFER_CURRENCY="EUR" +CURRENCY="EUR" +TRANSFER_CURRENCY="NONE" # Only use currencies under the first X currencies sorted by market capitalization LARGEST_MARKETCAP="250" diff --git a/dabo/dabo-bot.sh b/dabo/dabo-bot.sh index 05ceba1..b9be5b2 100755 --- a/dabo/dabo-bot.sh +++ b/dabo/dabo-bot.sh @@ -5,6 +5,8 @@ g_lockfile +export LANGUAGE="en_US" + ### CONFIG ### BASEPATH=/dabo/htdocs @@ -20,6 +22,8 @@ done ### MAIN ### +g_signal-notify "STARTING DABO BOT $0" + # prepare directories mkdir -p ${BASEPATH}/botdata/asset-histories mkdir -p ${BASEPATH}/botdata/trade-histories @@ -49,12 +53,13 @@ do # stock data if [ ${STOCK_EXCHANGE} = "BINANCE" ] then - #BINANCE_CLI_CMD="docker-compose -f /home/docker/binance-cli/docker-compose.yml exec -T binance-cli binance-cli" # command for current token infos (function for setting var QUANTITY_LOT_CUT TOKEN_INFO_CMD="binance_get_token_info" # command for buying/selling a token - #TRADE_CMD="$BINANCE_CLI_CMD ACTION -s TOKEN -u QUANTITY -t market" TRADE_CMD='binance-api-call POST /api/v3/order "&symbol=TOKEN"eOrderQty=QUANTITY&side=ACTION&type=MARKET"' + elif [ ${STOCK_EXCHANGE} = "BITPANDA" ] + then + TRADE_CMD='bitpanda-api-call POST public/v1/account/orders "--header \"Content-Type: application/json\" --data \"{\\\"instrument_code\\\":\\\"TOKEN\\\",\\\"side\\\":\\\"ACTION\\\",\\\"type\\\":\\\"MARKET\\\",\\\"amount\\\":\\\"QUANTITY\\\"}\""' fi # Get current assets diff --git a/dabo/functions/api-calls.sh b/dabo/functions/api-calls.sh deleted file mode 100644 index 0a4c4b2..0000000 --- a/dabo/functions/api-calls.sh +++ /dev/null @@ -1,14 +0,0 @@ -function EXCHANGE_GET_ASSETS_CMD { - - if [ ${STOCK_EXCHANGE} = "BINANCE" ] - then - binance-api-call GET /api/v3/ticker/price || return 1 - # parse API output - cat ${g_tmp}/API_CMD_OUT | jq -r '.[] | .symbol + "," + .price' | grep "${CURRENCY}," | egrep -v "${TRANSFER_CURRENCY},|,0[\.][0]*$" | sort >${f_filename}_OUT.tmp - fi - - - - -} - diff --git a/dabo/functions/binance-api-call.sh b/dabo/functions/binance-api-call.sh index 48d51c5..08a6233 100644 --- a/dabo/functions/binance-api-call.sh +++ b/dabo/functions/binance-api-call.sh @@ -6,9 +6,9 @@ function binance-api-call { local call=$2 local params=$3 - if [ -s /home/docker/binance-cli/.binance-secrets ] + if [ -s /dabo/.binance-secrets ] then - . /home/docker/binance-cli/.binance-secrets + . /dabo/.binance-secrets else g_echo_error "No secrets file found" return 1 diff --git a/dabo/functions/check_buy_conditions.sh b/dabo/functions/check_buy_conditions.sh index 0c1f5bd..f0bb72c 100644 --- a/dabo/functions/check_buy_conditions.sh +++ b/dabo/functions/check_buy_conditions.sh @@ -129,31 +129,30 @@ ${f_BUY}" # remove CURRENCY from asset f_ASSET=$(echo ${f_ASSET} | sed "s/${CURRENCY}//") - - # get stock exchange specific infos for trade (e.g. MIN_NOTIONAL) - $TOKEN_INFO_CMD ${f_ASSET} ${CURRENCY} - # use MIN_NOTIONAL+5% as INVEST_QUANTITY if INVEST_QUANTITY is under MIN_NOTIONAL - # +5% in spite of MIN_NOTIONAL to be able to sell when the price falls a little bit - [ $(echo "${f_INVEST_QUANTITY} < ${f_MIN_NOTIONAL}" | bc -l) -eq 0 ] || f_INVEST_QUANTITY=$(echo "scale=2; $f_MIN_NOTIONAL/100*105" | bc -l) - - # if there is not enough balance for buying because ${f_MIN_NOTIONAL} needed for buying to sell (workaround) - if [ $(echo "${CURRENCY_BALANCE} < ${f_MIN_NOTIONAL}*2" | bc -l) -ne 0 ] - then - g_echo_note "BUY ${f_ASSET} not enough balance ${CURRENCY_BALANCE} for buying because of MIN_NOTIONAL (${f_MIN_NOTIONAL}*2) needed for buying-to-sell (workaround)" - return 1 - fi - - # continue if not balance enough for lowest quantity (MIN_NOTIONAL) - if [ $(echo "${CURRENCY_BALANCE} > ${f_INVEST_QUANTITY}" | bc -l) -eq 0 ] - then - g_echo_note "BUY ${f_ASSET} not enough balance (${CURRENCY_BALANCE}) for lowest quantity (MIN_NOTIONAL - ${f_INVEST_QUANTITY})" - return 1 - fi - - #g_echo_note "BUY BUY BUY ${f_ASSET} ${CURRENCY} ${f_INVEST_QUANTITY} buy \"$f_BUY\"" if [ ${STOCK_EXCHANGE} = "BINANCE" ] then + # get stock exchange specific infos for trade (e.g. MIN_NOTIONAL) + ${TOKEN_INFO_CMD} ${f_ASSET} ${CURRENCY} + + # use MIN_NOTIONAL+5% as INVEST_QUANTITY if INVEST_QUANTITY is under MIN_NOTIONAL + # +5% in spite of MIN_NOTIONAL to be able to sell when the price falls a little bit + [ $(echo "${f_INVEST_QUANTITY} < ${f_MIN_NOTIONAL}" | bc -l) -eq 0 ] || f_INVEST_QUANTITY=$(echo "scale=2; $f_MIN_NOTIONAL/100*105" | bc -l) + + # if there is not enough balance for buying because ${f_MIN_NOTIONAL} needed for buying to sell (workaround) + if [ $(echo "${CURRENCY_BALANCE} < ${f_MIN_NOTIONAL}*2" | bc -l) -ne 0 ] + then + g_echo_note "BUY ${f_ASSET} not enough balance ${CURRENCY_BALANCE} for buying because of MIN_NOTIONAL (${f_MIN_NOTIONAL}*2) needed for buying-to-sell (workaround)" + return 1 + fi + + # continue if not balance enough for lowest quantity (MIN_NOTIONAL) + if [ $(echo "${CURRENCY_BALANCE} > ${f_INVEST_QUANTITY}" | bc -l) -eq 0 ] + then + g_echo_note "BUY ${f_ASSET} not enough balance (${CURRENCY_BALANCE}) for lowest quantity (MIN_NOTIONAL - ${f_INVEST_QUANTITY})" + return 1 + fi + binance_convert ${f_ASSET} ${CURRENCY} ${f_INVEST_QUANTITY} buy "$f_BUY" || \ do_trade ${f_ASSET} ${CURRENCY} ${f_INVEST_QUANTITY} buy "$f_BUY" else diff --git a/dabo/functions/do_trade.sh b/dabo/functions/do_trade.sh index 7012efc..2674d95 100644 --- a/dabo/functions/do_trade.sh +++ b/dabo/functions/do_trade.sh @@ -14,21 +14,25 @@ function do_trade { local f_link="https://www.coingecko.com/de/munze/$(egrep -i ^${f_ASSET}, COINGECKO_IDS | cut -d, -f2)" - # get stock exchange specific infos for trade (e.g. f_QUANTITY_LOT_CUT; f_MIN_NOTIONAL) - $TOKEN_INFO_CMD $f_ASSET $f_CURRENCY $f_QUANTITY - local f_QUANTITY=${f_QUANTITY_LOT_CUT} - - #### Workaround for f_QUANTITY_LOT_CUT by buy more and directly sell - g_echo_note "Checking for need of QUANTITY_LOT_CUT Workaround (${f_QUANTITY_LOT_CUT} > ${f_MIN_NOTIONAL})" - if [ $(echo "${f_QUANTITY_LOT_CUT} > ${f_MIN_NOTIONAL}" | bc -l) -eq 0 ] && [ ${f_ACTION} = sell ] + + if [ ${STOCK_EXCHANGE} = "BINANCE" ] then - # add 0.5 to f_MIN_NOTIONAL and trading fee - local f_MIN_NOTIONAL_WITH_FEE=$(echo "${f_MIN_NOTIONAL}+${FEE}+0.5" | bc -l) - g_echo_note "Doing QUANTITY_LOT_CUT Workaround - buying ${f_MIN_NOTIONAL_WITH_FEE} ${f_ASSET} ${f_CURRENCY}" - do_trade ${f_ASSET} ${f_CURRENCY} ${f_MIN_NOTIONAL_WITH_FEE} buy "Workaround for selling values under QUANTITY_LOT_CUT - buy +MIN_NOTIONAL" || return 1 - local f_QUANTITY=$(echo "${f_QUANTITY}+${f_MIN_NOTIONAL}" | bc -l) - fi + # get stock exchange specific infos for trade (e.g. f_QUANTITY_LOT_CUT; f_MIN_NOTIONAL) + ${TOKEN_INFO_CMD} ${f_ASSET} ${f_CURRENCY} ${f_QUANTITY} + local f_QUANTITY=${f_QUANTITY_LOT_CUT} + #### Workaround for f_QUANTITY_LOT_CUT by buy more and directly sell + g_echo_note "Checking for need of QUANTITY_LOT_CUT Workaround (${f_QUANTITY_LOT_CUT} > ${f_MIN_NOTIONAL})" + if [ $(echo "${f_QUANTITY_LOT_CUT} > ${f_MIN_NOTIONAL}" | bc -l) -eq 0 ] && [ ${f_ACTION} = sell ] + then + # add 0.5 to f_MIN_NOTIONAL and trading fee + local f_MIN_NOTIONAL_WITH_FEE=$(echo "${f_MIN_NOTIONAL}+${FEE}+0.5" | bc -l) + g_echo_note "Doing QUANTITY_LOT_CUT Workaround - buying ${f_MIN_NOTIONAL_WITH_FEE} ${f_ASSET} ${f_CURRENCY}" + do_trade ${f_ASSET} ${f_CURRENCY} ${f_MIN_NOTIONAL_WITH_FEE} buy "Workaround for selling values under QUANTITY_LOT_CUT - buy +MIN_NOTIONAL" || return 1 + local f_QUANTITY=$(echo "${f_QUANTITY}+${f_MIN_NOTIONAL}" | bc -l) + fi + fi + if [ "${f_ACTION}" = "buy" ] then # check for enough balance for trade @@ -39,15 +43,16 @@ function do_trade { then local f_note="Not enough balance for trade (${f_CURRENCY_BALANCE} > ${f_QUANTITY}) :${f_COMMENT} ${FUNCNAME} $@" - g_echo_note "$f_note" - g_signal-notify "$f_note" + g_echo_note "${f_note}" + g_signal-notify "${f_note}" return 1 fi fi # prepare trading command local f_CMDFILE="trade-histories/${f_DATE}-${f_CURRENCY}-${f_MIN_NOTIONAL}-TRADE_CMD" - echo "${TRADE_CMD}" | perl -pe "s/ACTION/${f_ACTION}/; s/TOKEN/${f_ASSET}${f_CURRENCY}/; s/QUANTITY/${f_QUANTITY}/" >${f_CMDFILE} + [ ${STOCK_EXCHANGE} = "BINANCE" ] && echo "${TRADE_CMD}" | perl -pe "s/ACTION/${f_ACTION}/; s/TOKEN/${f_ASSET}${f_CURRENCY}/; s/QUANTITY/${f_QUANTITY}/" >${f_CMDFILE} + [ ${STOCK_EXCHANGE} = "BITPANDA" ] && echo "${TRADE_CMD}" | perl -pe "s/ACTION/${f_ACTION}/; s/TOKEN/${f_ASSET}_${f_CURRENCY}/; s/QUANTITY/${f_QUANTITY}/" >${f_CMDFILE} # trade g_echo_note "Command: $(cat ${f_CMDFILE})" #g_runcmd g_retrycmd sh ${f_CMDFILE} >${f_CMDFILE}_OUT @@ -66,18 +71,20 @@ ${FUNCNAME} $@" sleep 1 f_QUANTITY=$(echo "$f_QUANTITY-0.1" | bc -l | sed 's/^\./0./;') g_echo_note "lower $f_QUANTITY by -0.1" - echo "${TRADE_CMD}" | perl -pe "s/ACTION/${f_ACTION}/; s/TOKEN/${f_ASSET}${f_CURRENCY}/; s/QUANTITY/${f_QUANTITY}/" >${f_CMDFILE} + [ ${STOCK_EXCHANGE} = "BINANCE" ] && echo "${TRADE_CMD}" | perl -pe "s/ACTION/${f_ACTION}/; s/TOKEN/${f_ASSET}${f_CURRENCY}/; s/QUANTITY/${f_QUANTITY}/" >${f_CMDFILE} + [ ${STOCK_EXCHANGE} = "BITPANDA" ] && echo "${TRADE_CMD}" | perl -pe "s/ACTION/${f_ACTION}/; s/TOKEN/${f_ASSET}_${f_CURRENCY}/; s/QUANTITY/${f_QUANTITY}/" >${f_CMDFILE} #g_runcmd g_retrycmd sh ${f_CMDFILE} >${f_CMDFILE}_OUT . ${f_CMDFILE} cat ${g_tmp}/API_CMD_OUT >${f_CMDFILE}_OUT - [ $f_try -eq $f_tries ] && break + [ ${f_try} -eq ${f_tries} ] && break ((f_try=f_try+1)) done fi # Check return and log trade - f_STATUS=$(cat ${f_CMDFILE}_OUT | grep '^{' | jq -r .status) + [ ${STOCK_EXCHANGE} = "BINANCE" ] && f_STATUS=$(cat ${f_CMDFILE}_OUT | grep '^{' | jq -r .status) + [ ${STOCK_EXCHANGE} = "BITPANDA" ] && f_STATUS=$(cat ${f_CMDFILE}_OUT | grep '^{' | jq -r .order_id) local f_trade_info_msg="TRADE - ${f_ACTION} ${f_ASSET}${f_CURRENCY} ${f_link} @@ -86,12 +93,17 @@ Complete Overview: https://bot.ds9.dedyn.io/ Comment: ${f_COMMENT}" - if [ "${f_STATUS}" = "FILLED" ] + if [ "${f_STATUS}" = "FILLED" ] || echo ${f_STATUS} | egrep -q '.+-.+-.+-.+' then g_echo_note "TRADE SUCCESSFUL!" - local f_PRICE=$(cat ${f_CMDFILE}_OUT | grep '^{' | jq -r .fills[].price | head -n1) - local f_COMMISSION=$(cat ${f_CMDFILE}_OUT | grep '^{' | jq -r .fills[].commission) | head -n1 - local f_COMMISSIONASSET=$(cat ${f_CMDFILE}_OUT | grep '^{' | jq -r .fills[].commissionAsset | head -n1) + [ ${STOCK_EXCHANGE} = "BINANCE" ] && local f_PRICE=$(cat ${f_CMDFILE}_OUT | grep '^{' | jq -r .fills[].price | head -n1) + [ ${STOCK_EXCHANGE} = "BINANCE" ] && local f_COMMISSION=$(cat ${f_CMDFILE}_OUT | grep '^{' | jq -r .fills[].commission) | head -n1 + [ ${STOCK_EXCHANGE} = "BINANCE" ] && local f_COMMISSIONASSET=$(cat ${f_CMDFILE}_OUT | grep '^{' | jq -r .fills[].commissionAsset | head -n1) + + [ ${STOCK_EXCHANGE} = "BITPANDA" ] && local f_PRICE=$(cat ${f_CMDFILE}_OUT | grep '^{' | jq -r .price | head -n1) + [ ${STOCK_EXCHANGE} = "BITPANDA" ] && local f_COMMISSION=$(echo "scale=2; $f_PRICE/100*${FEE}" | bc -l) + [ ${STOCK_EXCHANGE} = "BITPANDA" ] && local f_COMMISSIONASSET="EUR" + echo "${f_DATE},${f_ACTION},${f_CMDFILE}_OUT,${f_QUANTITY} ${f_CURRENCY},${f_PRICE},${f_COMMISSION} ${f_COMMISSIONASSET},${f_COMMENT}" | head -n1 >>trade-histories/${f_ASSET}${f_CURRENCY}.history.csv if [ "${f_ACTION}" = "buy" ] then diff --git a/dabo/functions/get_assets.sh b/dabo/functions/get_assets.sh index 50a0874..18e2f5d 100644 --- a/dabo/functions/get_assets.sh +++ b/dabo/functions/get_assets.sh @@ -11,7 +11,12 @@ function get_assets { binance-api-call GET /api/v3/ticker/price || return 1 # parse API output cat ${g_tmp}/API_CMD_OUT | jq -r '.[] | .symbol + "," + .price' | grep "${CURRENCY}," | egrep -v "${TRANSFER_CURRENCY},|,0[\.][0]*$" >${f_filename}_OUT.tmp + elif [ ${STOCK_EXCHANGE} = "BITPANDA" ] + then + bitpanda-api-call GET "public/v1/market-ticker" || return 1 + cat ${g_tmp}/API_CMD_OUT | jq -r '.[] | .instrument_code + "," + .last_price' | sed 's/_//' | grep "${CURRENCY}," | egrep -v "${TRANSFER_CURRENCY},|,0[\.][0]*$" >${f_filename}_OUT.tmp fi + # timestamp for data f_timestamp=$(g_date_print) # check output diff --git a/dabo/functions/get_balances.sh b/dabo/functions/get_balances.sh index c95079b..05607fb 100644 --- a/dabo/functions/get_balances.sh +++ b/dabo/functions/get_balances.sh @@ -11,6 +11,10 @@ function get_balances { binance-api-call GET sapi/v1/capital/config/getall || return 1 # parse outout cat ${g_tmp}/API_CMD_OUT | jq -r '.[] | .coin + "," + .free' | egrep -v ",0$|,0[\.][0]*$|${TRANSFER_CURRENCY}" | sort >${f_filename}_OUT.tmp_raw + elif [ ${STOCK_EXCHANGE} = "BITPANDA" ] + then + bitpanda-api-call GET public/v1/account/balances || return 1 + cat ${g_tmp}/API_CMD_OUT | jq -r '.balances[] | .currency_code + "," + .available' | egrep -v ",0$|,0[\.][0]*$|${TRANSFER_CURRENCY}" | sort >${f_filename}_OUT.tmp_raw fi if ! [ -s ${f_filename}_OUT.tmp_raw ] && egrep -q "^[A-Z]+,[0-9]*\.[0-9]+$" ${f_filename}_OUT.tmp_raw diff --git a/dabo/functions/market_performance.sh b/dabo/functions/market_performance.sh index 2b2722e..30be6a8 100644 --- a/dabo/functions/market_performance.sh +++ b/dabo/functions/market_performance.sh @@ -58,7 +58,7 @@ function market_performance { #local f_back=240 #[[ $(date +%u) -eq 6 ]] && f_back=1500 #[[ $(date +%u) -eq 7 ]] && f_back=2940 - local f_from=$(tail -n 10080 asset-histories/MSCI-WORLD-INDEX.history.csv | grep -v ^[A-Z] | head -n1 | cut -d, -f2) + local f_from=$(tail -n 1440 asset-histories/MSCI-WORLD-INDEX.history.csv | grep -v ^[A-Z] | head -n1 | cut -d, -f2) local f_to=$(tail -n 1 asset-histories/MSCI-WORLD-INDEX.history.csv | cut -d, -f2) local f_exchange_rate_diff_percentage=$(g_percentage-diff ${f_from} ${f_to}) #local f_back=10080 diff --git a/dabo/functions/webpage.sh b/dabo/functions/webpage.sh index 8cbc66b..9710349 100644 --- a/dabo/functions/webpage.sh +++ b/dabo/functions/webpage.sh @@ -194,7 +194,7 @@ function webpage { echo "

Current config

" >>../index.html.tmp - echo "
$(cat ../../bot.conf | perl -pe 's/\/>/g;')
" >>../index.html.tmp + echo "
$(cat ../../dabo-bot.conf | perl -pe 's/\/>/g;')
" >>../index.html.tmp diff --git a/docker-compose.yml b/docker-compose.yml index f298fc8..e9e48db 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,9 +6,12 @@ services: context: . dockerfile: Dockerfile restart: unless-stopped + user: 10000:10000 volumes: - ./dabo:/dabo:ro - ./data:/dabo/htdocs:rw + - ./home:/dabo/home:rw + - /usr/local/bin/notify.sh:/usr/local/bin/notify.sh:ro - /etc/localtime:/etc/localtime:ro networks: - dabo--network