initial commit

This commit is contained in:
Erik Bročko 2021-04-07 00:04:06 +02:00
commit f8efa34a68
Signed by: ericek111
GPG Key ID: F850D0320BA802C9
7 changed files with 469 additions and 0 deletions

33
echo.awk Normal file
View File

@ -0,0 +1,33 @@
BEGIN {
RS="\n"
FS=";"
split(ARG_TICKER, _tmp_tickers, ";")
for (i in _tmp_tickers) {
tickers[_tmp_tickers[i]] = ""
hasTickers = 1
}
gsub(/[-: ]/, "", ARG_DATE_AFTER);
gsub(/[-: ]/, "", ARG_DATE_BEFORE);
}
(hasTickers == 1) && ($2 in tickers == 0) { next }
{
fieldDate=$1
gsub(/[-: ]/, "", fieldDate);
if (ARG_DATE_AFTER != "" && ARG_DATE_AFTER > fieldDate) { next }
if (ARG_DATE_BEFORE != "" && ARG_DATE_BEFORE < fieldDate) { next }
}
{
print $0
}
END {
}

58
hist-ord.awk Normal file
View File

@ -0,0 +1,58 @@
BEGIN {
RS="\n"
FS=";"
split(ARG_TICKER, _tmp_tickers, ";")
for (i in _tmp_tickers) {
tickers[_tmp_tickers[i]] = ""
hasTickers = 1
}
gsub(/[-: ]/, "", ARG_DATE_AFTER);
gsub(/[-: ]/, "", ARG_DATE_BEFORE);
}
(hasTickers == 1) && ($2 in tickers == 0) { next }
{
fieldDate=$1
gsub(/[-: ]/, "", fieldDate);
if (ARG_DATE_AFTER != "" && ARG_DATE_AFTER > fieldDate) { next }
if (ARG_DATE_BEFORE != "" && ARG_DATE_BEFORE < fieldDate) { next }
}
{
totalTrans[$2]++
}
END {
mostTrans = 0
for (tic in totalTrans) {
if (mostTrans < totalTrans[tic]) {
mostTrans = totalTrans[tic]
}
}
if (ARG_WIDTH < 1) {
ARG_WIDTH = 1
}
for (tic in totalTrans) {
if (ARG_WIDTH == 1) {
cols = totalTrans[tic]
} else {
cols = (totalTrans[tic] / mostTrans) * ARG_WIDTH
}
row = ""
for (i = 1; i <= cols; i++) {
row = row"#"
}
printf "%-9s : %s\n", tic, row
}
}

43
last-price.awk Normal file
View File

@ -0,0 +1,43 @@
BEGIN {
RS="\n"
FS=";"
split(ARG_TICKER, _tmp_tickers, ";")
for (i in _tmp_tickers) {
tickers[_tmp_tickers[i]] = ""
hasTickers = 1
}
gsub(/[-: ]/, "", ARG_DATE_AFTER);
gsub(/[-: ]/, "", ARG_DATE_BEFORE);
}
(hasTickers == 1) && ($2 in tickers == 0) { next }
{
fieldDate=$1
gsub(/[-: ]/, "", fieldDate);
if (ARG_DATE_AFTER != "" && ARG_DATE_AFTER > fieldDate) { next }
if (ARG_DATE_BEFORE != "" && ARG_DATE_BEFORE < fieldDate) { next }
}
{
lastPrice[$2] = $4
}
END {
widestNum = 0
for (tic in lastPrice) {
str = sprintf("%.2f", lastPrice[tic])
strLen = length(str)
if (strLen > widestNum) {
widestNum = strLen
}
}
for (tic in lastPrice) {
printf "%-9s : %"widestNum".2f\n", tic, lastPrice[tic]
}
}

35
list-tick.awk Normal file
View File

@ -0,0 +1,35 @@
BEGIN {
RS="\n"
FS=";"
split(ARG_TICKER, _tmp_tickers, ";")
for (i in _tmp_tickers) {
tickers[_tmp_tickers[i]] = ""
hasTickers = 1
}
gsub(/[-: ]/, "", ARG_DATE_AFTER);
gsub(/[-: ]/, "", ARG_DATE_BEFORE);
}
(hasTickers == 1) && ($2 in tickers == 0) { next }
{
fieldDate=$1
gsub(/[-: ]/, "", fieldDate);
if (ARG_DATE_AFTER != "" && ARG_DATE_AFTER > fieldDate) { next }
if (ARG_DATE_BEFORE != "" && ARG_DATE_BEFORE < fieldDate) { next }
}
{
if ($2 in unique_tickers == 0) {
unique_tickers[$2] = ""
}
}
END {
for (i in unique_tickers) {
print i
}
}

70
pos.awk Normal file
View File

@ -0,0 +1,70 @@
function sumItUp(units, cpu) {
# avoid floating point arithmetic AT ALL COST!
partsLen = split(cpu, parts, ".")
if (partsLen == 1) {
cost = parts[1] * 100
return cost * units
}
dec = parts[2]
dec = substr(dec, 0, 2)
for (i = length(dec); i < 2; i++) {
dec = dec"0"
}
cost = parts[1] * 100 + dec
return cost * units
}
BEGIN {
RS="\n"
FS=";"
split(ARG_TICKER, _tmp_tickers, ";")
for (i in _tmp_tickers) {
tickers[_tmp_tickers[i]] = ""
hasTickers = 1
}
gsub(/[-: ]/, "", ARG_DATE_AFTER);
gsub(/[-: ]/, "", ARG_DATE_BEFORE);
}
(hasTickers == 1) && ($2 in tickers == 0) { next }
{
fieldDate=$1
gsub(/[-: ]/, "", fieldDate);
if (ARG_DATE_AFTER != "" && ARG_DATE_AFTER > fieldDate) { next }
if (ARG_DATE_BEFORE != "" && ARG_DATE_BEFORE < fieldDate) { next }
}
{
if ($3 == "buy") {
profit[$2] += $6
} else if ($3 == "sell") {
profit[$2] -= $6
}
lastPrice[$2] = $4
}
END {
widestNum = 0
for (tic in profit) {
profit[tic] = sumItUp(profit[tic], lastPrice[tic])
profit[tic] /= 100
str = sprintf("%.2f", profit[tic])
strLen = length(str)
if (strLen > widestNum) {
widestNum = strLen
}
}
for (tic in profit) {
printf "%-9s : %"widestNum".2f\n", tic, profit[tic]
}
}

56
profit.awk Normal file
View File

@ -0,0 +1,56 @@
function sumItUp(units, cpu) {
# avoid floating point arithmetic AT ALL COST!
partsLen = split(cpu, parts, ".")
if (partsLen == 1) {
cost = parts[1] * 100
return cost * units
}
dec = parts[2]
dec = substr(dec, 0, 2)
for (i = length(dec); i < 2; i++) {
dec = dec"0"
}
cost = parts[1] * 100 + dec
return cost * units
}
BEGIN {
RS="\n"
FS=";"
split(ARG_TICKER, _tmp_tickers, ";")
for (i in _tmp_tickers) {
tickers[_tmp_tickers[i]] = ""
hasTickers = 1
}
gsub(/[-: ]/, "", ARG_DATE_AFTER);
gsub(/[-: ]/, "", ARG_DATE_BEFORE);
profit = 0
}
(hasTickers == 1) && ($2 in tickers == 0) { next }
{
fieldDate=$1
gsub(/[-: ]/, "", fieldDate);
if (ARG_DATE_AFTER != "" && ARG_DATE_AFTER > fieldDate) { next }
if (ARG_DATE_BEFORE != "" && ARG_DATE_BEFORE < fieldDate) { next }
}
{
if ($3 == "buy") {
profit -= sumItUp($6, $4)
} else if ($3 == "sell") {
profit += sumItUp($6, $4)
}
}
END {
# taking the lazy way here, not cool!
profit /= 100
printf "%.2f\n", profit
}

174
tradelog.sh Executable file
View File

@ -0,0 +1,174 @@
#!/bin/bash
export LC_ALL=C
export POSIXLY_CORRECT=yes
printUsage() {
cat <<-EOF
Usage: $(basename "$0") [-h|--help] [FILTER] [COMMAND] [LOG [LOG2 [...]]
COMMAND:
list-tick list all tickers mentioned in the logs
profit final product of all entries (negative for a net loss)
pos currently held positions ordered asc. by value
last-price last
hist-ord histogram of transactions grouped by tickers
graph-pos graph positions for each ticker
[ommited] entries are echoed according to the provided filters (if any)
FILTER:
-a DATETIME only records AFTER (exclusive) this date are considered
-b DATETIME only records BEFORE (exclusive) this date are considered
format: YYYY-MM-DD HH:MM:SS or in POSIX: %Y-%m-%d %H:%M:%S
-t TICKER count records for this TICKER, can be repeated
-w WIDTH sets the width (in columns) of the longest line for commands
hist-ord - if ommited, one block = one transaction
graph-pos - if ommited, one block = 1000 units of currency
Note: When printing blocks of the graph, division products are floored
(rounded towards zero). So for \`graph-pos -w 6\` with 1234 (equal
to 6 blocks) as the maximum value, 1233.99 will be shown as #####.
Format:
DATUM A CAS;TICKER;TYP TRANSAKCE;JEDNOTKOVA CENA;MENA;OBJEM;ID
Exit codes:
0 Success.
1 Invalid date provided for the -a or -b filter.
2 Duplicated parameter.
EOF
exit 1
}
ARG_DATE_AFTER="0000-01-01 00:00:00"
ARG_DATE_BEFORE="9999-12-31 23:59:59"
ARG_TICKER=""
ARG_WIDTH="0"
ARG_COMMAND="echo"
# needs spaces around all quotes
ACCEPTED_COMMANDS=" list-tick profit pos last-price hist-ord graph-pos echo "
# validateDateFormat (date)
# Returns 0 if the provided string follows YYYY-MM-DD HH:MM:SS.
# Note: This is a veeeeeeeeeeery basic check, allows e. g. "2020-12-24 15"
function validateDateFormat {
# accepts 60 for leap seconds
awkOut="$(echo "$1" | awk -v FS='[-: ]' -v RS='\0' ' {
print (match($0, /^[[:digit:]]{4,}-[[:digit:]]{2,}-[[:digit:]]{2,} [[:digit:]]{2,}:[[:digit:]]{2,}:[[:digit:]]{2,}\n?$/)) ? "good" : "bad"
}')"
[ "$awkOut" = "good" ];
}
# processFile (file)
function processFile {
catcmd="cat"
if [ "${1: -3}" == ".gz" ]; then
catcmd="zcat"
fi
aftercmd="cat"
# it could be done in awk, but so far we managed without GNU extensions,
# hence we'll bite our tongue and just do this
case "$ARG_COMMAND" in
list-tick )
aftercmd="sort -dk1"
;;
pos )
aftercmd="sort -nk3 -r"
;;
last-price )
aftercmd="sort -dk1"
;;
hist-ord )
aftercmd="sort -dk1"
;;
esac
"$catcmd" "$1" | awk \
-f "$ARG_COMMAND.awk" \
-v ARG_TICKER="$ARG_TICKER" \
-v ARG_DATE_AFTER="$ARG_DATE_AFTER" \
-v ARG_DATE_BEFORE="$ARG_DATE_BEFORE" \
-v ARG_WIDTH="$ARG_WIDTH" \
- | $aftercmd
# Note: Oh wouldn't it be great to use the @include keyword
# from GNU awk and avoid all that duplicated code. Oh well.
}
# Transform long options into short ones
for arg in "$@"; do
shift
case "$arg" in
"--help") set -- "$@" "-h" ;;
*) set -- "$@" "$arg"
esac
done
_hasSpecifiedWidth=0
if [ ! $1 ]; then
cat -
exit 0
fi
while getopts "ha:b:t:w:" o; do
case "$o" in
a)
ARG_DATE_AFTER="$OPTARG"
if ! validateDateFormat "$OPTARG"; then
echo "Invalid after date." 1>&2
exit 1
fi
;;
b)
ARG_DATE_BEFORE="$OPTARG"
if ! validateDateFormat "$OPTARG"; then
echo "Invalid before date." 1>&2
exit 1
fi
p="$OPTARG"
;;
t)
ARG_TICKER="$ARG_TICKER;$OPTARG"
# if [ ! "$OPTARG" ]; then echo "Missing argument for -t" ; exit 2 ; fi
;;
w)
ARG_WIDTH="$OPTARG"
if [ $_hasSpecifiedWidth = "1" ]; then
echo "Width specified twice!" 1>&2
exit 2
fi
_hasSpecifiedWidth=1
;;
h)
printUsage
;;
*)
;;
esac
done
shift "$((OPTIND-1))"
# otherwise fails with "binary operator expected" ?!?!?!
_tmpArgs="$@"
if [ -z "$_tmpArgs" ]; then
processFile -
fi
for arg in "$@"; do
if [ -z "${ACCEPTED_COMMANDS##* $arg *}" ]; then
ARG_COMMAND="$arg"
shift
fi
done
for arg in "$@"; do
processFile "$arg"
done