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