Gnuplot based live crypto charting script

in LeoFinance5 years ago

skskskass.png

The unix philosophy is to have each tool do its job well. I saw an opportunity to combine three simple tools (zsh for scripting, gnuplot for charting, and jq for json querying) to make a live crypto charting program that has the advantage of being very resource unintensive (both ram and cpu).

As can be seen in the screenshot, this script will launch a live chart of whatever crypto pair is wanted, with a configurable polling frequency. Developed for and on Linux, it will work on Windows and Mac OSX with the appropriate compatibility layer. Dependencies are Gnuplot with x11 output, curl, jq, and zsh.

Here is the script:

#!/bin/zsh

while [[ $1 == -* ]]; do
        arg=${1#*=}
        case $1 in
                --symbol=*) symb=$arg ;; # ex. BTC-USDT
                --interval=*) kucoint=$arg ;; # ex. 1day
                --help|\
                -h) sed -n '/case .1/,/esac/p' "$0" | grep -- '^\s\+\-.*)'; exit 0 ;;
                *) echo wrong option; exit 1 ;;
        esac
        shift
done

[[ $symb ]] || symb=BTC-USDT
[[ $kucoint ]] || kucoint=30min
per=1800
multfactor=2
[[ $kucoint == 4hour ]] && { per=14400; multfactor=6; }
[[ $kucoint == 1day ]] && { per=86400; multfactor=30; }
freq=8
time=`date +%s`
timecopy=$time
(( time -= (60 * 60 * 48 * multfactor) ))
tznum=`date +%z`
tz=${tznum:0:1}$(( (${tznum:1:2} * 3600) + (${tznum:3:2} * 60) ))

url="https://api.kucoin.com/api/v1/market/candles?symbol${z/'
        '/}=$symb&type=$kucoint&startAt=$time&endAt=$timecopy"
json=`curl -s $url`
#echo $json > ~/candleszshtest.txt
#json=$(< ~/candleszshtest.txt)

function calcmvs
{
        local line=$1
        local items

        mult9=$(( 2.0 / (9 + 1) ))
        if ((line >= 9)); then
                items=($=maeligible[line])
                if (( line > 9 )); then
                        itemsprev1=($=maeligible[line-1])
                        ema9=$(( ((items[2] - itemsprev1[3]) * mult9) + itemsprev1[3] ))
                else
                        added=0
                        for asdf in {$(( line-(9-1) ))..$line}; do
                                items=($=maeligible[asdf])
                                (( added += items[2] ))
                        done
                        ema9=$(( added / 9 ))
                fi
                maeligible[line]=$items' '$ema9
                lines[$items[1]]+=' '$ema9
        fi

        mult20=$(( 2.0 / (20 + 1) ))
        if ((line >= 20)); then
                items=($=maeligible[line])
                if (( line > 20 )); then
                        itemsprev1=($=maeligible[line-1])
                        ema20=$(( ((items[2] - itemsprev1[4]) * mult20) + itemsprev1[4] ))
                else
                        added=0
                        for asdf in {$(( line-(20-1) ))..$line}; do
                                items=($=maeligible[asdf])
                                (( added += items[2] ))
                        done
                        ema20=$(( added / 20 ))
                fi
                maeligible[line]=$items' '$ema20
                lines[$items[1]]+=' '$ema20
        fi

        if ((line >= 50)); then
                items=($=maeligible[line])
                if (( line > 50 )); then
                        itemsprev50=($=maeligible[line-50])
                        itemsprev1=($=maeligible[line-1])
                        sma50=$(( ( ( itemsprev1[5] * 50 ) -
                                itemsprev50[2] + items[2] ) / 50 ))
                else
                        added=0
                        for asdf in {$(( line-(50-1) ))..$line}; do
                                items=($=maeligible[asdf])
                                (( added += items[2] ))
                        done
                        sma50=$(( added / 50 ))
                fi
                maeligible[line]=$items' '$sma50
                lines[$items[1]]+=' '$sma50
        fi
}

function addeligible
{
        asdf=$1
        if (( per == 1800 )); then
                # prefer 1hour MAs here
                (( ${${=lines[asdf]}[1]} % 3600 == 1800 )) || return
        fi
        maeligible+=($asdf' '${${=lines[asdf]}[5]})
}

# gnuplot expects time, open, low, high, close
raw=`echo $json | jq -r '.data | .[] | [.[0,1,4,3,2]] | join(" ")' | tac`
lines=(${(@f)raw})
for asdf in {1..$#lines}; do addeligible $asdf; done
#date +%s.%N #benchmark
for asdf in {1..$#maeligible}; do calcmvs $asdf; done
#date +%s.%N #benchmark
count=1
total=$#lines
(( total % 2 == 0 )) || { echo expected even number lines; exit 1; }
(( total = total / 2 ))
(( count += total ))
items=($=lines[count-1+total])
watch=($items[1] x $items[5])
cr_el=`tput cr; tput el`

xrdb -merge <<< "gnuplot*exportselection: off"
{
        cat <<- eof
                set terminal x11 noraise
                set grid
                set xdata time
                set timefmt "%s"
                set format x "%H:%M"
                set xrange [*:*]
                set yrange [*:*]
                set offsets 1,1,1,1
                set palette defined (-1 "#b22222", 1 "#228b22")
                unset colorbox
                set style fill solid noborder
        eof
        while true; do
                watch[1]=${watch[1]%.*}
                (( watch[3] < items[3] )) && items[3]=$watch[3]
                (( watch[3] > items[4] )) && items[4]=$watch[3]
                items[5]=$watch[3]
                items[6]=; items[7]=; items[8]=;
                curmod=$(( watch[1] % per ))
                if (( $#lastmod > 0 && curmod <= lastmod )); then
                        (( count++ ))
                        items=($((items[1]+per)) $watch[3] $watch[3] $watch[3] $watch[3])
                        lines[count-1+total]=$items
                        addeligible $((count-1+total))
                fi
                lastmod=$curmod
                lines[count-1+total]=$items
                [[ ${${=maeligible[$#maeligible]}[1]} == $#lines ]] && {
                        maeligible[$#maeligible]=${${=maeligible[$#maeligible]}[1]}${z/'
                                '/}' '$items[5]
                        calcmvs $#maeligible
                }
                cat <<- eof
                        set title "${symb/-/\/} (kuco) interval $kucoint " . \
                                "freq ${freq}s : $watch[3]"
                        \$dat << gnupeof
                        ${(F)lines[count,count-1+total]}
                        gnupeof
                        plot \$dat using (\$1+$tz):2:3:4:5:(\$5 < \$2 ? -1 : 1) \
                        notitle with candlesticks palette \
                        , \$dat using (\$1+$tz):6 notitle with lines lc rgb "violet" \
                        , \$dat using (\$1+$tz):7 notitle with lines lc rgb "aquamarine" \
                        , \$dat using (\$1+$tz):8 notitle with lines lc rgb "#e9bd3d"
                eof
                echo -n 'Options: (q)uit, (f)req ' >&2
                opt=; read -s -t $freq -k 1 opt
                print -nr- $cr_el >&2
                case $opt in
                        f)
                                echo -n 'Enter a poll frequency in seconds: ' >&2
                                read newfreq
                                [[ $newfreq =~ ^[0-9]+$ ]] && freq=$newfreq
                                ;;
                        q)
                                break
                                ;;
                esac
                oldwatchtime=${watch[1]}
                while true; do
                        watch=(`curl -s "https://api.kucoin.com/api/v1/market/${z/'
                                '/}orderbook/level1?symbol=$symb" | jq -r \
                                '[.data.time/1000, .code, .data.price | tostring]
                                | join(" ")'`)
                        (( $#watch == 3 )) && [[ $watch[2] == 200000 ]] && break
                        sleep 8
                done
                (( watch[1] - oldwatchtime > per )) && \
                        { echo exiting due to gap >&2; break; }
        done
} | gnuplot

I am posting this on Hive before anywhere else.

Sort:  

Congratulations @l0bst3r! You have completed the following achievement on the Hive blockchain and have been rewarded with new badge(s) :

You received more than 10 upvotes. Your next target is to reach 50 upvotes.

You can view your badges on your board And compare to others on the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP

Support the HiveBuzz project. Vote for our proposal!