diff --git a/dabo/analyze.conf b/dabo/analyze.conf new file mode 100644 index 0000000..7d97eb1 --- /dev/null +++ b/dabo/analyze.conf @@ -0,0 +1,5 @@ + +ANALYZE_TIME="^2023-04-17" + +. bot.conf + diff --git a/dabo/analyze.sh b/dabo/analyze.sh new file mode 100755 index 0000000..0a5ba5c --- /dev/null +++ b/dabo/analyze.sh @@ -0,0 +1,129 @@ +#!/bin/bash + +. /etc/bash/gaboshlib.include + +g_nice + +function g_echo_note { + [ -z "$1" ] && return 0 + echo -en "\033[97m$(tail -n1 ${g_tmp}/$tmpfile | cut -d, -f1) \033[36mNOTE:" + cat <<< "$@" + echo -en "\033[0m" +} + +function analyze { + local file=$1 + tmpfile=$(basename "${file}") + + . /etc/bash/gaboshlib/g_percentage-diff.bashfunc + . functions/check_buy_conditions.sh + . functions/check_sell_conditions.sh + . functions/get_vars_from_csv.sh + . bot.conf + . analyze.conf + + + #g_echo "Analyzing file: $file" + + # cleanup + f_SELL=1 + f_BUY="" + >${g_tmp}/${tmpfile} + >${g_tmp}/result-${tmpfile} + + rm -f ${g_tmp}/open-${tmpfile} + rm -f ${g_tmp}/interim-${tmpfile} + rm -f ${g_tmp}/output-${tmpfile} + + ORIGIFS="$IFS" + IFS=$'\n' + for line in $(egrep "^${ANALYZE_TIME}" "$file") + do + IFS="$ORIGIFS" + current=$(echo $line | cut -d, -f2) + time=$(echo $line | cut -d, -f1 | cut -d: -f1,2) + + echo "$line" >>${g_tmp}/${tmpfile} + if [ -f "${g_tmp}/open-${tmpfile}" ] + then + check_sell_conditions ${g_tmp}/${tmpfile} >>${g_tmp}/output-${tmpfile} 2>&1 + fi + if ! [ -f "${g_tmp}/open-${tmpfile}" ] + then + f_market_performance=$(grep "^$time" htdocs/botdata/MARKET_PERFORMANCE | tail -n1 | cut -d: -f4 | cut -d"%" -f1 | sed 's/ *//') + [ -z "${f_market_performance}" ] && continue + if [ $(echo "${f_market_performance} < ${GOOD_MARKET_PERFORMANCE_INDEX}" | bc -l) -eq 0 ] + then + check_buy_conditions ${g_tmp}/${tmpfile} >>${g_tmp}/output-${tmpfile} 2>&1 || break + else + g_echo_note "bad market (${f_market_performance} < ${GOOD_MARKET_PERFORMANCE_INDEX}) - Price: $current" >>${g_tmp}/output-${tmpfile} 2>&1 + fi + fi + done + + # sell at the end to have a final result. + if [ -f ${g_tmp}/open-${tmpfile} ] + then + f_SELL="SELL ${f_ASSET}: End of file/data" + echo "SELL: $(tail -n1 ${g_tmp}/${tmpfile} | cut -d, -f1) === ${f_SELL}" >>${g_tmp}/output-${tmpfile} 2>&1 + result=$(g_percentage-diff ${BUY_PRICE} ${current}) + result=$(echo "${result}-${FEE}" | bc | sed 's/^\./0./; s/^-\./-0./') + echo "$result" >>${g_tmp}/result-${tmpfile} + echo "RESULT: ${result}% (${BUY_PRICE} -> ${current})" >>${g_tmp}/output-${tmpfile} + rm -f ${g_tmp}/open-${tmpfile} + rm -f ${g_tmp}/interim-${tmpfile} + fi + + complete_result=0 + for result in $(cat ${g_tmp}/result-${tmpfile}) + do + complete_result=$(echo "$complete_result+$result" | bc -l | sed 's/^\./0./; s/^-\./-0./' | xargs printf "%.2f") + done + echo "COMPLETE RESULT $file analyze-${analyzedate}/${tmpfile}.log: ${complete_result}%" | tee -a ${g_tmp}/output-${tmpfile} + echo "=====================================" >>${g_tmp}/output-${tmpfile} + echo "${complete_result}" >>${g_tmp}/overall-result-${tmpfile} + + cat ${g_tmp}/output-${tmpfile} >"analyze-${analyzedate}/${tmpfile}.log" + +} + +. bot.conf +. analyze.conf +set | grep ^[A-Z].*=[-0-9] + +cores=$(cat /proc/cpuinfo | grep "^processor.*:" | tail -n1 | perl -pe 's/processor.*: //') +echo -n "parallel -j${cores} bash -c --" >/tmp/parallel-$$ + +analyzedate="$(date +%Y-%m-%d--%H-%M-%S)" +mkdir "analyze-${analyzedate}" +cp bot.conf analyze.conf analyze-${analyzedate}/ + +for file in $@ +do + echo "${file}" | grep -q "BALANCE" && continue + echo -n " \"analyze ${file}\"" >>/tmp/parallel-$$ +done +export -f g_echo_note +export g_tmp +export analyzedate +. /tmp/parallel-$$ + +echo "OVERALL RESULT: $(cat ${g_tmp}/overall-result-* | awk '{ SUM += $1 } END { printf("%2.2f", SUM) }')%" | tee analyze-${analyzedate}/overall-result.log +cat analyze-${analyzedate}/*.history.csv.log >analyze-${analyzedate}/analyze-overall.log + +echo Trades: "$(grep "BUY: " analyze-${analyzedate}/analyze-overall.log | wc -l)" | tee -a analyze-${analyzedate}/overall-result.log +echo Interim Results: "$(grep "INTERIM RESULT: " analyze-${analyzedate}/analyze-overall.log | wc -l)" | tee -a analyze-${analyzedate}/overall-result.log +echo Interim Results Positive: "$(grep "INTERIM RESULT: [0-9]" analyze-${analyzedate}/analyze-overall.log | wc -l)" | tee -a analyze-${analyzedate}/overall-result.log +echo Interim Results Negative: "$(grep "INTERIM RESULT: -" analyze-${analyzedate}/analyze-overall.log | wc -l)" | tee -a analyze-${analyzedate}/overall-result.log +echo " + +Complete Results" >>analyze-${analyzedate}/overall-result.log +grep "COMPLETE RESULT " analyze-${analyzedate}/analyze-overall.log | grep -v ": 0%" >>analyze-${analyzedate}/overall-result.log + +echo " + +Trades" >>analyze-${analyzedate}/overall-result.log +egrep "BUY: |SELL: " analyze-${analyzedate}/analyze-overall.log >>analyze-${analyzedate}/overall-result.log + + + diff --git a/dabo/dabo-bot.conf b/dabo/dabo-bot.conf new file mode 100644 index 0000000..628cdae --- /dev/null +++ b/dabo/dabo-bot.conf @@ -0,0 +1,130 @@ + +# The exchange we use for using the correct API (BINANCE or BITPANDA) +STOCK_EXCHANGE="BITPANDA" + +# fee per trade in percentage on exchange (taker and maker added) +FEE="0.4" + +# Interval in seconds +INTERVAL="150" + +## Currency used for trading +CURRENCY="USDT" +TRANSFER_CURRENCY="EUR" + +# Only use currencies under the first X currencies sorted by market capitalization +LARGEST_MARKETCAP="250" + +# Blacklist Currencies +BLACKLIST="^DAI${CURRENCY}|^BUSD${CURRENCY}|^USDC${CURRENCY}|^USDP${CURRENCY}|^TUSD${CURRENCY}|^USDD${CURRENCY}|^GUSD${CURRENCY}|^FEI${CURRENCY}|^USTC${CURRENCY}|^FRAX${CURRENCY}|^USDJ${CURRENCY}|^LUSD${CURRENCY}|^EURS${CURRENCY}|^TRIBE${CURRENCY}|^BNB${CURRENCY}" + +## Signal Group for Notifications +SIGNAL_GROUP="Krypto-Bot" + +## Percent from balance per invest. +# Overwritten by MIN_NOTIONAL+X% from stock if lower +INVEST="5" + +# GOOD_MARKET_PERFORMANCE_INDEX defines from fwhich growth the market is considered good/favorable for investment. +# The market performance is calculated from the average percentage development of various indicators such as development MSCI World, Bitcoin and Ethereum as well as forecasts. +# for details see functions/market_performance.sh +# If the market performance is under this value no buying will be done +GOOD_MARKET_PERFORMANCE_INDEX="0.3" + +# Stop all trading and sell everything if the complete balance shrinks under this value in ${CURRENCY} +EMERGENCY_STOP="190" + + + +###### BUY CONDITIONS ###### + +### RSI Indicator checks +# Don't buy if the RSI-XX value is >= BUY_RSIXX_BUY_SIGNAL_UNTIL +BUY_RSI5_SIGNAL_UNTIL="99" +BUY_RSI14_SIGNAL_UNTIL="99" +BUY_RSI21_SIGNAL_UNTIL="99" +BUY_RSI60_SIGNAL_UNTIL="99" +BUY_RSI120_SIGNAL_UNTIL="99" +BUY_RSI240_SIGNAL_UNTIL="99" +BUY_RSI420_SIGNAL_UNTIL="99" +BUY_RSI720_SIGNAL_UNTIL="99" + +# Don't buy if the RSI-XX value is <= BUY_RSIXX_BUY_SIGNAL_FROM +BUY_RSI5_SIGNAL_FROM="0" +BUY_RSI14_SIGNAL_FROM="0" +BUY_RSI21_SIGNAL_FROM="0" +BUY_RSI60_SIGNAL_FROM="0" +BUY_RSI120_SIGNAL_FROM="0" +BUY_RSI240_SIGNAL_FROM="0" +BUY_RSI420_SIGNAL_FROM="0" +BUY_RSI720_SIGNAL_FROM="0" + + +### MACD Indicator Checks +# Don't buy if MACD Histogram Relation is not between this values. +# The ratio is calculated by the difference between the maximum positive Histogram value (of the defined time period) and the current value. +# Here you can specify a percentage range. +# If only the buy signal should be considered, simply specify the range 0 and 100. +# decimal numbers are not allowed here. +BUY_MACD_RELATION_FROM="60" +BUY_MACD_RELATION_TO="95" + +# Don't buy if price change is under this percentage values +BUY_MIN_PRICE_CHANGE_LAST_1_DAY="0.25" +BUY_MIN_PRICE_CHANGE_LAST_7_DAY="-1" +BUY_MIN_PRICE_CHANGE_LAST_14_DAY="-2" +BUY_MIN_PRICE_CHANGE_LAST_30_DAY="-5" + + +# Dont buy if growth in the last defined time period <= BUY_MINGROWTH +BUY_MINGROWTH_PERIOD="30" +BUY_MINGROWTH="-10" + + + +###### SELL CONDITIONS ###### + +# Force hold if result negative expect SELL_PERCENTAGE_FROM_LAST_PURCHASE_NEGATIVE. Boolean 1 for true 0 for false. +SELL_HOLD_IF_RESULT_NEGATIVE="1" + +# If the price falls by this percentage value from the purchase price, then sell in any case +SELL_PERCENTAGE_FROM_LAST_PURCHASE_NEGATIVE="-10" + +# from here only if SELL_HOLD_IF_RESULT_NEGATIVE doesn't match + +# If the price falls by this percentage value from the last rate +SELL_IF_LAST_RATE_LOWER_THEN="-0.25" + +### RSI Indicator checks + +# SELL if the RSI-XX value is <= SELL_RSIXX_SELL_SIGNAL_UNTIL +SELL_RSI5_SIGNAL_UNTIL="99" +SELL_RSI14_SIGNAL_UNTIL="99" +SELL_RSI21_SIGNAL_UNTIL="99" +SELL_RSI60_SIGNAL_UNTIL="99" +SELL_RSI120_SIGNAL_UNTIL="99" +SELL_RSI240_SIGNAL_UNTIL="99" +SELL_RSI420_SIGNAL_UNTIL="99" +SELL_RSI720_SIGNAL_UNTIL="99" + +# SELL if the RSI-XX value is >= SELL_RSIXX_SELL_SIGNAL_FROM +SELL_RSI5_SIGNAL_FROM="90" +SELL_RSI14_SIGNAL_FROM="90" +SELL_RSI21_SIGNAL_FROM="90" +SELL_RSI60_SIGNAL_FROM="70" +SELL_RSI120_SIGNAL_FROM="50" +SELL_RSI240_SIGNAL_FROM="50" +SELL_RSI420_SIGNAL_FROM="50" +SELL_RSI720_SIGNAL_FROM="50" + +# If the price after this time periods is lower the the trading fee, then sell if SELL_HOLD_IF_RESULT_NEGATIVE is fine with it +SELL_IF_LOWER_THEN_FEE_AFTER_PERIOD="9999999" + +### MACD Indicator Checks +# Sell if MACD Histogram relation is < this value +# The ratio is calculated by the difference between the maximum negative Histogram value (of the defined time period) and the current value. +# Here you can specify a percentage range. +# If only the sell signal should be considered, simply specify 0 +# decimal numbers are not allowed here. +SELL_MACD_RELATION_FROM="25" + diff --git a/dabo/dabo-bot.sh b/dabo/dabo-bot.sh new file mode 100755 index 0000000..05ceba1 --- /dev/null +++ b/dabo/dabo-bot.sh @@ -0,0 +1,82 @@ +#!/bin/bash + +. /etc/bash/gaboshlib.include + +g_lockfile + + +### CONFIG ### + +BASEPATH=/dabo/htdocs +g_tries=3 +g_tries_delay=5 + + +### FUNCTIONS ### +for bashfunc in $(find ${BASEPATH}/../functions -type f -name "*.sh") +do + . "$bashfunc" +done + +### MAIN ### + +# prepare directories +mkdir -p ${BASEPATH}/botdata/asset-histories +mkdir -p ${BASEPATH}/botdata/trade-histories +cd ${BASEPATH}/botdata + +touch firstloop + +# am I the bot (important for functions used by analyze.sh +echo $0 | grep -q "dabo-bot\.sh" && BOT=1 + +# run endless loop +while true +do + + # wait until next full minute in the beginning to be able to work with continue in this loop + if [ -f firstloop ] + then + rm -f firstloop + else + g_echo_note "NEXT LOOP - sleping until next full ${INTERVAL} seconds" + sleep $((${INTERVAL} - $(date +%s) % ${INTERVAL})) + fi + + # reload config + . ../../dabo-bot.conf + + # 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"' + fi + + # Get current assets + get_assets || continue + + # Get current balances + get_balances || continue + + # Check the situation on the market + if ! market_performance + then + f_market_performance=$(cat MARKET_PERFORMANCE_LATEST) + fi + + ##### Sell something? #### + check_for_sell + + ##### Buy something? #### + check_for_buy + + ##### Update webpage + webpage & + +done + diff --git a/dabo/functions/api-calls.sh b/dabo/functions/api-calls.sh new file mode 100644 index 0000000..0a4c4b2 --- /dev/null +++ b/dabo/functions/api-calls.sh @@ -0,0 +1,14 @@ +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 new file mode 100644 index 0000000..48d51c5 --- /dev/null +++ b/dabo/functions/binance-api-call.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +function binance-api-call { + g_echo_note "RUNNING FUNCTION ${FUNCNAME} $@" + local method=$1 + local call=$2 + local params=$3 + + if [ -s /home/docker/binance-cli/.binance-secrets ] + then + . /home/docker/binance-cli/.binance-secrets + else + g_echo_error "No secrets file found" + return 1 + fi + + call=$(echo "$call" | sed "s/^\///") + if echo "${call}" | egrep -q "^sapi/|^api/v3/order" + then + params="recvWindow=60000${params}" + local timestamp=$(date +%s000) + params="${params}×tamp=${timestamp}" + local signature=$(echo -n "${params}" | openssl dgst -sha256 -hmac "${API_SECRET}" | cut -d" " -f2) + params="?${params}&signature=$signature" + fi + + echo "curl -s -H \"X-MBX-APIKEY: $API_KEY\" -X \"$method\" \"https://api.binance.com/${call}${params}\"" >${g_tmp}/API_CMD + echo "curl -s -H \"X-MBX-APIKEY: API_KEY\" -X \"$method\" \"https://api.binance.com/${call}${params}\"" >${g_tmp}/API_CMD_WO_KEY + g_runcmd g_retrycmd sh ${g_tmp}/API_CMD >${g_tmp}/API_CMD_OUT 2>&1 + if egrep -q -i '^{"code":|error' ${g_tmp}/API_CMD_OUT + then + g_echo_error "$(cat ${g_tmp}/API_CMD_WO_KEY): $(cat ${g_tmp}/API_CMD_OUT)" + return 1 + fi +} + diff --git a/dabo/functions/binance_convert.sh b/dabo/functions/binance_convert.sh new file mode 100644 index 0000000..aee5893 --- /dev/null +++ b/dabo/functions/binance_convert.sh @@ -0,0 +1,118 @@ +function binance_convert { + # Info for log + g_echo_note "RUNNING FUNCTION ${FUNCNAME} $@" + + # needed vars + local f_ASSET=$1 + local f_CURRENCY=$2 + local f_QUANTITY=$3 + local f_ACTION=$4 # buy or sell + local f_COMMENT=$5 + local f_QUANTITY_CURRENCY=$6 + + local f_DATE=$(date '+%F_%H-%M-%S') + local f_ASSET_HIST_FILE="asset-histories/${f_ASSET}${f_CURRENCY}.history.csv" + + local f_link="https://www.coingecko.com/de/munze/$(egrep -i ^${f_ASSET}, COINGECKO_IDS | cut -d, -f2)" + + local f_CMDFILE="trade-histories/${f_DATE}-${f_CURRENCY}-BINANCE_CONVERT-TRADE_CMD" + + local f_num_converts=$(find trade-histories/*-*-BINANCE_CONVERT-TRADE_CMD -type f -mmin 60 | wc -l) + if [ $f_num_converts -ge 99 ] + then + g_echo_note "Already did 99 or more binance converts last hour." + return 1 + fi + + if [ "${f_ACTION}" = "buy" ] + then + # check for enough balance for trade + get_balances + local f_CURRENCY_BALANCE=$(egrep "^${f_CURRENCY}," EXCHANGE_GET_BALANCES_CMD_OUT | cut -d"," -f2) + g_echo_note "Checking for enough balance for trade (${f_CURRENCY_BALANCE} > ${f_QUANTITY})" + if [ $(echo "${f_CURRENCY_BALANCE} > ${f_QUANTITY}" | bc -l) -eq 0 ] + 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" + return 1 + fi + + # get quote on buy + echo "binance-api-call POST /sapi/v1/convert/getQuote '&fromAsset=${f_CURRENCY}&toAsset=${f_ASSET}&fromAmount=${f_QUANTITY}&walletType=SPOT&validTime=10s'" >${f_CMDFILE} + fi + + if [ "${f_ACTION}" = "sell" ] + then + # get quote on sell + echo "binance-api-call POST /sapi/v1/convert/getQuote '&fromAsset=${f_ASSET}&toAsset=${f_CURRENCY}&fromAmount=${f_QUANTITY}&walletType=SPOT&validTime=10s'" >${f_CMDFILE} + fi + + echo "cat ${g_tmp}/API_CMD_OUT >${f_CMDFILE}_QUOTE_OUT +local f_quoteid=\$(cat ${g_tmp}/API_CMD_OUT | jq -r '.quoteId') +binance-api-call POST /sapi/v1/convert/acceptQuote \""eId=\${f_quoteid}\" +cat ${g_tmp}/API_CMD_OUT >${f_CMDFILE}_OUT +" >>${f_CMDFILE} + + # convert/trade + g_echo_note "Command: $(cat ${f_CMDFILE})" + . ${f_CMDFILE} + cat ${g_tmp}/API_CMD_OUT >${f_CMDFILE}_OUT + g_echo_note "Command Output: $(cat ${f_CMDFILE}_OUT)" + + + # Check return and log trade + f_STATUS=$(cat ${f_CMDFILE}_OUT | grep '^{' | jq -r .orderStatus) + + local f_trade_info_msg="CONVERT/TRADE - ${f_ACTION} ${f_ASSET}${f_CURRENCY} +${f_link} + +Complete Overview: https://bot.ds9.dedyn.io/ + +Comment: ${f_COMMENT}" + + if echo "${f_STATUS}" | egrep -q "PROCESS|ACCEPT_SUCCESS|SUCCESS" + then + g_echo_note "CONVERT/TRADE SUCCESSFUL!" + [ "${f_ACTION}" = "buy" ] && local f_PRICE=$(cat ${f_CMDFILE}_QUOTE_OUT | grep '^{' | jq -r .inverseRatio | head -n1) + [ "${f_ACTION}" = "sell" ] && local f_PRICE=$(cat ${f_CMDFILE}_QUOTE_OUT | grep '^{' | jq -r .ratio | head -n1) + local f_COMMISSION="0" + local f_COMMISSIONASSET="${f_CURRENCY}" + 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 + echo "${f_DATE},${f_ACTION},${f_CMDFILE}_OUT,${f_QUANTITY} ${f_CURRENCY},${f_PRICE},${f_COMMISSION} ${f_COMMISSIONASSET},${f_COMMENT}" | head -n1 >>trade-histories/trade-$(date +%F.%T. | sed 's/:/_/g')${f_ASSET}${f_CURRENCY}-open.history.csv + fi + if [ "${f_ACTION}" = "sell" ] + then + f_tradehistfile="$(ls trade-histories/trade-*${f_ASSET}${f_CURRENCY}-open.history.csv | tail -n1)" + echo "${f_DATE},${f_ACTION},${f_CMDFILE}_OUT,${f_QUANTITY_CURRENCY} ${f_CURRENCY},${f_PRICE},${f_COMMISSION} ${f_COMMISSIONASSET},${f_COMMENT}" | head -n1 >>${f_tradehistfile} + f_tradehistfileclosed=$(echo ${f_tradehistfile} | sed 's/open.history.csv/closed.history.csv/') + mv ${f_tradehistfile} ${f_tradehistfileclosed} + fi + g_signal-notify "CONVERT/TRADE SUCCESSFUL! + +${f_trade_info_msg} + +Command stored: ${f_CMDFILE}[_OUT]" + [ -f DIFF_BUY_PRICE_${f_ASSET}${f_CURRENCY} ] && [ "${f_ACTION}" = "sell" ] && rm -f DIFF_BUY_PRICE_${f_ASSET}${f_CURRENCY} + # get new balances + get_balances + return 0 + else + g_echo_note "CONVERT/TRADE FAILED! +$(cat ${f_CMDFILE}_OUT)" + g_signal-notify "CONVERT/TRADE FAILED! +${f_trade_info_msg} + +Command ${f_CMDFILE}: +$0 $@ +$(cat ${f_CMDFILE}) + +OUTPUT: +$(cat ${f_CMDFILE}_OUT) +" + return 1 + fi +} diff --git a/dabo/functions/binance_convert_dust.sh b/dabo/functions/binance_convert_dust.sh new file mode 100644 index 0000000..9fa1846 --- /dev/null +++ b/dabo/functions/binance_convert_dust.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +function binance_convert_dust { + + # find BNB Balance of an conversion which ran before - Balance takes a while to be shown on BNB + # get BNB balance + binance-api-call GET sapi/v1/capital/config/getall + local f_bnb_balance=$(cat ${g_tmp}/API_CMD_OUT | jq -r '.[] | .coin + "," + .free' | grep "^BNB," | cut -d, -f2) + # convert BNB to $CURRENCY + #if echo "${f_bnb_balance}" | egrep -q "[0-9]\.[0-9]" + if [ $(echo "${f_bnb_balance} > 0.004" | bc -l) -ne 0 ] + then + binance-api-call POST /sapi/v1/convert/getQuote "&fromAsset=BNB&toAsset=${CURRENCY}&fromAmount=${f_bnb_balance}&walletType=SPOT&validTime=30s" + local f_quoteid=$(cat ${g_tmp}/API_CMD_OUT | jq -r '.quoteId') + g_signal-notify "Converting dust from ${f_bnb_balance} BNB to ${CURRENCY} +$(cat ${g_tmp}/API_CMD_OUT)" + binance-api-call POST /sapi/v1/convert/acceptQuote ""eId=${f_quoteid}" + fi + + # Only run every 6 houres - binance doesn't allow to do it more often + [ -s BINANCE_LAST_DUST_RUN ] || date >BINANCE_LAST_DUST_RUN + find BINANCE_LAST_DUST_RUN -mmin +362 -delete + [ -s BINANCE_LAST_DUST_RUN ] && return 0 + date >BINANCE_LAST_DUST_RUN + + g_echo_note "RUNNING FUNCTION ${FUNCNAME} $@" + + # find dust + local f_dust_assets="" + local f_dust + binance-api-call POST /sapi/v1/asset/dust-btc + # ignore $CURRENCY and assets in open trades + for f_dust in $(cat ${g_tmp}/API_CMD_OUT | jq -r '.details[].asset' | egrep -v "${CURRENCY}") + do + ls trade-histories/trade-*.${f_dust}${CURRENCY}-open.history.csv >/dev/null 2>&1 && continue + f_dust_assets="${f_dust_assets},$f_dust" + done + f_dust_assets=$(echo ${f_dust_assets} | sed 's/^,//') + + # convert dust to BNB + if [ -n "${f_dust_assets}" ] + then + g_signal-notify "Converting dust from ${f_dust_assets} to BNB" + binance-api-call POST /sapi/v1/asset/dust "&asset=${f_dust_assets}" + fi +} diff --git a/dabo/functions/binance_get_token_info.sh b/dabo/functions/binance_get_token_info.sh new file mode 100644 index 0000000..8531bf5 --- /dev/null +++ b/dabo/functions/binance_get_token_info.sh @@ -0,0 +1,38 @@ +function binance_get_token_info { + g_echo_note "RUNNING FUNCTION ${FUNCNAME} $@" + local f_ASSET=$1 + local f_CURRENCY=$2 + local f_QUANTITY=$3 + # cleanup cache + [ -s BINANCE_TOKEN_INFO_CMD_OUT ] && find BINANCE_TOKEN_INFO_CMD_OUT -mmin +60 -or -empty -delete + #echo "$BINANCE_CLI_CMD info" >BINANCE_TOKEN_INFO_CMD + #[ -s BINANCE_TOKEN_INFO_CMD_OUT ] || g_runcmd g_retrycmd sh BINANCE_TOKEN_INFO_CMD >BINANCE_TOKEN_INFO_CMD_OUT + if ! [ -s BINANCE_TOKEN_INFO_CMD_OUT ] + then + binance-api-call GET /api/v3/exchangeInfo >BINANCE_TOKEN_INFO_CMD_OUT + cat ${g_tmp}/API_CMD_OUT >BINANCE_TOKEN_INFO_CMD_OUT + fi + + + # cut quantity by lot-step-size + if ! [ -z "$f_QUANTITY" ] + then + local f_LOT_STEP_SIZE=$(cat BINANCE_TOKEN_INFO_CMD_OUT | jq -c "[ .symbols[] | select( .symbol==\"${f_ASSET}${f_CURRENCY}\") ]" | jq -c '.[].filters[] | select( .filterType=="LOT_SIZE")' | jq -r '.stepSize' | perl -pe 's/0+$//; s/\.$//') + if [ -z "${f_LOT_STEP_SIZE}" ] + then + g_echo "Got no LOT_STEP_SIZE from BINANCE_TOKEN_INFO_CMD_OUT for ${f_ASSET} - Assuming 0.01" + f_LOT_STEP_SIZE="0.01" + fi + local f_LOT_DIV=$(echo "scale=0; ${f_QUANTITY}/${f_LOT_STEP_SIZE}" | bc -l) + f_QUANTITY_LOT_CUT=$(echo "${f_LOT_DIV}*${f_LOT_STEP_SIZE}" | bc -l | sed 's/^\./0./;') + fi + + # get min CURRENCY + f_MIN_NOTIONAL=$(cat BINANCE_TOKEN_INFO_CMD_OUT | jq -c "[ .symbols[] | select( .symbol==\"${f_ASSET}${f_CURRENCY}\") ]" | jq -c '.[].filters[] | select( .filterType=="MIN_NOTIONAL")' | jq -r '.minNotional' | perl -pe 's/0+$//; s/\.$//') + if [ -z "${f_MIN_NOTIONAL=}" ] + then + g_echo "Got no LOT_STEP_SIZE from BINANCE_TOKEN_INFO_CMD_OUT for ${f_ASSET} - Assuming 10.00000000" + f_MIN_NOTIONAL="10.00000000" + fi +} + diff --git a/dabo/functions/check_buy_conditions.sh b/dabo/functions/check_buy_conditions.sh new file mode 100644 index 0000000..0c1f5bd --- /dev/null +++ b/dabo/functions/check_buy_conditions.sh @@ -0,0 +1,173 @@ +function check_buy_conditions { + + local f_ASSET_HIST_FILE="$1" + f_ASSET=$(basename ${f_ASSET_HIST_FILE} | cut -d\. -f1) + + if [ -n "${BOT}" ] + then + # ignore already invested asset + if ls trade-histories/trade-*${f_ASSET}-open.history.csv >/dev/null 2>&1 + then + g_echo_note "BUY ${f_ASSET}@${CURRENCY}: $f_ASSET Already invested - ignoring for more diversification" + return 0 + fi + fi + + get_vars_from_csv "${f_ASSET_HIST_FILE}" || return 1 + + ### from here: check for defined state to buy + f_BUY="${f_last_line}" + + + local f_priceXago=$(tail -n${BUY_MINGROWTH_PERIOD} ${f_ASSET_HIST_FILE} | grep ^[0-9] | head -n1 | cut -d, -f2) + local f_pricenow=$(echo ${f_last_line} | grep ^[0-9] | cut -d, -f2) + local f_pricediff=$(g_percentage-diff "$f_priceXago" "$f_pricenow") + if [ $(echo "${f_pricediff} < ${BUY_MINGROWTH}" | bc -l) -ne 0 ] + then + g_echo_note "BUY ${f_ASSET}@${CURRENCY}:${f_price}: With ${f_pricediff} under ${BUY_MINGROWTH}% growth in the last ${BUY_MINGROWTH_PERIOD} time periods" + return 0 + fi + + #local f_macd_histogram_max=$(tail -n26 "${f_ASSET_HIST_FILE}" | cut -d"," -f8 | egrep "^[0-9]|^-[0-9]" | sed 's/^-//' | sort -n | tail -n1) + #local f_macd_histogram_relation=$(echo "100+$(g_percentage-diff ${f_macd_histogram_max} ${f_macd_histogram})" | bc | cut -d\. -f1) + + + # MACD + # no negative or empty values + echo "${f_macd_histogram_relation}" | grep -q "^[0-9]" || return 0 + # check for conditions + if [ ${f_macd_histogram_relation} -le ${BUY_MACD_RELATION_FROM} ] || [ ${f_macd_histogram_relation} -ge ${BUY_MACD_RELATION_TO} ] + then + g_echo_note "BUY ${f_ASSET}@${CURRENCY}:${f_price}: MACD conditions NOT met" + return 0 + fi + + + # RSI + if \ + [ ${f_rsi5} -le ${BUY_RSI5_SIGNAL_UNTIL} ] && \ + [ ${f_rsi14} -le ${BUY_RSI14_SIGNAL_UNTIL} ] && \ + [ ${f_rsi21} -le ${BUY_RSI21_SIGNAL_UNTIL} ] && \ + [ ${f_rsi60} -le ${BUY_RSI60_SIGNAL_UNTIL} ] && \ + [ ${f_rsi120} -le ${BUY_RSI120_SIGNAL_UNTIL} ] && \ + [ ${f_rsi240} -le ${BUY_RSI240_SIGNAL_UNTIL} ] && \ + [ ${f_rsi420} -le ${BUY_RSI420_SIGNAL_UNTIL} ] && \ + [ ${f_rsi720} -le ${BUY_RSI720_SIGNAL_UNTIL} ] && \ + [ ${f_rsi5} -ge ${BUY_RSI5_SIGNAL_FROM} ] && \ + [ ${f_rsi14} -ge ${BUY_RSI14_SIGNAL_FROM} ] && \ + [ ${f_rsi21} -ge ${BUY_RSI21_SIGNAL_FROM} ] && \ + [ ${f_rsi60} -ge ${BUY_RSI60_SIGNAL_FROM} ] && \ + [ ${f_rsi120} -ge ${BUY_RSI120_SIGNAL_FROM} ] && \ + [ ${f_rsi240} -ge ${BUY_RSI240_SIGNAL_FROM} ] && \ + [ ${f_rsi420} -ge ${BUY_RSI420_SIGNAL_FROM} ] && \ + [ ${f_rsi720} -ge ${BUY_RSI720_SIGNAL_FROM} ] + then + echo "BUY ${f_ASSET}@${CURRENCY}:${f_price}: RSI conditions met" >/dev/null + else + g_echo_note "BUY ${f_ASSET}@${CURRENCY}:${f_price}: RSI conditions NOT met" + return 0 + fi + + + # Price change + if \ + [ $(echo "${f_price_change_1_day} > ${BUY_MIN_PRICE_CHANGE_LAST_1_DAY}" | bc -l) -ne 0 ] && \ + [ $(echo "${f_price_change_7_day} > ${BUY_MIN_PRICE_CHANGE_LAST_7_DAY}" | bc -l) -ne 0 ] && \ + [ $(echo "${f_price_change_14_day} > ${BUY_MIN_PRICE_CHANGE_LAST_14_DAY}" | bc -l) -ne 0 ] && \ + [ $(echo "${f_price_change_30_day} > ${BUY_MIN_PRICE_CHANGE_LAST_30_DAY}" | bc -l) -ne 0 ] + then + echo "BUY ${f_ASSET}@${CURRENCY}:${f_price}: Price change conditions met" >/dev/null + else + g_echo_note "BUY ${f_ASSET}@${CURRENCY}:${f_price}: Price change conditions NOT met" + return 0 + fi + + + if [ -n "$f_BUY" ] + then + # Check for beginning MACD Trend + if ! tail -n3 ${f_ASSET_HIST_FILE} | grep -q '|buy,' + then + g_echo_note "BUY ${f_ASSET}@${CURRENCY}:${f_price}: MACD Trend not near (3 timeframes) the beginning buy signal" + return 0 + fi + + # Check for growing MACD trend + local f_macd_histogram_before=$(tail -n2 ${f_ASSET_HIST_FILE} | head -n1 | cut -d, -f8) + local f_macd_histogram_2before=$(tail -n3 ${f_ASSET_HIST_FILE} | head -n1 | cut -d, -f8) + local f_macd_histogram_3before=$(tail -n4 ${f_ASSET_HIST_FILE} | head -n1 | cut -d, -f8) + local f_macd_histogram_4before=$(tail -n5 ${f_ASSET_HIST_FILE} | head -n1 | cut -d, -f8) + local f_macd_histogram_5before=$(tail -n6 ${f_ASSET_HIST_FILE} | head -n1 | cut -d, -f8) + if \ + [ $(echo "${f_macd_histogram} < ${f_macd_histogram_before}" | bc -l) -ne 0 ] && \ + [ $(echo "${f_macd_histogram_before} < ${f_macd_histogram_2before}" | bc -l) -ne 0 ] && \ + [ $(echo "${f_macd_histogram_2before} < ${f_macd_histogram_3before}" | bc -l) -ne 0 ] && \ + [ $(echo "${f_macd_histogram_3before} < ${f_macd_histogram_4before}" | bc -l) -ne 0 ] && \ + [ $(echo "${f_macd_histogram_4before} < ${f_macd_histogram_5before}" | bc -l) -ne 0 ] + then + g_echo_note "BUY ${f_ASSET}@${CURRENCY}:${f_price} MACD Histogram not rising (now ${f_macd_histogram} < 1 before ${f_macd_histogram_before} < 2 before ${f_macd_histogram_2before} < 3 before ${f_macd_histogram_3before} < 4 before ${f_macd_histogram_4before} < 5 before ${f_macd_histogram_5before}) - trend loses strength / not growing - don't buy" + return 0 + fi + + fi + + + + ### Buy or not buy? + # BOT + if [ -n "$f_BUY" ] && [ -n "${BOT}" ] + then + echo "${f_last_line},${f_ASSET}" >>trade.log + + f_BUY="BUY ${f_ASSET}@${CURRENCY}:${f_price}: All BUY conditions met!!! +${f_BUY}" + g_echo_note "${f_BUY}" + + # calculate quantity from balance for potentially invest + local f_INVEST_QUANTITY=$(echo "scale=2; ${CURRENCY_BALANCE}/100*${INVEST}" | bc -l | sed 's/^\./0./;' | cut -d\. -f1) + g_echo_note "BUY current investment quantity is ${f_INVEST_QUANTITY} ${CURRENCY}" + + # 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 + binance_convert ${f_ASSET} ${CURRENCY} ${f_INVEST_QUANTITY} buy "$f_BUY" || \ + do_trade ${f_ASSET} ${CURRENCY} ${f_INVEST_QUANTITY} buy "$f_BUY" + else + do_trade ${f_ASSET} ${CURRENCY} ${f_INVEST_QUANTITY} buy "$f_BUY" + fi + f_BUY="" + fi + + # ANALYZE + if [ -n "$f_BUY" ] && [ -z "${BOT}" ] + then + echo "BUY: $(echo ${f_last_line} | cut -d, -f1) === $f_BUY" + echo "$f_BUY" >${g_tmp}/open-${tmpfile} + BUY_PRICE=$current + fi +} + diff --git a/dabo/functions/check_for_buy.sh b/dabo/functions/check_for_buy.sh new file mode 100644 index 0000000..e4f52a4 --- /dev/null +++ b/dabo/functions/check_for_buy.sh @@ -0,0 +1,54 @@ +function check_for_buy { + + g_echo_note "RUNNING FUNCTION ${FUNCNAME} $@" + + g_echo_ok "Investmentbudget: $CURRENCY_BALANCE $CURRENCY" + + # check for emergency stop + local f_COMPLETE_BALANCE=$(tail -n1 "asset-histories/BALANCECOMPLETE${CURRENCY}.history.csv" | cut -d, -f2) + if [ $(echo "${f_COMPLETE_BALANCE} < ${EMERGENCY_STOP}" | bc -l) -ne 0 ] + then + g_echo_note "BUY ATTENTION! EMERGENCY STOP DUE TO POOR PERFORMANCE: BALANCE (${f_COMPLETE_BALANCE}) LOWER THEN EMERGENCY_STOP-VALUE (${EMERGENCY_STOP})" + return 0 + fi + + ## Generate grep regex for already invested assets + #f_investedassets_regex=$(cat EXCHANGE_GET_BALANCES_CMD_OUT | cut -d, -f1 | perl -pe 's/^/\^/; s/\n/\|/' | perl -pe 's/\|$//') + + if ! [ -s ASSETS ] + then + g_echo_note "BUY file ASSETS empty $(ls -l ASSETS)" + return 0 + fi + + ## use BUY_CONDITION_RISK if market is bad + if [ $(echo "${f_market_performance} > ${GOOD_MARKET_PERFORMANCE_INDEX}" | bc -l) -eq 0 ] + then + #g_echo_note "BUY market performance ${f_market_performance}% looks bad - Using BUY_CONDITION_RISK (${BUY_CONDITION_RISK}) as BUY_CONDITION" + #BUY_CONDITION=${BUY_CONDITION_RISK} + g_echo_note "BUY market performance ${f_market_performance}% looks bad - Dont buy anything" + return 0 + else + g_echo_note "BUY market performance ${f_market_performance}% looks ok - going on buying with BUY_CONDITION (${BUY_CONDITION})" + fi + + # go through highest assets + local f_line + for f_ASSET in $(cat ASSETS | egrep -v "${BLACKLIST}") + do + + f_ASSET_HIST_FILE="asset-histories/${f_ASSET}.history.csv" + + if [ $(tail -n 6 "$f_ASSET_HIST_FILE" | egrep -v ",,|,$" | wc -l) -ge 5 ] + then + check_buy_conditions "$f_ASSET_HIST_FILE" + else + g_echo_note "BUY $f_ASSET_HIST_FILE not enough data - waiting for complete values" + fi + + done +} + + + + diff --git a/dabo/functions/check_for_sell.sh b/dabo/functions/check_for_sell.sh new file mode 100644 index 0000000..7a1299b --- /dev/null +++ b/dabo/functions/check_for_sell.sh @@ -0,0 +1,230 @@ +function check_for_sell { + g_echo_note "RUNNING FUNCTION ${FUNCNAME} $@" + + ## sell earlier if market is bad + if [ $(echo "${f_market_performance} > ${GOOD_MARKET_PERFORMANCE_INDEX}" | bc -l) -eq 0 ] + then + g_echo_note "SELL market performance ${f_market_performance}% bad - sell earlier" + #SELL_PERCENTAGE_FROM_LAST_PURCHASE_NEGATIVE="${SELL_PERCENTAGE_FROM_LAST_PURCHASE_NEGATIVE_BAD_MARKET}" + #SELL_PERCENTAGE_FROM_LAST_PURCHASE_POSITIVE="${SELL_PERCENTAGE_FROM_LAST_PURCHASE_POSITIVE_BAD_MARKET}" + else + g_echo_note "SELL market performance ${f_market_performance}% ok" + fi + + + + # Check all balances + for f_EXCHANGE_GET_BALANCES_CMD_OUT in $(cat EXCHANGE_GET_BALANCES_CMD_OUT | egrep -v "^${CURRENCY},") + do + f_ASSET=$(echo ${f_EXCHANGE_GET_BALANCES_CMD_OUT} | cut -d, -f1) + f_QUANTITY=$(echo ${f_EXCHANGE_GET_BALANCES_CMD_OUT} | cut -d, -f2) + f_QUANTITY_CURRENCY=$(echo ${f_EXCHANGE_GET_BALANCES_CMD_OUT} | cut -d, -f3) + f_LAST_EXCHANGE_RATE=$(echo ${f_EXCHANGE_GET_BALANCES_CMD_OUT} | cut -d, -f4) + f_ASSET_HIST_FILE="asset-histories/${f_ASSET}${CURRENCY}.history.csv" + + # State for currency + g_echo "SELL ${f_ASSET}: Balance: $f_QUANTITY ($f_QUANTITY_CURRENCY $CURRENCY)" + + # check for emergency stop + local f_COMPLETE_BALANCE=$(tail -n1 "asset-histories/BALANCECOMPLETE${CURRENCY}.history.csv" | cut -d, -f2) + if [ $(echo "${f_COMPLETE_BALANCE} < ${EMERGENCY_STOP}" | bc -l) -ne 0 ] + then + local f_msg="ATTENTION! EMERGENCY STOP DUE TO POOR PERFORMANCE: BALANCE (${f_COMPLETE_BALANCE}) LOWER THEN EMERGENCY_STOP-VALUE (${EMERGENCY_STOP})" + g_echo_error "$f_msg" + do_trade ${f_ASSET} ${CURRENCY} ${f_QUANTITY_CURRENCY} sell "${f_msg}" + continue + fi + + check_sell_conditions ${f_ASSET_HIST_FILE} + + #local f_perf + #for f_perf in ${f_perf_week} ${f_perf_month} + #do + # [ -z "${f_perf}" ] && continue + # if [ $(echo "${f_perf} < ${EMERGENCY_STOP}" | bc -l) -ne 0 ] + # then + # local f_msg="ATTENTION! EMERGENCY STOP DUE TO POOR PERFORMANCE: LOWER THEN EMERGENCY_STOP-VALUE (${EMERGENCY_STOP}) (WEEK: ${f_perf_week}; MONTH: ${f_perf_month})" + # g_echo_error "$f_msg" + # do_trade ${f_ASSET} ${CURRENCY} ${f_QUANTITY_CURRENCY} sell "${f_msg}" + # continue 2 + # fi + #done + + ## check for DONT_SELL_IF_GROWTH_OVER + #local f_condition + #for f_condition in $DONT_SELL_IF_GROWTH_OVER + #do + # local f_min=$(echo ${f_condition} | cut -d'>' -f1) + # local f_percentage=$(echo ${f_condition} | cut -d'>' -f1) + # get_rate_percentage_min_before_and_now ${f_ASSET} ${CURRENCY} ${f_min} || continue + # if [ $(echo "${f_exchange_rate_diff_percentage} > ${f_percentage}" | bc -l) -ne 0 ] + # then + # g_echo_note "SELL ${f_ASSET}: growth rate last ${f_min} minute(s) was over ${f_percentage}% (${f_exchange_rate_diff_percentage})" + # continue 2 + # fi + #done + + + ### from here: check for defined state to sell + # Reset g_SELL for next round +# f_SELL="" +# +# +# ## check current result +# f_TRADE_HIST_FILE="trade-histories/${f_ASSET}${CURRENCY}.history.csv" +# if [ -s "${f_TRADE_HIST_FILE}" ] +# then +# f_BUY_PRICE=$(grep ",buy," $f_TRADE_HIST_FILE | tail -n1 | cut -d, -f5) +# f_BUY_PRICE_LAST_RATE_DIFF=$(g_percentage-diff $f_BUY_PRICE $f_LAST_EXCHANGE_RATE) +# # Store for overview +# echo ${f_BUY_PRICE_LAST_RATE_DIFF} >DIFF_BUY_PRICE_${f_ASSET}${CURRENCY} +# +# +# # check for SELL_PERCENTAGE_FROM_LAST_PURCHASE_NEGATIVE +# if [ $(echo "${f_BUY_PRICE_LAST_RATE_DIFF} < ${SELL_PERCENTAGE_FROM_LAST_PURCHASE_NEGATIVE}" | bc -l) -ne 0 ] +# then +# f_SELL="Selling ${f_QUANTITY_CURRENCY} ${CURRENCY} - SELL_PERCENTAGE_FROM_LAST_PURCHASE_NEGATIVE(${SELL_PERCENTAGE_FROM_LAST_PURCHASE_NEGATIVE}%) > Last rate (${f_BUY_PRICE_LAST_RATE_DIFF}%)" +# do_trade ${f_ASSET} ${CURRENCY} ${f_QUANTITY_CURRENCY} sell "${f_SELL}" +# continue +# else +# g_echo_note "SELL ${f_ASSET}: compare current price $f_LAST_EXCHANGE_RATE and price at the time of purchase $f_BUY_PRICE (${f_BUY_PRICE_LAST_RATE_DIFF}): over SELL_PERCENTAGE_FROM_LAST_PURCHASE_NEGATIVE(${SELL_PERCENTAGE_FROM_LAST_PURCHASE_NEGATIVE}%)" +# fi + + +# # check for SELL_PERCENTAGE_FROM_LAST_PURCHASE_POSITIVE +# if [ $(echo "${f_BUY_PRICE_LAST_RATE_DIFF} < ${SELL_PERCENTAGE_FROM_LAST_PURCHASE_POSITIVE}" | bc -l) -ne 0 ] +# then +# g_echo_note "SELL ${f_ASSET}: Not doing further checking - compare current price $f_LAST_EXCHANGE_RATE and price at the time of purchase $f_BUY_PRICE (${f_BUY_PRICE_LAST_RATE_DIFF}): under SELL_PERCENTAGE_FROM_LAST_PURCHASE_POSITIVE(${SELL_PERCENTAGE_FROM_LAST_PURCHASE_POSITIVE}%)" +# continue +# elif [ $(echo "${f_market_performance} > ${GOOD_MARKET_PERFORMANCE_INDEX}" | bc -l) -eq 0 ] +# then +# # check for SELL_PERCENTAGE_FROM_LAST_PURCHASE_POSITIVE if on bad market +# local f_msg="SELL ${f_ASSET}: Sell because over SELL_PERCENTAGE_FROM_LAST_PURCHASE_POSITIVE_BAD_MARKET (${SELL_PERCENTAGE_FROM_LAST_PURCHASE_POSITIVE_BAD_MARKET}) $f_BUY_PRICE (${f_BUY_PRICE_LAST_RATE_DIFF} in bad market" +# g_echo_note "$f_msg" +# do_trade ${f_ASSET} ${CURRENCY} ${f_QUANTITY_CURRENCY} sell "${f_msg}" +# continue +# fi + + +# # check for SELL_PERCENTAGE_FROM_LAST_PURCHASE_SOFT +# if [ $(echo "${f_BUY_PRICE_LAST_RATE_DIFF} > ${SELL_PERCENTAGE_FROM_LAST_PURCHASE_SOFT}" | bc -l) -ne 0 ] +# then +# f_SELL="Selling ${f_QUANTITY_CURRENCY} ${CURRENCY} - SELL_PERCENTAGE_FROM_LAST_PURCHASE_SOFT(${SELL_PERCENTAGE_FROM_LAST_PURCHASE_SOFT}%) > Last rate (${f_BUY_PRICE_LAST_RATE_DIFF}%)" +# else +# g_echo_note "SELL ${f_ASSET}: compare current price $f_LAST_EXCHANGE_RATE and price at the time of purchase $f_BUY_PRICE (${f_BUY_PRICE_LAST_RATE_DIFF}) not over SELL_PERCENTAGE_FROM_LAST_PURCHASE_SOFT(${SELL_PERCENTAGE_FROM_LAST_PURCHASE_SOFT}%)" +# fi +# else +# g_echo_warn "SELL ${f_ASSET}: Missing TRADE_HIST_FILE (${f_TRADE_HIST_FILE})" +# fi + + +# ## check for SELL_PERCENTAGE_HARD +# get_rate_percentage_min_before_and_now $f_ASSET $CURRENCY 15 || continue +# if [ $(echo "${f_exchange_rate_diff_percentage} < ${SELL_PERCENTAGE_HARD}" | bc -l) -ne 0 ] +# then +# f_SELL="SELLING ${f_QUANTITY_CURRENCY} ${CURRENCY} - SELL_PERCENTAGE_HARD(${SELL_PERCENTAGE_HARD}%) > 15 minutes rate (${f_exchange_rate_diff_percentage}%)" +# else +# g_echo_note "SELL ${f_ASSET}: Rate change 15 minutes ago (${f_exchange_rate_diff_percentage}) not over SELL_PERCENTAGE_HARD(${SELL_PERCENTAGE_HARD}%)" +# fi + + + + + ## check for SELL_PERCENTAGE_SOFT + # go through time intervals +# for f_min in ${MINUTE_STEPS_SELL} +# do +# # calculate rate change last defined minutes +# get_rate_percentage_min_before_and_now $f_ASSET $CURRENCY $f_min || continue +# if [ $(echo "${f_exchange_rate_diff_percentage} < ${SELL_PERCENTAGE_SOFT}" | bc -l) -ne 0 ] +# then +# f_SELL="SELLING ${f_QUANTITY_CURRENCY} ${CURRENCY} - SELL_PERCENTAGE_SOFT(${SELL_PERCENTAGE_SOFT}%) > $f_min minutes rate (${f_exchange_rate_diff_percentage}%)" +# break +# else +# g_echo_note "SELL ${f_ASSET}: Rate change $f_min ago (${f_exchange_rate_diff_percentage}) not over SELL_PERCENTAGE_SOFT(${SELL_PERCENTAGE_SOFT}%)" +# fi +# done + + # dont sell if lock exists + #if [ -s "${f_ASSET}${CURRENCY}-sell.lock" ] + #then + # g_echo_note "SELL ${f_ASSET}: Not doing further checking - ${f_ASSET}${CURRENCY}-sell.lock exists and not older then 30 minutes" + # continue + #fi + + # check last 5 minutes - if growth over 0.5% then hold (important for rally prices + #get_rate_percentage_min_before_and_now ${f_ASSET} ${CURRENCY} 5 || continue + #[ $(echo "${f_exchange_rate_diff_percentage} > 0.5" | bc -l) -ne 0 ] && continue + +# # check last 10 minutes - if loss over -1% then sell +# get_rate_percentage_min_before_and_now ${f_ASSET} ${CURRENCY} 10 +# if [ $(echo "${f_exchange_rate_diff_percentage} < -1" | bc -l) -ne 0 ] +# then +# f_SELL="SELL ${f_ASSET}: 10min Loss ${f_exchange_rate_diff_percentage} higher then -1%" +# g_echo_note "$f_SELL" +# fi +# +# # Sell on RSI higher then RSI_SELL_SIGNAL_FROM_FORCE +# local f_rsi=$(tail -n1 ${f_ASSET_HIST_FILE} | cut -d, -f3) +# if [ ${f_rsi} -ge ${RSI_SELL_SIGNAL_FROM_FORCE} ] && [ ${f_rsi} -lt 100 ] +# then +# f_SELL="SELL ${f_ASSET}: RSI: $f_rsi" +# g_echo_note "$f_SELL" +# fi +# +# +# # check MACD and RSI +# local f_macd=$(tail -n1 ${f_ASSET_HIST_FILE} | cut -d, -f7) +# g_echo_note "SELL ${f_ASSET}: Checking for indicators: RSI $f_rsi ; MACD: $f_macd" +# if [ $(echo "${f_macd} < 0" | bc -l) -ne 0 ] +# then +# g_echo_note "SELL ${f_ASSET}: trend change RSI $f_rsi ; MACD: $f_macd" +# if [ $(echo "${f_rsi} > 60" | bc -l) -ne 0 ] +# then +# f_SELL="SELL ${f_ASSET}: RSI: $f_rsi ; MACD: $f_macd" +# g_echo_note "$f_SELL" +# fi +# fi + + + +# # check for SELL_CONDITION and RSI Indicator +# local f_condition +# local f_loopcount=0 +# local f_conditioncount=0 +# for f_condition in $SELL_CONDITION +# do +# local f_min=$(echo ${f_condition} | cut -d'<' -f1) +# local f_percentage=$(echo ${f_condition} | cut -d'<' -f2) +# get_rate_percentage_min_before_and_now ${f_ASSET} ${CURRENCY} ${f_min} || continue 2 +# if [ $(echo "${f_exchange_rate_diff_percentage} < ${f_percentage}" | bc -l) -ne 0 ] +# then +# g_echo_note "SELL ${f_ASSET}: Price change met SELL_CONDITION (decrease of ${f_exchange_rate_diff_percentage}% higher then ${f_percentage}% in ${f_min} minutes." +# ((f_conditioncount=f_conditioncount+1)) +# else +# g_echo_note "SELL ${f_ASSET}: Price change didn't meet SELL_CONDITION (decrease of ${f_exchange_rate_diff_percentage}% lower then ${f_percentage}% in ${f_min} minutes." +# fi +# ((f_loopcount=f_loopcount+1)) +# done +# if [ $f_conditioncount -eq $f_loopcount ] +# then +# if [ ${f_rsi} -ge ${RSI_SELL_SIGNAL_FROM} ] +# then +# f_SELL="SELL ${f_ASSET}: Price change met all SELL_CONDITIONs ${SELL_CONDITION} ($f_conditioncount of $f_loopcount) with RSI ${f_rsi}" +# g_echo_note "$f_SELL" +# fi +# else +# g_echo_note "SELL ${f_ASSET}: Price change didn't meet SELL_CONDITIONs ${SELL_CONDITION} ($f_conditioncount of $f_loopcount)" +# fi + +# # Sell or not sell? +# if [ -n "$f_SELL" ] +# then +# do_trade ${f_ASSET} ${CURRENCY} ${f_QUANTITY_CURRENCY} sell "${f_SELL}" +# fi + + done + + + +} diff --git a/dabo/functions/check_sell_conditions.sh b/dabo/functions/check_sell_conditions.sh new file mode 100644 index 0000000..c77012e --- /dev/null +++ b/dabo/functions/check_sell_conditions.sh @@ -0,0 +1,143 @@ +function check_sell_conditions { + + local f_ASSET_HIST_FILE="$1" + f_ASSET=$(basename ${f_ASSET_HIST_FILE} | cut -d\. -f1) + + ### from here: check for defined state to sell + f_SELL="" + + # get data + get_vars_from_csv ${f_ASSET_HIST_FILE} || return 1 + + ### check current result + if [ -n "${BOT}" ] + then + f_TRADE_HIST_FILE="$(ls -1tr trade-histories/trade-*${f_ASSET}-open.history.csv | tail -n1)" + if ! [ -s "${f_TRADE_HIST_FILE}" ] + then + g_echo_note "SELL ${f_ASSET}: No trade history file (${f_TRADE_HIST_FILE}) found - ignoring" + return 0 + fi + f_TRADE_HIST_FILE_INTERIM=$(echo ${f_TRADE_HIST_FILE} | sed 's/-open\.history\.csv$/-interim.history.csv/') + local f_BUY_PRICE=$(grep ',BUY' $f_TRADE_HIST_FILE | tail -n1 | cut -d, -f5) + local f_BUY_PRICE_LAST_RATE_DIFF=$(g_percentage-diff ${f_BUY_PRICE} ${f_price}) + # Store for overview + echo ${f_BUY_PRICE_LAST_RATE_DIFF} >DIFF_BUY_PRICE_${f_ASSET} + else + f_TRADE_HIST_FILE="${g_tmp}/open-${tmpfile}" + f_TRADE_HIST_FILE_INTERIM="${g_tmp}/interim-${tmpfile}" + f_BUY_PRICE_LAST_RATE_DIFF=$(g_percentage-diff ${BUY_PRICE} ${f_price}) + f_BUY_PRICE=${BUY_PRICE} + result=${f_BUY_PRICE_LAST_RATE_DIFF} + echo "${f_last_line}" + echo "INTERIM RESULT: ${f_BUY_PRICE_LAST_RATE_DIFF}%" + fi + + + # check if the result (profit/loss until now) is lowering and sell if too low () + if [ -f ${f_TRADE_HIST_FILE_INTERIM} ] + then + local f_BUY_PRICE_2ND_LAST_RATE_DIFF=$(tail -n1 ${f_TRADE_HIST_FILE_INTERIM}) + local f_diff_result=$(echo "${f_BUY_PRICE_LAST_RATE_DIFF} - (${f_BUY_PRICE_2ND_LAST_RATE_DIFF})" | bc | sed 's/^\./0./; s/^-\./-0./') + if [ $(echo "${f_diff_result} < ${SELL_IF_LAST_RATE_LOWER_THEN}" | bc -l) -ne 0 ] && [ $(echo "${f_BUY_PRICE_LAST_RATE_DIFF} > ${FEE}" | bc -l) -ne 0 ] + then + f_SELL="Loss between last (${f_BUY_PRICE_LAST_RATE_DIFF}%) and 2nd last (${f_BUY_PRICE_2ND_LAST_RATE_DIFF}%) rate/result more then ${SELL_IF_LAST_RATE_LOWER_THEN}% (${f_diff_result}%)" + fi + + # Sell if the last X time units is lower then FEE + if [ $(cat ${f_TRADE_HIST_FILE_INTERIM} | wc -l) -ge ${SELL_IF_LOWER_THEN_FEE_AFTER_PERIOD} ] + then + local f_BUY_PRICE_XND_LAST_RATE_DIFF=$(tail -n${SELL_IF_LOWER_THEN_FEE_AFTER_PERIOD} ${f_TRADE_HIST_FILE_INTERIM} | head -n1) + local f_diff_result=$(echo "${f_BUY_PRICE_LAST_RATE_DIFF} - (${f_BUY_PRICE_XND_LAST_RATE_DIFF})" | bc) + if [ $(echo "${f_diff_result} < ${FEE}" | bc -l) -ne 0 ] + then + f_SELL="Loss between last rate (${f_BUY_PRICE_LAST_RATE_DIFF}%) in ${SELL_IF_LOWER_THEN_FEE_AFTER_PERIOD} time units before more then ${FEE} (${f_diff_result}%)" + fi + fi + fi + + # store new interim result + echo ${f_BUY_PRICE_LAST_RATE_DIFF} >>${f_TRADE_HIST_FILE_INTERIM} + + + # Sell on MACD Condition + if [ $(echo "${f_macd_histogram_relation} < ${SELL_MACD_RELATION_FROM}" | bc -l) -ne 0 ] + then + f_SELL="MACD condition met" + fi + + + # Sell on RSI conditions + if \ + [ ${f_rsi5} -le ${SELL_RSI5_SIGNAL_UNTIL} ] && \ + [ ${f_rsi14} -le ${SELL_RSI14_SIGNAL_UNTIL} ] && \ + [ ${f_rsi21} -le ${SELL_RSI21_SIGNAL_UNTIL} ] && \ + [ ${f_rsi60} -le ${SELL_RSI60_SIGNAL_UNTIL} ] && \ + [ ${f_rsi120} -le ${SELL_RSI120_SIGNAL_UNTIL} ] && \ + [ ${f_rsi240} -le ${SELL_RSI240_SIGNAL_UNTIL} ] && \ + [ ${f_rsi420} -le ${SELL_RSI420_SIGNAL_UNTIL} ] && \ + [ ${f_rsi720} -le ${SELL_RSI720_SIGNAL_UNTIL} ]&& \ + [ ${f_rsi5} -ge ${SELL_RSI5_SIGNAL_FROM} ] && \ + [ ${f_rsi14} -ge ${SELL_RSI14_SIGNAL_FROM} ] && \ + [ ${f_rsi21} -ge ${SELL_RSI21_SIGNAL_FROM} ] && \ + [ ${f_rsi60} -ge ${SELL_RSI60_SIGNAL_FROM} ] && \ + [ ${f_rsi120} -ge ${SELL_RSI120_SIGNAL_FROM} ] && \ + [ ${f_rsi240} -ge ${SELL_RSI240_SIGNAL_FROM} ] && \ + [ ${f_rsi420} -ge ${SELL_RSI420_SIGNAL_FROM} ] && \ + [ ${f_rsi720} -ge ${SELL_RSI720_SIGNAL_FROM} ] + then + f_SELL="RSI conditions met" + fi + + + # hold on negative result + if [ "${SELL_HOLD_IF_RESULT_NEGATIVE}" -gt 0 ] + then + if [ $(echo "(${f_BUY_PRICE_LAST_RATE_DIFF} - ${FEE}) < 0" | bc -l) -ne 0 ] + then + g_echo_note "Result negative - holding (INTERIM RESULT ${f_BUY_PRICE_LAST_RATE_DIFF}% - FEE ${FEE}% < 0" + f_SELL="" + fi + fi + + + # check for SELL_PERCENTAGE_FROM_LAST_PURCHASE_NEGATIVE + if [ $(echo "${f_BUY_PRICE_LAST_RATE_DIFF} < ${SELL_PERCENTAGE_FROM_LAST_PURCHASE_NEGATIVE}" | bc -l) -ne 0 ] + then + f_SELL="SELL_PERCENTAGE_FROM_LAST_PURCHASE_NEGATIVE(${SELL_PERCENTAGE_FROM_LAST_PURCHASE_NEGATIVE}%) > Last rate (${f_BUY_PRICE_LAST_RATE_DIFF}%)" + fi + + + + [ -n "$f_SELL" ] && f_SELL="SELL ${f_ASSET}@${CURRENCY}:${f_price}: $f_SELL" + + ### Sell or not sell? + # BOT + if [ -n "$f_SELL" ] && [ -n "${BOT}" ] + then + g_echo_note "$f_SELL" + echo "${f_last_line},${f_ASSET}" >>trade.log + f_ASSET=$(echo ${f_ASSET} | sed "s/${CURRENCY}//") + # binance_convert ${f_ASSET} ${CURRENCY} ${f_QUANTITY} sell "${f_SELL}" + if [ ${STOCK_EXCHANGE} = "BINANCE" ] + then + binance_convert ${f_ASSET} ${CURRENCY} ${f_QUANTITY} sell "${f_SELL}" ${f_QUANTITY_CURRENCY} || \ + do_trade ${f_ASSET} ${CURRENCY} ${f_QUANTITY_CURRENCY} sell "${f_SELL}" + else + do_trade ${f_ASSET} ${CURRENCY} ${f_QUANTITY_CURRENCY} sell "${f_SELL}" + fi + fi + + # ANALYZE + if [ -n "${f_SELL}" ] && [ -z "${BOT}" ] + then + echo "SELL: $(tail -n1 ${f_ASSET_HIST_FILE} | cut -d, -f1) === $f_SELL" + result=$(g_percentage-diff ${BUY_PRICE} ${current}) + result=$(echo "${result}-${FEE}" | bc | sed 's/^\./0./; s/^-\./-0./') + echo "${result}" >>${g_tmp}/result-${tmpfile} + rm -f "${f_TRADE_HIST_FILE}" + rm -f "${f_TRADE_HIST_FILE_INTERIM}" + echo "RESULT: ${result}% (${BUY_PRICE} -> ${current})" + fi + +} diff --git a/dabo/functions/do_trade.sh b/dabo/functions/do_trade.sh new file mode 100644 index 0000000..7012efc --- /dev/null +++ b/dabo/functions/do_trade.sh @@ -0,0 +1,134 @@ +function do_trade { + # Info for log + g_echo_note "RUNNING FUNCTION ${FUNCNAME} $@" + + # needed vars + local f_ASSET=$1 + local f_CURRENCY=$2 + local f_QUANTITY=$3 + local f_ACTION=$4 # buy or sell + local f_COMMENT=$5 + + local f_DATE=$(date '+%F_%H-%M-%S') + local f_ASSET_HIST_FILE="asset-histories/${f_ASSET}${f_CURRENCY}.history.csv" + + 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 ] + 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 + + if [ "${f_ACTION}" = "buy" ] + then + # check for enough balance for trade + get_balances + local f_CURRENCY_BALANCE=$(egrep "^${f_CURRENCY}," EXCHANGE_GET_BALANCES_CMD_OUT | cut -d"," -f2) + g_echo_note "Checking for enough balance for trade (${f_CURRENCY_BALANCE} > ${f_QUANTITY})" + if [ $(echo "${f_CURRENCY_BALANCE} > ${f_QUANTITY}" | bc -l) -eq 0 ] + 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" + 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} + # trade + g_echo_note "Command: $(cat ${f_CMDFILE})" + #g_runcmd g_retrycmd sh ${f_CMDFILE} >${f_CMDFILE}_OUT + . ${f_CMDFILE} + cat ${g_tmp}/API_CMD_OUT >${f_CMDFILE}_OUT + g_echo_note "Command Output: $(cat ${f_CMDFILE}_OUT)" + + # workaround for "insufficient balance" error. lower quantity in 0.1 steps and try again (for values loosing while selling) + if [ "${f_ACTION}" = "sell" ] && grep -q "Account has insufficient balance for requested action." ${f_CMDFILE}_OUT + then + g_echo_note "workaround for \"insufficient balance\" error." + local f_tries=10 + local f_try=1 + until (grep -q "FILLED" ${f_CMDFILE}_OUT) + do + 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} + #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=f_try+1)) + done + fi + + + # Check return and log trade + f_STATUS=$(cat ${f_CMDFILE}_OUT | grep '^{' | jq -r .status) + + local f_trade_info_msg="TRADE - ${f_ACTION} ${f_ASSET}${f_CURRENCY} +${f_link} + +Complete Overview: https://bot.ds9.dedyn.io/ + +Comment: ${f_COMMENT}" + + if [ "${f_STATUS}" = "FILLED" ] + 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) + 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 + if echo ${f_COMMENT} | grep -q "Workaround for selling values under QUANTITY_LOT_CUT" + then + echo "${f_DATE},${f_ACTION},${f_CMDFILE}_OUT,${f_QUANTITY} ${f_CURRENCY},${f_PRICE},${f_COMMISSION} ${f_COMMISSIONASSET},${f_COMMENT}" | head -n1 >>trade-histories/trade-*${f_ASSET}${f_CURRENCY}-open.history.csv + else + echo "${f_DATE},${f_ACTION},${f_CMDFILE}_OUT,${f_QUANTITY} ${f_CURRENCY},${f_PRICE},${f_COMMISSION} ${f_COMMISSIONASSET},${f_COMMENT}" | head -n1 >>trade-histories/trade-$(date +%F.%T. | sed 's/:/_/g')${f_ASSET}${f_CURRENCY}-open.history.csv + fi + fi + if [ "${f_ACTION}" = "sell" ] + then + f_tradehistfile="$(ls trade-histories/trade-*${f_ASSET}${f_CURRENCY}-open.history.csv | tail -n1)" + echo "${f_DATE},${f_ACTION},${f_CMDFILE}_OUT,${f_QUANTITY} ${f_CURRENCY},${f_PRICE},${f_COMMISSION} ${f_COMMISSIONASSET},${f_COMMENT}" | head -n1 >>${f_tradehistfile} + f_tradehistfileclosed=$(echo ${f_tradehistfile} | sed 's/open.history.csv/closed.history.csv/') + mv ${f_tradehistfile} ${f_tradehistfileclosed} + fi + g_signal-notify "TRADE SUCCESSFUL! + +${f_trade_info_msg} + +Command stored: ${f_CMDFILE}[_OUT]" + [ -f DIFF_BUY_PRICE_${f_ASSET}${f_CURRENCY} ] && [ "${f_ACTION}" = "sell" ] && rm -f DIFF_BUY_PRICE_${f_ASSET}${f_CURRENCY} + # get new balances + get_balances + else + g_echo_note "TRADE FAILED! +$(cat ${f_CMDFILE}_OUT)" + g_signal-notify "TRADE FAILED! +${f_trade_info_msg} + +Command ${f_CMDFILE}: +$0 $@ +$(cat ${f_CMDFILE}) + +OUTPUT: +$(cat ${f_CMDFILE}_OUT) +" + fi +} diff --git a/dabo/functions/get_asset.sh b/dabo/functions/get_asset.sh new file mode 100644 index 0000000..e7133ca --- /dev/null +++ b/dabo/functions/get_asset.sh @@ -0,0 +1,83 @@ +function get_asset { + + g_echo_note "RUNNING FUNCTION ${FUNCNAME} $@" + local f_ASSET="$1" + + # write asset hist file with macd and rsi + local f_ASSET_HIST_FILE="asset-histories/${f_ASSET}.history-raw.csv" + [ -f "${f_ASSET_HIST_FILE}" ] || echo "Date and Time,Price" >"${f_ASSET_HIST_FILE}" + if ! grep -q "^$(echo "${f_timestamp}" | cut -d: -f1,2)" "${f_ASSET_HIST_FILE}" + then + local f_line="${f_timestamp},$(egrep "^${f_ASSET}," EXCHANGE_GET_ASSETS_CMD_OUT | cut -d, -f2)" + echo "${f_line}" >>${f_ASSET_HIST_FILE} + + local f_linecount=0 + local f_last_price=0 + local f_lines="$(tail -n 51 "${f_ASSET_HIST_FILE}" | wc -l)" + + for f_price in $(tail -n 50 "${f_ASSET_HIST_FILE}" | grep "^[0-9]" | cut -d, -f2) + do + if [ "$f_last_price" == "$f_price" ] + then + continue + fi + let "f_linecount+=1" + f_last_price=$f_price + done + + if [ ${f_linecount} -le 3 ] && [ ${f_lines} -ge 50 ] + then + g_echo_note "${f_ASSET_HIST_FILE}: price seems not to change - ignoring" + return 0 + fi + + f_ASSET_HIST_FILE="asset-histories/${f_ASSET}.history.csv" + if [ ${f_linecount} -lt 30 ] && [ ${f_lines} -ge 50 ] + then + g_echo_note "${f_ASSET_HIST_FILE}: set price to 5 minute period" + if ! echo "${f_timestamp}" | egrep -q ":[0-5]0:|:[0-5]5:" + then + return 0 + fi + fi + + # headline + [ -s "${f_ASSET_HIST_FILE}" ] || echo 'Date and Time,Price,Price Change,EMA12,EMA26,MACD,EMA9 MACD (Signal),MACD Histogram,MACD Signal,RSI5,RSI14,RSI21,RSI720,RSI60,RSI120,RSI240,RSI420,Price Change 24h,Price Change 7d,Price Change 14d,Price Change 30d' >"${f_ASSET_HIST_FILE}" + + # date and price + echo -n "${f_line}" >>${f_ASSET_HIST_FILE} + + # calculate price change percentage + f_last_price=$(tail -n2 ${f_ASSET_HIST_FILE} | head -n1 | cut -d, -f2) + if echo $f_last_price | grep -q "^[0-9]" + then + f_price=$(tail -n1 ${f_ASSET_HIST_FILE} | cut -d, -f2) + local f_price_change=$(g_percentage-diff ${f_last_price} ${f_price}) + else + local f_price_change="" + fi + echo -n ",${f_price_change}" >>"${f_ASSET_HIST_FILE}" + + # calculate macd and rsi + get_macd_indicator ${f_ASSET_HIST_FILE} + get_rsi_indicator ${f_ASSET_HIST_FILE} 5 + get_rsi_indicator ${f_ASSET_HIST_FILE} 14 + get_rsi_indicator ${f_ASSET_HIST_FILE} 21 + get_rsi_indicator ${f_ASSET_HIST_FILE} 720 + get_rsi_indicator ${f_ASSET_HIST_FILE} 60 + get_rsi_indicator ${f_ASSET_HIST_FILE} 120 + get_rsi_indicator ${f_ASSET_HIST_FILE} 240 + get_rsi_indicator ${f_ASSET_HIST_FILE} 480 + + # get coingecko price change + local f_asset=$(echo ${f_ASSET} | sed "s/${CURRENCY}\$//" | tr '[:upper:]' '[:lower:]') + echo -n ,$(jq -r ".[] |select(.symbol==\"${f_asset}\")|\"\\(.price_change_percentage_24h_in_currency)\"" COINGECKO_GET_ASSETS_CMD_OUT) >>${f_ASSET_HIST_FILE} + echo -n ,$(jq -r ".[] |select(.symbol==\"${f_asset}\")|\"\\(.price_change_percentage_7d_in_currency)\"" COINGECKO_GET_ASSETS_CMD_OUT) >>${f_ASSET_HIST_FILE} + echo -n ,$(jq -r ".[] |select(.symbol==\"${f_asset}\")|\"\\(.price_change_percentage_14d_in_currency)\"" COINGECKO_GET_ASSETS_CMD_OUT) >>${f_ASSET_HIST_FILE} + echo -n ,$(jq -r ".[] |select(.symbol==\"${f_asset}\")|\"\\(.price_change_percentage_30d_in_currency)\"" COINGECKO_GET_ASSETS_CMD_OUT) >>${f_ASSET_HIST_FILE} + + # end with newline + echo "" >>${f_ASSET_HIST_FILE} + fi +} + diff --git a/dabo/functions/get_assets.sh b/dabo/functions/get_assets.sh new file mode 100644 index 0000000..50a0874 --- /dev/null +++ b/dabo/functions/get_assets.sh @@ -0,0 +1,150 @@ +function get_assets { + + g_echo_note "RUNNING FUNCTION ${FUNCNAME} $@" + + ## determine assets with prices + local f_filename="EXCHANGE_GET_ASSETS_CMD" + # Try to collect assets + # get data from API + 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]*$" >${f_filename}_OUT.tmp + fi + # timestamp for data + f_timestamp=$(g_date_print) + # check output + if [ -s ${f_filename}_OUT.tmp ] && egrep -q "^[A-Z]+${CURRENCY},[0-9]*\.[0-9]+$" ${f_filename}_OUT.tmp + then + mv ${f_filename}_OUT.tmp ${f_filename}_OUT + else + g_echo_warn "${f_filename}_OUT.tmp has wrong Syntax. - Not updating ${f_filename}_OUT $(tail -n 10 ${f_filename}_OUT.tmp)" + return 2 + fi + + ## write assets list and filter by marketcap and sort by price + # get krypto symbol list sorted by marketcap + echo "curl -s -X 'GET' \"https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=1&sparkline=false&price_change_percentage=1h,24h,7d,14d,30d\" -H 'accept: application/json'" >COINGECKO_GET_ASSETS_CMD + g_runcmd g_retrycmd sh COINGECKO_GET_ASSETS_CMD >COINGECKO_GET_ASSETS_CMD_OUT || return 1 +#ASSET_MARKETCAP_OUT.tmp || return 1 + if [ -s COINGECKO_GET_ASSETS_CMD_OUT ] && grep -q "market_cap_rank" COINGECKO_GET_ASSETS_CMD_OUT + then + + # get marketcap sort + jq -r '.[].symbol' COINGECKO_GET_ASSETS_CMD_OUT | tr [:lower:] [:upper:] | head -n $LARGEST_MARKETCAP >ASSET_MARKETCAP_OUT.tmp-sort + if [ -s ASSET_MARKETCAP_OUT.tmp-sort ] && egrep -q "^[A-Z0-9]+$" ASSET_MARKETCAP_OUT.tmp-sort + then + mv ASSET_MARKETCAP_OUT.tmp-sort SORTMARKETCAP + else + g_echo_warn "ASSET_MARKETCAP_OUT.tmp-sort has wrong Syntax. - Not updating ASSET_MARKETCAP_OUT.tmp-sort $(tail -n 10 ASSET_MARKETCAP_OUT.tmp-sort)" + return 2 + fi + + # write down 24h pricechange + cat COINGECKO_GET_ASSETS_CMD_OUT | jq -r '.[] | .symbol + "," + (.price_change_percentage_24h|tostring)' | tr [:lower:] [:upper:] >ASSET_PRICE_CHANGE_PERCENTAGE_24H + + # store coingecko symbolids for coingecko info URLs + jq -r '.[] | .symbol + "," + .id' COINGECKO_GET_ASSETS_CMD_OUT >COINGECKO_IDS.tmp + if [ -s COINGECKO_IDS.tmp ] + then + mv COINGECKO_IDS.tmp COINGECKO_IDS + else + g_echo_warn "COINGECKO_IDS.tmp has wrong Syntax. - Not updating COINGECKO_IDS $(tail -n 10 COINGECKO_IDS.tmp)" + return 2 + fi + fi + + + # Write file with asset list and ignore marketcap under LARGEST_MARKETCAP + cat EXCHANGE_GET_ASSETS_CMD_OUT | cut -d"," -f1 | while read f_ASSET + do + # ignore marketcap under LARGEST_MARKETCAP + f_assetwocurrency=$(echo ${f_ASSET} | sed "s/$CURRENCY$//") + if ! egrep -q "^${f_assetwocurrency}$" SORTMARKETCAP + then + #g_echo_note "Ignoring $f_ASSET because of lower marketcap then Top ${LARGEST_MARKETCAP}" + continue + fi + echo ${f_ASSET} >>ASSETS.tmp + done + mv ASSETS.tmp ASSETS + + + ## write histfiles by having a look on marketcap + #>ASSETS_HIGHEST.tmp + # get alread invested assets + #f_investedassets_regex=$(cat EXCHANGE_GET_BALANCES_CMD_OUT | cut -d, -f1 | perl -pe 's/^/\^/; s/\n/\|/' | perl -pe 's/\|$//') + local f_ASSET + + + local f_parallel_arg + echo -n "parallel -j3 bash -c --" >/tmp/parallel + for f_ASSET in $(cat ASSETS | egrep -v "${BLACKLIST}") + do + #get_asset "${f_ASSET}" + echo -n " \"get_asset ${f_ASSET}\"" >>/tmp/parallel + done + export -f get_asset + export f_timestamp + . /tmp/parallel + + # cleanup trashlines (possibly generated by kill further of this progress) + sed -i "/[0-9]$(date +%Y)-/d" asset-histories/* + + #cat ASSETS | while read f_ASSET + #do + # # write asset hist file with macd and rsi + # local f_ASSET_HIST_FILE="asset-histories/${f_ASSET}.history.csv" + # [ -f "${f_ASSET_HIST_FILE}" ] || touch "${f_ASSET_HIST_FILE}" + # if ! grep -q "^$(echo "${f_timestamp}" | cut -d: -f1,2)" "${f_ASSET_HIST_FILE}" + # then + # local f_line="${f_timestamp},$(egrep "^${f_ASSET}," EXCHANGE_GET_ASSETS_CMD_OUT | cut -d, -f2)" + # #g_echo_note "Writing new line (${f_line}) to history ${ASSET_HIST_FILE}" + # echo -n "${f_line}" >>${f_ASSET_HIST_FILE} + # get_macd_indicator ${f_ASSET_HIST_FILE} + # get_rsi_indicator ${f_ASSET_HIST_FILE} 30 + # echo ",${f_macd},${f_rsi}" >>${f_ASSET_HIST_FILE} + # fi + + +# cat ASSETS | while read f_ASSET +# do +# # ignore already invested asset +# if echo "$f_ASSET" | egrep -q "$f_investedassets_regex" +# then +# g_echo_note "Already invested in $f_ASSET - ignoring for more diversification" +# continue +# fi +# +# # sort latest assets prices by highest in last timeframe (ignore - or 0) +# get_rate_percentage_min_before_and_now $(echo ${f_ASSET} | sed "s/${CURRENCY}\$//") ${CURRENCY} 1 || continue +# local f_ASSET_PERCENTAGE=${f_exchange_rate_diff_percentage} +# echo "$f_ASSET_PERCENTAGE" | egrep -q '^-|^0.0' && continue +# g_echo "adding ${f_ASSET}({$f_ASSET_PERCENTAGE}) to ASSETS_HIGHEST because marketcap in Top ${LARGEST_MARKETCAP}" +# echo "${f_ASSET_PERCENTAGE},${f_ASSET}" >>ASSETS_HIGHEST.tmp +# done +# cat ASSETS_HIGHEST.tmp | sort -r >ASSETS_HIGHEST.tmp2 +# mv ASSETS_HIGHEST.tmp2 ASSETS_HIGHEST +# rm ASSETS_HIGHEST.tmp + + # get MSCI World Index for analysis + echo "wget -q -O - https://www.boerse.de/realtime-kurse/MSCI-World/XC0009692739 | egrep 'itemprop=\"price\" content=\"[0-9]+\.[0-9]+\"' | cut -d\\\" -f6" >MSCI_WORLD_CMD + g_runcmd g_retrycmd sh MSCI_WORLD_CMD >MSCI_WORLD_CMD_OUT.tmp + # check output + if [ -s MSCI_WORLD_CMD_OUT.tmp ] && egrep -q "^[0-9]*\.[0-9]+$" MSCI_WORLD_CMD_OUT.tmp + then + if egrep -q "^0\.00$" MSCI_WORLD_CMD_OUT.tmp + then + g_echo_note "Ignoring MSCI World $(tail -n 10 MSCI_WORLD_CMD_OUT.tmp) - maybe out of business day" + else + mv MSCI_WORLD_CMD_OUT.tmp MSCI_WORLD_CMD_OUT + fi + else + g_echo_warn "MSCI_WORLD_CMD_OUT.tmp has wrong Syntax. - Not updating MSCI WORLD Index $(tail -n 10 MSCI_WORLD_CMD_OUT.tmp)" + fi + echo "${f_timestamp},$(cat MSCI_WORLD_CMD_OUT)" >>asset-histories/MSCI-WORLD-INDEX.history.csv + + +} + diff --git a/dabo/functions/get_balances.sh b/dabo/functions/get_balances.sh new file mode 100644 index 0000000..c95079b --- /dev/null +++ b/dabo/functions/get_balances.sh @@ -0,0 +1,126 @@ +function get_balances { + + g_echo_note "RUNNING FUNCTION ${FUNCNAME} $@" + + local f_filename="EXCHANGE_GET_BALANCES_CMD" + + # get data from API + if [ ${STOCK_EXCHANGE} = "BINANCE" ] + then + binance_convert_dust + 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 + fi + + if ! [ -s ${f_filename}_OUT.tmp_raw ] && egrep -q "^[A-Z]+,[0-9]*\.[0-9]+$" ${f_filename}_OUT.tmp_raw + then + g_echo_warn "${f_filename}_OUT.tmp_raw has wrong Syntax or is empty. - Not updating ${f_filename}_OUT $(tail -n 10 ${f_filename}_OUT.tmp_raw)" + return 2 + fi + + # CURRENCY is defined + if [ -n "${CURRENCY}" ] + then + + # put balance of (main) currency in global CURRENCY_BALANCE + local f_CURRENCY_BALANCE=$(egrep "^${CURRENCY}," ${f_filename}_OUT.tmp_raw | cut -d"," -f2) + if echo "${f_CURRENCY_BALANCE}" | egrep -q "^[0-9]*\.[0-9]*" + then + CURRENCY_BALANCE=${f_CURRENCY_BALANCE} + g_echo_note "Investmentbudget: $CURRENCY_BALANCE $CURRENCY" + else + g_echo_warn "Could not determine CURRENCY_BALANCE (${f_CURRENCY_BALANCE} ${CURRENCY}) from file ${f_filename}_OUT.tmp_raw $(cat ${f_filename}_OUT.tmp_raw)" + return 3 + fi + + # add balance in CURRNCY + local f_line + for f_line in $(cat ${f_filename}_OUT.tmp_raw) + do + local f_ASSET=$(echo ${f_line} | cut -d, -f1) + local f_QUANTITY=$(echo ${f_line} | cut -d, -f2) + + if [ "${f_ASSET}" = "${CURRENCY}" ] + then + g_echo_note "own currency line $f_line" + echo "${f_ASSET},${f_QUANTITY},$f_QUANTITY,1.00000000" >>${f_filename}_OUT.tmp + continue + fi + + local f_ASSET_HIST_FILE="asset-histories/${f_ASSET}${CURRENCY}.history.csv" + if ! [ -s ${f_ASSET_HIST_FILE} ] + then + g_echo_note "No history file (${f_ASSET_HIST_FILE}) found for asset (${f_ASSET}) - ignoring $f_line." + continue + fi + + # ignore non traded currencies by bot + ls trade-histories/trade-*.${f_ASSET}${CURRENCY}-open.history.csv >/dev/null 2>&1 || continue + + # get last prices + local f_LAST_EXCHANGE_PRICE=$(tail -n1 ${f_ASSET_HIST_FILE} | head -n1 | cut -d, -f2) + if ! echo "$f_LAST_EXCHANGE_PRICE" | egrep -q "^[0-9]" + then + g_echo_warn "didn't get latest price from ${f_ASSET_HIST_FILE}" + continue + fi + + # calculate quantity in CURRENCY + local f_QUANTITY_CURRENCY=$(echo "scale=8; $f_LAST_EXCHANGE_PRICE*$f_QUANTITY" | bc -l | sed 's/^\./0./;') + if ! echo "$f_QUANTITY_CURRENCY" | egrep -q "^[0-9]" + then + g_echo_warn "could not calculate quantity in ${CURRENCY}" + continue + fi + + # ignore low values + if [ $(echo "$f_QUANTITY_CURRENCY < 2" | bc -l) -ne 0 ] + then + g_echo_note "${f_ASSET}: Ignore low quantity asset ${f_ASSET}(${f_QUANTITY})" + continue + else + g_echo_note "${f_ASSET}: Going on with non-low quantity (>1) asset ${f_ASSET}(${f_QUANTITY})" + fi + + echo "${f_ASSET},${f_QUANTITY},${f_QUANTITY_CURRENCY},${f_LAST_EXCHANGE_PRICE}" >>${f_filename}_OUT.tmp + done + fi + + if [ -s ${f_filename}_OUT.tmp ] && egrep -q "^[A-Z]+,[0-9]*\.[0-9]+,[0-9]*\.[0-9]+,[0-9]*\.[0-9]+$" ${f_filename}_OUT.tmp + then + mv ${f_filename}_OUT.tmp ${f_filename}_OUT + else + g_echo_warn "${f_filename}_OUT.tmp has wrong Syntax or is empty. - Not updating ${f_filename}_OUT $(tail -n 10 ${f_filename}_OUT.tmp)" + return 2 + fi + + # get complete spot balance + local f_SPOT_BALANCE=0 + local f_EXCHANGE_GET_BALANCES_CMD_OUT + for f_EXCHANGE_GET_BALANCES_CMD_OUT in $(cat EXCHANGE_GET_BALANCES_CMD_OUT | grep -v ^$CURRENCY,) + do + #local f_ASSET=$(echo ${f_EXCHANGE_GET_BALANCES_CMD_OUT} | cut -d"," -f1) + local f_QUANTITY_CURRENCY=$(echo ${f_EXCHANGE_GET_BALANCES_CMD_OUT} | cut -d"," -f3) + local f_SPOT_BALANCE=$(echo "scale=2; $f_SPOT_BALANCE+$f_QUANTITY_CURRENCY" | bc -l) + done + + # calculate complete balance + local f_COMPLETE_BALANCE=$(echo "scale=2; $CURRENCY_BALANCE+$f_SPOT_BALANCE" | bc -l) + local f_timestamp=$(g_date_print) + + # write balance history + echo "$f_timestamp,$f_COMPLETE_BALANCE" >>"asset-histories/BALANCECOMPLETE${CURRENCY}.history.csv" + echo "$f_timestamp,$f_SPOT_BALANCE" >>"asset-histories/BALANCESPOT${CURRENCY}.history.csv" + echo "$f_timestamp,$CURRENCY_BALANCE" >>"asset-histories/BALANCE${CURRENCY}.history.csv" + + ### global vars for overall performance last day, week, month + #get_rate_percentage_min_before_and_now BALANCECOMPLETE ${CURRENCY} 1440 + #f_perf_day=${f_exchange_rate_diff_percentage} + #get_rate_percentage_min_before_and_now BALANCECOMPLETE ${CURRENCY} 10080 + #f_perf_week=${f_exchange_rate_diff_percentage} + #get_rate_percentage_min_before_and_now BALANCECOMPLETE ${CURRENCY} 43200 + #f_perf_month=${f_exchange_rate_diff_percentage} + +} + diff --git a/dabo/functions/get_ema.sh b/dabo/functions/get_ema.sh new file mode 100644 index 0000000..a26272e --- /dev/null +++ b/dabo/functions/get_ema.sh @@ -0,0 +1,27 @@ +function get_ema { + + local f_hist_file="$1" + local f_hist_file_column="$2" + local f_period="$3" + local f_last_ema="$4" + local f_lastvalue="$5" + + f_ema="" + + # calculate EMA if last EMA is given + if [ -n "$f_last_ema" ] + then + f_ema=$(echo "scale=10; ${f_lastvalue}*(2/(${f_period}+1))+${f_last_ema}*(1-(2/(${f_period}+1)))" | bc | sed 's/^\./0./; s/^-\./-0./' ) + # calculate SMA12 only for first time as base for EMA and call it EMA if there are enough value periods + else + local f_period_sum=$(tail -n${f_period} "${f_hist_file}" | cut -d, -f${f_hist_file_column} | egrep "^[0-9]|-[0-9]" | wc -l) + if [ ${f_period_sum} -eq ${f_period} ] + then + f_ema=$(tail -n ${f_period} "${f_hist_file}" | cut -d"," -f${f_hist_file_column} | awk "{ SUM += \$1} END { printf(\"%10.10f\", SUM/${f_period}) }") + else + g_echo_note "${FUNCNAME} $@: Not enough data - waiting for more values. (${f_period} needed; ${f_period_sum} given)" + return 0 + fi + fi +} + diff --git a/dabo/functions/get_macd_indicator.sh b/dabo/functions/get_macd_indicator.sh new file mode 100644 index 0000000..d11188b --- /dev/null +++ b/dabo/functions/get_macd_indicator.sh @@ -0,0 +1,118 @@ +function get_macd_indicator { + + #g_echo_note "RUNNING FUNCTION ${FUNCNAME} $@" + # get histfile + local f_hist_file="$1" + + # define or clean gloval macdvars + f_macd_ema12="" + f_macd_ema26="" + f_macd="" + f_macd_ema9_signal="" + f_macd_signal="" + + if ! [ -s "$f_hist_file" ] + then + g_echo_warn "${FUNCNAME} $@: Histfile $f_hist_file does not exist or is empty" + return 1 + fi + + # read last two lines + local f_lastline=$(tail -n1 "$f_hist_file" | grep ^2) + local f_second_lastline=$(tail -n2 "$f_hist_file" | head -n1 | grep ^2) + + # Try to get current MACD values + local f_macd_lastprice=$(echo "$f_lastline" | cut -d, -f2) + f_macd_ema12=$(echo "$f_lastline" | cut -d, -f4) + f_macd_ema26=$(echo "$f_lastline" | cut -d, -f5) + f_macd=$(echo "$f_lastline" | cut -d, -f6) + f_macd_ema9_signal=$(echo "$f_lastline" | cut -d, -f7) + + # if data is complete + if [ -n "${f_macd_ema12}" ] && [ -n "${f_macd_ema26}" ] && [ -n "${f_macd}" ] && [ -n "${f_macd_ema9_signal}" ] + then + g_echo_note "${FUNCNAME} $@: Looks like current MACD was already calculated" + return 1 + fi + + # Try to get last MACD values + f_macd_last_ema12=$(echo "$f_second_lastline" | cut -d, -f4) + f_macd_last_ema26=$(echo "$f_second_lastline" | cut -d, -f5) + f_macd_last=$(echo "$f_second_lastline" | cut -d, -f6) + f_macd_last_ema9_signal=$(echo "$f_second_lastline" | cut -d, -f7) + f_macd_last_histogram=$(echo "$f_second_lastline" | cut -d, -f8) + + # calc EMA12 + get_ema "${f_hist_file}" 2 12 "${f_macd_last_ema12}" "${f_macd_lastprice}" + if [ -z "${f_ema}" ] + then + g_echo_note "${FUNCNAME} $@: Not enough data for calculating EMA12 - waiting for more values" + echo -n ",,,,,," >>"${f_hist_file}" + return 0 + fi + f_macd_ema12=${f_ema} + echo -n ",${f_macd_ema12}" >>"${f_hist_file}" + + # calc EMA26 + if [ -z "${f_macd_last_ema12}" ] + then + echo -n ",,,,," >>"${f_hist_file}" + return 0 + fi + get_ema "${f_hist_file}" 2 26 "${f_macd_last_ema26}" "${f_macd_lastprice}" + if [ -z "${f_ema}" ] + then + g_echo_note "${FUNCNAME} $@: Not enough data for calculating EMA26 - waiting for more values" + echo -n ",,,,," >>"${f_hist_file}" + return 0 + fi + f_macd_ema26=${f_ema} + echo -n ",${f_macd_ema26}" >>"${f_hist_file}" + + # calc MACD + if [ -z "${f_macd_ema26}" ] + then + echo -n ",,,," >>"${f_hist_file}" + return 0 + fi + #[ -z "${f_macd_ema12}" ] && return 0 + f_macd=$(echo "scale=8; ${f_macd_ema12}-${f_macd_ema26}" | bc | sed 's/^\./0./; s/^-\./-0./') + echo -n ",${f_macd}" >>"${f_hist_file}" + + # calc MACD Signal + if [ -z "${f_macd}" ] + then + echo -n ",,," >>"${f_hist_file}" + return 0 + fi + get_ema "${f_hist_file}" 6 9 "${f_macd_last_ema9_signal}" "${f_macd}" + if [ -z "${f_ema}" ] + then + g_echo_note "${FUNCNAME} $@: Not enough data for calculating EMA9 Signal - waiting for more values" + echo -n ",,," >>"${f_hist_file}" + return 0 + fi + f_macd_ema9_signal=${f_ema} + echo -n ",${f_macd_ema9_signal}" >>"${f_hist_file}" + + # calc MACD Histogram + f_macd_histogram=$(echo "scale=10; ${f_macd}-(${f_macd_ema9_signal})" | bc | sed 's/^\./0./; s/^-\./-0./') + echo -n ",${f_macd_histogram}" >>"${f_hist_file}" + + # check for MACD buy or sell signal + echo ${f_macd_histogram} | grep -q "^-" && echo ${f_macd_last_histogram} | grep -q "^[0-9]" && f_macd_signal="sell" + echo ${f_macd_histogram} | grep -q "^[0-9]" && echo ${f_macd_last_histogram} | grep -q "^-" && f_macd_signal="buy" + + # calculate MACD Histogram relation + if [ $(tail -n36 "${f_hist_file}" | wc -l) -ge 35 ] + then + local f_macd_histogram_max=$(tail -n60 "${f_hist_file}" | cut -d"," -f8 | egrep "^[0-9]|^-[0-9]" | sed 's/^-//' | sort -n | tail -n1) + local f_macd_histogram_relation=$(echo "100+$(g_percentage-diff ${f_macd_histogram_max} ${f_macd_histogram})" | bc | sed 's/^\./0./; s/^-\./-0./' | cut -d\. -f1) + fi + + echo -n ",${f_macd_histogram_relation}|${f_macd_signal}" >>"${f_hist_file}" + + + +} + diff --git a/dabo/functions/get_rate_percentage_min_before_and_now.sh b/dabo/functions/get_rate_percentage_min_before_and_now.sh new file mode 100644 index 0000000..2e63f0b --- /dev/null +++ b/dabo/functions/get_rate_percentage_min_before_and_now.sh @@ -0,0 +1,63 @@ +function get_rate_percentage_min_before_and_now { + g_echo_note "RUNNING FUNCTION ${FUNCNAME} $@" + # get vars + local f_asset=$1 + local f_currency=$2 + local f_min=$3 + # Optional time from min before sould be calculated (to be able to calculate from 5 min before to 10 minutes before) + local f_min_from=$4 + + # clean up global variable (maybe used in runs before) + f_exchange_rate_diff_percentage="" + + local f_asset_hist_file="asset-histories/${f_asset}${f_currency}.history.csv" + if [ -s "f_asset_hist_file" ] + then + g_echo_note "${f_asset}: No or empty histfile(${f_asset_hist_file}) found" + return 1 + fi + + # prepare date + if [ -n "${f_min_from}" ] + then + local f_min_date_from=$(date "+%F %H:%M:" --date="-${f_min_from} minutes") + f_min=$((f_min+f_min_from)) + else + local f_min_date_from=$(date "+%F %H:%M:") + fi + local f_min_date=$(date "+%F %H:%M:" --date="-${f_min} minutes") + + if [ -z "${f_min_date}" ] + then + g_echo_warn "${f_asset}: Error while calculating date ${f_min} before" + return 1 + fi + + # get current exchange rate + local f_current_exchange_rate=$(egrep "^${f_min_date_from}" ${f_asset_hist_file} | cut -d, -f2) + if [ -z "${f_current_exchange_rate}" ] + then + g_echo_note "${f_asset}: From rate $f_min_from ${f_min_date_from} not found in ${f_asset_hist_file}" + return 1 + fi + + # get exchange rate defined minutes before + local f_min_before_exchange_rate=$(egrep "^${f_min_date}" ${f_asset_hist_file} | cut -d, -f2) + if [ -z "${f_min_before_exchange_rate}" ] + then + g_echo_note "${f_asset}: Rate ${f_min} minutes ago not found in ${f_asset_hist_file}" + return 2 + fi + + # calculate difference in percentage between current rate and rate defined minutes before + f_exchange_rate_diff_percentage=$(g_percentage-diff ${f_min_before_exchange_rate} ${f_current_exchange_rate}) + if [ -z "$f_exchange_rate_diff_percentage" ] + then + g_echo_warn "${f_asset}: Calculating difference between rate ${f_min} minutes ago (${min_before_exchange_rate}) and current (${m_current_exchange_rate}) in percentage failed" + return 3 + fi + + # give back percentage + g_echo_note "${f_asset}: difference from ${f_current_exchange_rate} from ${f_min_date_from} and ${f_min_date} ${f_min_before_exchange_rate}: ${f_exchange_rate_diff_percentage}%" + return 0 +} diff --git a/dabo/functions/get_rsi_indicator.sh b/dabo/functions/get_rsi_indicator.sh new file mode 100644 index 0000000..89177fe --- /dev/null +++ b/dabo/functions/get_rsi_indicator.sh @@ -0,0 +1,79 @@ +function get_rsi_indicator { + #g_echo_note "RUNNING FUNCTION ${FUNCNAME} $@" + # get histfile + local f_hist_file="$1" + local f_last_minutes="$2" + + # calculate change of lastest f_last_minutes+1 prices for calculating rsi over the last f_last_minutes periods + local f_price + local f_last_price + local f_positive_sum=0 + local f_negative_sum=0 + f_rsi="" + + local f_period=$((f_last_minutes+1)) + local f_period_sum=$(tail -n${f_period} "${f_hist_file}" | cut -d, -f2 | grep "^[0-9]" | wc -l) + if ! [ ${f_period_sum} -ge ${f_period} ] + then + g_echo_note "${FUNCNAME} $@: Not enough data - waiting for more values. (${f_period} needed; ${f_period_sum} given)" + echo -n ",${f_rsi}" >>"${f_hist_file}" + return 0 + fi + +# for f_price in $(tail -n${f_period} "${f_hist_file}" | cut -d"," -f2) +# do +# # not for the first line because a comparative value is missing +# if [ -z "${f_last_price}" ] +# then +# f_last_price=${f_price} +# continue +# fi +# +# # calculate each change +# local f_price_change=$(g_percentage-diff ${f_last_price} ${f_price}) +# f_last_price=${f_price} +# +# # add positive/negative changes +# if echo "${f_price_change}" | grep -q '^-' +# then +# f_negative_sum=$(echo "${f_negative_sum}+${f_price_change}" | bc -l) +# else +# f_positive_sum=$(echo "${f_positive_sum}+${f_price_change}" | bc -l) +# fi +# done + + f_positive_sum=$(tail -n${f_period} "${f_hist_file}" | cut -d"," -f3 | grep "^[0-9]" | awk "{ SUM += \$1} END { printf(\"%10.10f\", SUM/${f_period}) }") + f_negative_sum=$(tail -n${f_period} "${f_hist_file}" | cut -d"," -f3 | grep "^-[0-9]" | awk "{ SUM += \$1} END { printf(\"%10.10f\", SUM/${f_period}) }") + + + # if one of both is "0" then fix results + if [ ${f_negative_sum} == "0.0000000000" ] + then + f_rsi=100 + #g_echo_note "RSI-Indicator RSI: ${f_rsi}%" + echo -n ",${f_rsi}" >>"${f_hist_file}" + return 0 + fi + + if [ ${f_positive_sum} == "0.0000000000" ] + then + f_rsi=0 + #g_echo_note "RSI-Indicator RSI: ${f_rsi}%" + echo -n ",${f_rsi}" >>"${f_hist_file}" + return 0 + fi + + # calculate positive/negative change averages + local f_negative_sum_average=$(echo "${f_negative_sum}/${f_last_minutes}" | bc -l | sed 's/-//') + local f_positive_sum_average=$(echo "${f_positive_sum}/${f_last_minutes}" | bc -l) + + # calculate RS and RSI + local f_rs=$(echo "${f_positive_sum_average}/${f_negative_sum_average}" | bc -l) + f_rsi=$(echo "100-(100/(1+${f_rs}))" | bc -l | xargs printf "%.0f") + + echo -n ",${f_rsi}" >>"${f_hist_file}" + + #g_echo_note "RSI-Indicator RSI: ${f_rsi}%" + +} + diff --git a/dabo/functions/get_vars_from_csv.sh b/dabo/functions/get_vars_from_csv.sh new file mode 100644 index 0000000..48ad240 --- /dev/null +++ b/dabo/functions/get_vars_from_csv.sh @@ -0,0 +1,47 @@ +function get_vars_from_csv { + + local f_ASSET_HIST_FILE="$1" + if ! [ -s "${f_ASSET_HIST_FILE}" ] + then + g_echo_warn "${f_ASSET_HIST_FILE} does not exist or is empty" + return 1 + fi + + f_last_line="$(tail -n1 "${f_ASSET_HIST_FILE}")" + + readarray -d "," -t f_last_line_array < <(echo "0,${f_last_line}") + + get_var_from_line date 1 + get_var_from_line price 2 + get_var_from_line price_change 3 + + get_var_from_line macd_histogram 8 + get_var_from_line macd_signal_relation 9 + f_macd_histogram_relation=$(echo "${f_macd_signal_relation}" | cut -d\| -f1) + f_macd_histogram_signal=$(echo "${f_macd_signal_relation}" | cut -d\| -f2) + + get_var_from_line rsi5 10 + get_var_from_line rsi14 11 + get_var_from_line rsi21 12 + get_var_from_line rsi720 13 + get_var_from_line rsi60 14 + get_var_from_line rsi120 15 + get_var_from_line rsi240 16 + get_var_from_line rsi420 17 + + get_var_from_line price_change_1_day 18 + get_var_from_line price_change_7_day 19 + get_var_from_line price_change_14_day 20 + get_var_from_line price_change_30_day 21 + +} + +function get_var_from_line { + if [ -z "${f_last_line_array[$2]}" ] + then + g_echo_note "Didn't get $1 in position $2" + return 1 + fi + declare -g f_$1="$(echo ${f_last_line_array[$2]})" +} + diff --git a/dabo/functions/market_performance.sh b/dabo/functions/market_performance.sh new file mode 100644 index 0000000..2b2722e --- /dev/null +++ b/dabo/functions/market_performance.sh @@ -0,0 +1,92 @@ +function market_performance { + g_echo_note "RUNNING FUNCTION ${FUNCNAME} $@" + + + ## function for scoring limits set in config up or down by specific (market) facts + # generates variable f_market_performance + # forecast bitcoin (is quartered because uncertain) + f_url="https://30rates.com/btc-to-usd-forecast-today-dollar-to-bitcoin" + [ -e btc-forecast ] && find btc-forecast -mmin +60 -delete + if ! [ -s btc-forecast ] + then + g_runcmd g_retrycmd curl -s "$f_url" >btc-forecast || return 1 + fi + local f_forecast=$(cat btc-forecast | hxnormalize -x | hxselect 'table' | grep "" | grep -A1 "Price" -m1 | tail -n1 | cut -d'>' -f3 | cut -d'<' -f1) + if ! echo "$f_forecast" | egrep -q '^[0-9]*\.[0-9]*$|^[0-9]*$' + then + g_echo_warn "Didn't get correct forecast from $f_url" + return 1 + fi + local f_today=$(cat btc-forecast | hxnormalize -x | hxselect 'table' | egrep '' | head -n1 | cut -d'>' -f3 | cut -d'<' -f1) + if ! echo "$f_today" | egrep -q '^[0-9]*\.[0-9]*$|^[0-9]*$' + then + g_echo_warn "Didn't get correct forecast from $f_url" + return 1 + fi + local f_btc_forecast=$(g_percentage-diff ${f_today} ${f_forecast}) + echo $f_btc_forecast | egrep -q '[0-9]\.[0-9]' || return 1 + g_echo_note "BTC forecast: $f_btc_forecast" + f_btc_forecast=$(echo "scale=2; ${f_btc_forecast}/3" | bc -l | sed -r 's/^(-?)\./\10./') + + + # forecast ethereum (is quartered because uncertain) + local f_url="https://30rates.com/ethereum-price-prediction-tomorrow-week-month-eth-forecast" + [ -e eth-forecast ] && find eth-forecast -mmin +60 -delete + if ! [ -s eth-forecast ] + then + g_runcmd g_retrycmd curl -s "$f_url" >eth-forecast || return 1 + fi + f_forecast=$(cat eth-forecast | hxnormalize -x | hxselect 'table' | grep "" | grep -A1 "Price" -m1 | tail -n1 | cut -d'>' -f3 | cut -d'<' -f1) + if ! echo "$f_forecast" | egrep -q '^[0-9]*\.[0-9]*$|^[0-9]*$' + then + g_echo_warn "Didn't get correct forecast $f_forecast from $f_url" + return 1 + fi + f_today=$(cat eth-forecast | hxnormalize -x | hxselect 'table' | egrep '' | head -n1 | cut -d'>' -f3 | cut -d'<' -f1) + if ! echo "$f_today" | egrep -q '^[0-9]*\.[0-9]*$|^[0-9]*$' + then + g_echo_warn "Didn't get correct forecast from $f_url" + return 1 + fi + local f_eth_forecast=$(g_percentage-diff ${f_today} ${f_forecast}) + echo $f_eth_forecast | egrep -q '[0-9]\.[0-9]' || return 1 + g_echo_note "ETH forecast: $f_eth_forecast" + f_eth_forecast=$(echo "scale=2; ${f_eth_forecast}/3" | bc -l | sed -r 's/^(-?)\./\10./') + + + # price performance msci world (last week and halved because of the long time) + #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_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 + #get_rate_percentage_min_before_and_now MSCI-WORLD- INDEX $f_back || return 1 + local f_msci_world_performance=$(echo "scale=2; ${f_exchange_rate_diff_percentage}/2" | bc -l | sed -r 's/^(-?)\./\10./') + + # price performance bitcoin (last 30 minutes) + local f_from=$(tail -n 30 asset-histories/BTC${CURRENCY}.history.csv | grep -v ^[A-Z] | head -n1 | cut -d, -f2) + local f_to=$(tail -n 1 asset-histories/BTC${CURRENCY}.history.csv | cut -d, -f2) + local f_exchange_rate_diff_percentage=$(g_percentage-diff ${f_from} ${f_to}) + #get_rate_percentage_min_before_and_now BTC ${CURRENCY} 360 || return 1 + local f_btc_performance=${f_exchange_rate_diff_percentage} + + # price performance ethereum (last 30 minutes) + #get_rate_percentage_min_before_and_now ETH ${CURRENCY} 360 || return 1 + local f_from=$(tail -n 30 asset-histories/ETH${CURRENCY}.history.csv | grep -v ^[A-Z] | head -n1 | cut -d, -f2) + local f_to=$(tail -n 1 asset-histories/ETH${CURRENCY}.history.csv | cut -d, -f2) + local f_exchange_rate_diff_percentage=$(g_percentage-diff ${f_from} ${f_to}) + local f_eth_performance=${f_exchange_rate_diff_percentage} + + # hourly price performance over TOP 250 marketcap by coingecko + local f_top250_marketcap_performance=$(jq -r ".[].price_change_percentage_1h_in_currency" COINGECKO_GET_ASSETS_CMD_OUT | awk '{ SUM += $1} END { printf("%.2f", SUM/250) }') + + ## calculate market performance + f_market_performance=$(echo "scale=2; (${f_btc_forecast} + ${f_eth_forecast} + ${f_msci_world_performance} + ${f_btc_performance} + ${f_eth_performance} + ${f_top250_marketcap_performance})/6" | bc -l | sed -r 's/^(-?)\./\10./') + local f_date=$(g_date_print) + echo "${f_date} Market Performance: ${f_market_performance}%; BTC forecast: ${f_btc_forecast}%; ETH forecast: ${f_eth_forecast}%; MSCI WORLD: ${f_msci_world_performance}%; BTC: ${f_btc_performance}%; ETH: ${f_eth_performance}%; TOP250 Marketcap: ${f_top250_marketcap_performance}%" >>MARKET_PERFORMANCE + echo -n "${f_market_performance}" >MARKET_PERFORMANCE_LATEST + +} + diff --git a/dabo/functions/webpage.sh b/dabo/functions/webpage.sh new file mode 100644 index 0000000..8cbc66b --- /dev/null +++ b/dabo/functions/webpage.sh @@ -0,0 +1,232 @@ +function webpage { + + + g_echo_note "RUNNING FUNCTION ${FUNCNAME} $@" + # create status webpage + echo ' + + + + +Statuspage Crypto Bot +' >../index.html.tmp + echo "

Statuspage Crypto Bot (ReadOnly)

+

Last update $(date "+%F %T")

" >>../index.html.tmp + + + local f_SPOT_BALANCE=$(tail -n1 "asset-histories/BALANCESPOT${CURRENCY}.history.csv" | cut -d, -f2) + local f_COMPLETE_BALANCE=$(tail -n1 "asset-histories/BALANCECOMPLETE${CURRENCY}.history.csv" | cut -d, -f2) + echo '

Overview

' >>../index.html.tmp + echo " + + + + + + + + + + + + +
Overall Balance:${CURRENCY} ${f_COMPLETE_BALANCE}
SPOT Balance (invested):${CURRENCY} $f_SPOT_BALANCE
Free Balance (not invested):${CURRENCY} ${CURRENCY_BALANCE}
" >>../index.html.tmp + + echo "

Trade Performance (overall closed trades)

" >>../index.html.tmp + + echo " + + + + + + + + + + + + + + + + +
Performance day (last 24 hours): $(find trade-histories -name "*-closed.history.csv.result" -mtime -1 -exec cat {} \; | awk '{ SUM += $1} END { printf("%.2f", SUM) }')%
Performance week (last 7 days): $(find trade-histories -name "*-closed.history.csv.result" -mtime -7 -exec cat {} \; | awk '{ SUM += $1} END { printf("%.2f", SUM) }')%
Performance month (last 30 days): $(find trade-histories -name "*-closed.history.csv.result" -mtime -31 -exec cat {} \; | awk '{ SUM += $1} END { printf("%.2f", SUM) }')%
Performance complete): $(find trade-histories -name "*-closed.history.csv.result" -exec cat {} \; | awk '{ SUM += $1} END { printf("%.2f", SUM) }')%
" >>../index.html.tmp + + + echo '

Open Trades (Invested Assets)

' >>../index.html.tmp + echo "" >>../index.html.tmp + local line + cat EXCHANGE_GET_BALANCES_CMD_OUT | grep -v ${CURRENCY} | sort | while read line + do + local spot_balances=($(echo $line | sed 's/,/ /g')) + echo " + + + + " >>../index.html.tmp + done + echo "
${spot_balances[0]} πŸ”—${spot_balances[1]}${CURRENCY} ${spot_balances[2]} ( $(cat DIFF_BUY_PRICE_${spot_balances[0]}${CURRENCY})%)
" >>../index.html.tmp + + echo "

Market Performance ( $(cat MARKET_PERFORMANCE_LATEST)%)

" >>../index.html.tmp + echo -e "
$(egrep ":00:" MARKET_PERFORMANCE | tail -n10)\n$(tail -n1 MARKET_PERFORMANCE)
" >>../index.html.tmp + echo "Complete list" >>../index.html.tmp + + echo '

Latest trades

' >>../index.html.tmp + echo '

Open

' >>../index.html.tmp + + echo "" >>../index.html.tmp + echo " + + + + + + " >>../index.html.tmp + + local f_trade_file + for f_trade_file in $(ls -t trade-histories/trade-*-open.history.csv 2>/dev/null) + do + local tradeline=$(tail -n1 ${f_trade_file}) + local asset=$(echo ${f_trade_file} | cut -d. -f3 | cut -d- -f1) + echo " + + + + + + + " >>../index.html.tmp + done + echo "
DateAssetAction${CURRENCY} Quantity${CURRENCY} PriceCommissionComment
$(echo ${tradeline} | cut -d, -f1)${asset} πŸ”—$(echo ${tradeline} | cut -d, -f2)$(echo ${tradeline} | cut -d, -f4)$(echo ${tradeline} | cut -d, -f5)$(echo ${tradeline} | cut -d, -f6)$(echo ${tradeline} | cut -d, -f7,8,9,10,11,12,13,14,15)
" >>../index.html.tmp + + echo '

Closed

' >>../index.html.tmp + + echo "" >>../index.html.tmp + echo " + + + + + + " >>../index.html.tmp + + for f_trade_file in $(ls -t trade-histories/trade-*-closed.history.csv 2>/dev/null | head -n 50) + do + local tradeline + cat ${f_trade_file} | while read tradeline + do + local f_action=$(echo ${tradeline} | cut -d, -f2) + local f_price=$(echo ${tradeline} | cut -d, -f5) + + if echo ${f_action} | grep -q buy + then + echo ${f_price} >${g_tmp}/buyprice + fi + + if echo ${f_action} | grep -q sell + then + if [ -s "${f_trade_file}.result" ] + then + local f_profit=$(cat "${f_trade_file}.result") + f_price="${f_price} ( ${f_profit}%)" + else + local f_profit=$(g_percentage-diff $(cat ${g_tmp}/buyprice) ${f_price}) + f_price="${f_price} ( ${f_profit}%)" + echo "${f_profit}" >"${f_trade_file}.result" + fi + fi + local asset=$(echo ${f_trade_file} | cut -d. -f3 | cut -d- -f1) + echo " + + + + + + + " >>../index.html.tmp + done + done + echo "
DateAssetAction${CURRENCY} Quantity${CURRENCY} Price (result)CommissionComment
$(echo ${tradeline} | cut -d, -f1)${asset} πŸ”—${f_action}$(echo ${tradeline} | cut -d, -f4)${f_price}$(echo ${tradeline} | cut -d, -f6)$(echo ${tradeline} | cut -d, -f7,8,9,10,11,12,13,14,15)
" >>../index.html.tmp + + + +# echo "" >>../index.html.tmp +# echo " +# +# +# +# +# +# " >>../index.html.tmp +# grep ^20 trade-histories/*history.csv | sed 's/\// /; s/\./ /; s/:/ /; s/,/ /g' | sort -r -k3 >${g_tmp}/trade-hist-complete +# local tradeline +# cat ${g_tmp}/trade-hist-complete | while read tradeline +# do +# # use array trade +# local trade=($tradeline) +# local f_trace_price=${trade[8]} +# if [ ${trade[4]} = "sell" ] +# then +# local f_trade_date=${trade[3]} +# local f_trade_currency=${trade[1]} +# local f_trade_sell_for=${trade[6]} +# local f_trade_buy_for=$(grep -B1 "^${f_trade_date},sell," "trade-histories/${f_trade_currency}.history.csv" | grep ",buy," | cut -d, -f4 | cut -d" " -f1) +# local f_result=$(echo "scale=2; ${f_trade_sell_for}-$f_trade_buy_for" | bc -l | sed 's/^\./0./; s/^-\./-0./' | xargs printf "%.2f") +# local f_result_percentage=$(g_percentage-diff $f_trade_buy_for ${f_trade_sell_for}) +# f_trace_price="$f_trace_price ($f_result( ${f_result_percentage}%))" +# #echo "${trade[1]} $f_trade_buy_for -- ${f_trade_sell_for} == $f_result(${f_result_percentage}%)" +# fi +# echo " +# +# +# +# +# +# +# " >>../index.html.tmp +# +# done +# echo "
DateAssetAction${CURRENCY} Quantity${CURRENCY} Price (profit/loss)CommissionComment
${trade[3]}${trade[1]} πŸ”—${trade[4]}${trade[6]}${f_trace_price}${trade[9]} ${trade[10]}${trade[@]:11:30}
" >>../index.html.tmp + + + + + + echo "

Current config

" >>../index.html.tmp + echo "
$(cat ../../bot.conf | perl -pe 's/\/>/g;')
" >>../index.html.tmp + + + + echo '

Available Assets and histories

' >>../index.html.tmp + echo "" >>../index.html.tmp + echo "" >>../index.html.tmp + local asset + cat ASSETS | egrep -v "${BLACKLIST}" | sort | while read asset + do + [ -s asset-histories/${asset}.history.csv ] || continue + echo "" >>../index.html.tmp + kcurrency=$(echo ${asset} | sed "s/${CURRENCY}//") + #get_rate_percentage_min_before_and_now ${kcurrency} ${CURRENCY} 1440 + local asset=($(cat asset-histories/${asset}.history.csv | egrep -v "0.00000000$" | tail -n2 | head -n1 | sed 's/,/ /g')) + echo " + + + + " >>../index.html.tmp + done + echo "
AssetDatePrice ${CURRENCY}24h change (USD)
${asset} πŸ”—${asset[0]} ${asset[1]}${CURRENCY} ${asset[2]} $(grep "^$kcurrency," ASSET_PRICE_CHANGE_PERCENTAGE_24H | cut -d, -f2)%
" >>../index.html.tmp + + echo '

Complete trading histories

' >>../index.html.tmp + echo "" >>../index.html.tmp + find trade-histories -type f -name *.history.csv | cut -d/ -f2 | cut -d. -f1 | sort | while read asset + do + [ -s trade-histories/${asset}.history.csv ] || continue + echo "" >>../index.html.tmp + done + echo "
${asset} πŸ”—
" >>../index.html.tmp + + # color magic + cat ../index.html.tmp | perl -pe 's/ (\-[0-9]+\.[0-9]+\%)/$1<\/font>/g; s/ ([0-9]+\.[0-9]+\%)/$1<\/font>/g;' >../index.html + #mv ../index.html.tmp ../index.html +} diff --git a/data/browser.css b/data/browser.css new file mode 100644 index 0000000..9e2b86b --- /dev/null +++ b/data/browser.css @@ -0,0 +1,217 @@ + +.container { position:relative; margin:0 auto; width:959px; } +.column { width:100%; } + + +/* Mobile (Portrait) +================================================== */ + +@media only screen and (max-width: 767px) { +.container { width:100%; } +} + + +/* CSS Reset +================================================== */ + +html,body,div,span,h1,h6,p,a,ul,li,audio { +border:0; +font:inherit; +font-size:25px; +margin:0; +padding:0; +vertical-align:baseline; +} + +body { line-height:1; } +/* ul { list-style:none; } + + +/* Basic Styles +================================================== */ + +html,body { +background-color:#000000; +color:#808080; +font:24px Helvetica, Arial, sans-serif; +font-weight:300; +padding:5px 0; +} + + +/* Typography +================================================== */ + +h1 { font-size:42px; line-height:44px; margin:20px 0 0; text-align: center; font-weight: bold; text-decoration:underline; } +h2 { font-size:32px; line-height:44px; margin:20px 0 0; text-align: left; font-weight: bold; } + +/* Links +================================================== */ + +a,a:visited { color:#808080; outline:0; text-decoration:none; } +a:hover,a:focus,li:hover,li:focus,label:hover { color:#bbb; } +p a,p a:visited { line-height:inherit; } + + +/* Misc. +================================================== */ + +.add-bottom { margin-bottom:20px !important; } +.left { float:left; } +.right { float:right; } +.center { text-align:center; } + + + +/* Audio Player Styles +================================================== */ + +/* Default / Desktop / Firefox */ +audio { margin:0 15px 0 14px; width:670px; } +#mainwrap { box-shadow:0 0 6px 1px rgba(0,0,0,.25); } +#audiowrap { background-color:#111111; margin:0 auto; } +#plwrap { margin:0 auto; } +#tracks { position:relative; text-align:center; } +#nowPlay { display:inline; } +#npTitle { margin:0; padding:21px; text-align:right; } +#npAction { padding:21px; position:absolute; } +#plList { margin:0; } +#plList li { background-color:#111111; cursor:pointer; display:block; margin:0; padding:10px 0; } +.plItem { position:relative; } +.plTitle { left:38px; overflow:hidden; position:absolute; right:1px; text-overflow:ellipsis; top:0; white-space:nowrap; } +.plNum { padding-left:2px; width:25px; } +.plLength { padding-left:21px; position:absolute; right:21px; top:0; } +.plSel,.plSel:hover { background-color:#222222!important; cursor:default!important; } +a[id^="btn"] { background-color:#111111; cursor:pointer; display:inline-block; font-size:50px; margin:0; padding:21px 27px; text-decoration:none; } +a[id^="btn"]:last-child { margin-left:-4px; } +a[id^="btn"]::-moz-focus-inner { border:0; padding:0; } + +/* Chrome / Android / Tablet +html[data-useragent*="Chrome"][data-useragent*="Android"] body { color:#373837; } +html[data-useragent*="Chrome"][data-useragent*="Android"] audio { margin-left:4px; width:689px; } +html[data-useragent*="Chrome"][data-useragent*="Android"] #audiowrap { background-color:#fafafa; } +html[data-useragent*="Chrome"][data-useragent*="Android"] a[id^="btn"] { background-color:#fafafa; color:#373837; } +html[data-useragent*="Chrome"][data-useragent*="Android"] a[id^="btn"]:hover { background-color:#eee; } +html[data-useragent*="Chrome"][data-useragent*="Android"] #plList li { background-color:#fafafa; } +html[data-useragent*="Chrome"][data-useragent*="Android"] #plList li:hover { background-color:#eee; } +html[data-useragent*="Chrome"][data-useragent*="Android"] .plSel, +html[data-useragent*="Chrome"][data-useragent*="Android"] .plSel:hover { background-color:#eee!important; } +*/ + +/* Audio Player Media Queries +================================================== */ + +/* Tablet Portrait +@media only screen and (min-width: 768px) and (max-width: 959px) { +audio { width:526px; } +html[data-useragent*="MSIE 9.0"] audio { width:536px; } +html[data-useragent*="MSIE 10.0"] audio { width:543px; } +html[data-useragent*="rv:11.0"] audio { width:551px; } +html[data-useragent*="OS 7"] audio { width:546px; } +html[data-useragent*="OS 8"] audio { width:550px; } +html[data-useragent*="OS 9"] audio { width:550px; } +html[data-useragent*="Chrome"] audio { width:533px; } +html[data-useragent*="Chrome"][data-useragent*="Android"] audio { margin-left:4px; width:545px; } +} + +/* Mobile Landscape +@media only screen and (min-width: 480px) and (max-width: 767px) { +audio { width:390px; } +html[data-useragent*="MSIE 9.0"] audio { width:400px; } +html[data-useragent*="MSIE 10.0"] audio { width:407px; } +html[data-useragent*="rv:11.0"] audio { width:415px; } +html[data-useragent*="OS 7"] audio { width:410px; } +html[data-useragent*="OS 8"] audio { width:414px; } +html[data-useragent*="OS 9"] audio { width:414px; } +html[data-useragent*="Chrome"] audio { width:397px; } +html[data-useragent*="Chrome"][data-useragent*="Mobile"] audio { margin-left:4px; width:410px; } +#npTitle { width:245px; } +} + +/* Mobile Portrait +@media only screen and (max-width: 479px) { +audio { width:270px; } +html[data-useragent*="MSIE 9.0"] audio { width:280px; } +html[data-useragent*="MSIE 10.0"] audio { width:287px; } +html[data-useragent*="rv:11.0"] audio { width:295px; } +html[data-useragent*="OS 7"] audio { width:290px; } +html[data-useragent*="OS 8"] audio { width:294px; } +html[data-useragent*="OS 9"] audio { width:294px; } +html[data-useragent*="Chrome"] audio { width:277px; } +html[data-useragent*="Chrome"][data-useragent*="Mobile"] audio { margin-left:4px; width:290px; } +#npTitle { width:167px; } +} +*/ + +audio { width:92%; } + +/* z.B. für VPN Login */ +input { + width: 100%; + font-size:25px; + padding: 12px 20px; + margin: 8px 0; + box-sizing: border-box; + border: 4px solid #808080; + background-color: #222222; + color: #808080; +} + +textarea { + width: 100%; + height: 500px; + font-size:12px; + padding: 12px 20px; + margin: 8px 0; + box-sizing: border-box; + border: 4px solid #808080; + background-color: #222222; + color: #808080; +} + +pre,#footer { + font:16px sans-serif; +} + +.statusok { + font:18px sans-serif; + background-color: #00BF40; + color: black; +} + +.statusnok { + font:18px sans-serif; + background-color: #E10020; + color: black; +} + +/*Ausblenden des input-Feldes */ +.toggleBox input[type="checkbox"] { + position: absolute; + left: -99999px; +} +/* Der Aufklappmechanismus */ +.toggleBox input:checked ~ div { + opacity: 0; + height: 0; + display: none; +} + +.toggleBox input:not(:checked) ~ div { + opacity: 1; +} + +/* Steuerung der Sichtbarkeit der labels */ +.toggleBox input:not(:checked) ~ .open, +.toggleBox input:checked ~ .close { + display: none; +} + +tr:hover {background-color: coral;} + +table, th, td { + border: 1px solid; + border-collapse: collapse; + padding: 5px; +} + diff --git a/docker-compose.yml b/docker-compose.yml index d6cb8e7..f298fc8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,7 +18,7 @@ services: image: nginx:latest restart: unless-stopped volumes: - - ./htdocs:/usr/share/nginx/html:ro + - ./data:/usr/share/nginx/html:ro - /etc/localtime:/etc/localtime:ro networks: - traefik