initial commit
This commit is contained in:
commit
f8efa34a68
33
echo.awk
Normal file
33
echo.awk
Normal 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
58
hist-ord.awk
Normal 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
43
last-price.awk
Normal 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
35
list-tick.awk
Normal 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
70
pos.awk
Normal 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
56
profit.awk
Normal 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
174
tradelog.sh
Executable 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user