Compare commits

..

5 Commits

Author SHA1 Message Date
c77ebd10ff loader.sh
loader for 2 scripts:
-stocks.sh
-cryptos.sh
2025-01-13 21:04:40 +01:00
cae388ac91 cryptos.sh
Same as stocks.sh but for cryptocurrency
-list_coint.txt list of existing cryptocurrency
2025-01-13 21:02:33 +01:00
85362cacda purchased.json
Temporary purchased stocks file
2025-01-13 20:39:34 +01:00
8fa59c9846 New stocks.sh
forked from https://github.com/appatalks/ticker.sh
2025-01-13 20:35:04 +01:00
d381eb8043 Rename stocks.sh to stocks_legacy
as stocks.sh no longer work (API finance Yahoo HS)
2025-01-13 20:32:43 +01:00
7 changed files with 6119 additions and 236 deletions

447
cryptos.sh Executable file
View File

@@ -0,0 +1,447 @@
#!/usr/bin/env bash
#set -e
### https://coinmarketcap.com/api/documentation/v1/#operation/getV2CryptocurrencyQuotesLatest
VERSION="v1.0"
### TODO
# Locale compliant
# Localization
redbold="\033[1;31m"
red="\033[0;31m"
greenbold="\033[1;32m"
green="\033[0;32m"
yellowbold="\033[1;33m"
yellow="\033[0;33m"
bgd="\033[1;4;31m"
bold="\033[1m"
#bold_under="\033[1;4m"
italic="\033[3m"
ita_under="\033[3;4m"
underline="\033[4m"
reset="\033[0m"
### Variables
ScriptArgs=( "$@" )
ScriptPath="$(readlink -f "$0")" # /Users/user/Documents/Scripts/stocks/crypto.sh
ScriptWorkDir="$(dirname "$ScriptPath")" # /Users/user/Documents/Scripts/stocks
####### Configurations ######
### Credentials
### Put the CMC_API_KEY= in .env file
dotenv () {
set -a
# shellcheck disable=SC1091
[ -f "$ScriptWorkDir/.env" ] && . "$ScriptWorkDir/.env" || echo -e "${red}\nNo .env file found ! CMC_API_KEY needed !!${reset}"
set +a
}
dotenv
### or in the script
#CMC_API_KEY=
if [ -z $CMC_API_KEY ]; then
echo -e "\n${red}No CMC_API_KEY key found !${reset}"
help
exit
fi
### Config file
cryptos_list=$HOME/.cryptos.yaml
# Free version does not support more than 1 currency.
# So, 2 requests for Euro and Dollar exchange rates
# Default currency (MUST BE THE SAME as unit_cost in ~/.cryptos.yaml)
currency="EUR"
symb_currency="€"
# Secondary currency (optionnal)
currency2="USD"
symb_currency2="$"
color_display=true # (true/false)
####### /Configurations ######
### Requierements
if ! $(type jq >/dev/null 2>&1); then
echo -e "${red}'jq' is not in the PATH. (See: https://stedolan.github.io/jq/)${reset}"
exit 1
fi
if ! $(type yq >/dev/null 2>&1); then
echo -e "${red}'yq' is not in the PATH. (See: https://github.com/kislyuk/yq)${reset}"
exit 1
fi
cat < /dev/null > /dev/tcp/1.1.1.1/53
if [[ $? -ne 0 ]]; then
echo -e "\n${red}No Internet connection !${reset}"
echo -e "Exit !"
exit 1
fi
####### Functions ######
# Help
Help() {
echo -e "${bold}cryptos $VERSION${reset}\n"
echo "Syntax: cryptos.sh [<coin>]"
echo
echo "Examples: cryptos.sh BTC ETH SOLE"
echo " cryptos.sh (need ~/.cryptos.yaml file)"
echo
echo "Options:"
echo "-h Print this Help."
echo
echo "Requires a CoinMarketCap API key"
echo "https://coinmarketcap.com/api/"
echo "Requires jq and yq installed"
echo
echo "\$currency variable (default)(line 39) must be the same as unit_cost in ~/.cryptos.yaml"
}
# Check if number is positive (or 0) or negative
CheckSign() {
local n="$1" # Capture the number passed as a parameter.
if [ $(echo "$n < 0" | bc -l) -eq 1 ] ; then
#printf "%+17.13f : %s\n" "$n" "negative"
p="-"
else
#printf "%+17.13f : %s\n" "$n" "positive or zero"
p="+"
fi
echo "$p"
}
LANG=C
LC_NUMERIC=C
### API
URLPREFIX="https://"
URLBASE="pro-api.coinmarketcap.com/"
URLPOSTFIX="v1/cryptocurrency/"
DATAURL=${URLPREFIX}${URLBASE}${URLPOSTFIX}
### Load (or download) a list of existing cryptos
#list_coins=$(curl --silent -H "X-CMC_PRO_API_KEY: $CMC_API_KEY" -H "Accept: application/json" -d "start=1&limit=5&convert=USD" -G "${DATAURL}listings"/latest)
#echo "$list_coins" | jq -r '.data[].name, .data[].symbol'
#echo "$list_coins" | jq -r '.data[] | {name: .name, symbol: .symbol}'
#list_coins=$(curl --silent -H "X-CMC_PRO_API_KEY: $CMC_API_KEY" -H "Accept: application/json" -d "start=1&limit=5&convert=USD" -G "${DATAURL}listings"/latest | jq -r '.data[] | .symbol')
get_list_coins (){
START_TIME=`echo $(($(date +%s%N)/1000000))`
curl --silent -H "X-CMC_PRO_API_KEY: $CMC_API_KEY" -H "Accept: application/json" -d "start=1&limit=5000&convert=USD" -G "${DATAURL}listings"/latest | jq -r '.data[] | .symbol' > "$ScriptWorkDir/list_coins.txt"
status=$?
END_TIME=`echo $(($(date +%s%N)/1000000))`
ELAPSED_TIME=$(($END_TIME - $START_TIME))
[ $status -eq 0 ] && echo -e "${green}Request to CMC API listings $currency successfull in $ELAPSED_TIME ms !${reset}" || echo -e "${red}Request to CMC API listing $currency failed !${reset}"
}
echo -e "${greenbold}Bash Crypto${reset} $VERSION"
echo -e "Using CoinMarketCap API ($DATAURL)\n"
if [ -f "$ScriptWorkDir/list_coins.txt" ]; then
timestamp_current=$(date +"%s")
timestamp_file=$(date -r list_coins.txt +"%s")
since=$(($((timestamp_current - timestamp_file))/(60*60*24)))
# Re-Download coins list every 15 days
((since > 15)) && { echo -e "${green}Download coins list...${reset}"; get_list_coins; }
list_coins=$(cat "$ScriptWorkDir/list_coins.txt")
[ $? -eq 0 ] && echo -e "${green}Load coins list...${reset}" || echo -e "${red}Fail to load coins list...${reset}"
else
get_list_coins
list_coins=$(cat "$ScriptWorkDir/list_coins.txt")
[ $? -eq 0 ] && echo -e "${green}Load coins list...${reset}" || echo -e "${red}Fail to load coins list...${reset}"
fi
### Read cryptos from ~/.cryptos.yaml config file
cryptos=()
# from arguments
if [ -n "$ScriptArgs" ]; then
REGEXP='^[a-zA-Z0-9,_ ]+$'
q="$@"
if [[ "$q" =~ $REGEXP ]]; then
q=${q//,/\ }
q=${q//_/\ }
cryptos+=($q)
else
Help
exit 1
fi
# Check if crypto passed as argument really exist
for val in ${!cryptos[@]}
do
k="${cryptos[$val]}"
#echo $val # index
#echo $k # value
if [[ ! "$list_coins" == *"$k"* ]]; then
echo -e "${red}$k is not a crypto. Remove it !${reset}"
unset cryptos[$val]
fi
done
# Recreate the array, because the gaps have to disappear
cryptos=("${cryptos[@]}")
echo -e "${green}Found ${#cryptos[@]} coins to display: ${cryptos[@]} ...${reset}"
# from ~/.cryptos.yaml config file
else
if [ -f "$cryptos_list" ]; then
cl=$(cat $cryptos_list)
a=$(yq '.watchlist' <<< $cl)
#a=$(cat $cryptos_list | yq '.watchlist') # Cryptos to display
status=$?
#[ $? -eq 0 ] && echo -e "${green}Load coins list to display...${reset}" || echo -e "${red}Fail to load coins list to display...${reset}"
b=$(yq '.lot' <<< $cl)
#b=$(cat $cryptos_list | yq '.lots') # Cryptos i've purchased
c=$((yq '.lots[] | select(.quantity != 0) | .symbol' | tr '\n' ' ') <<< $cl)
#c=$(cat $cryptos_list | yq '.lots[] | select(.quantity != 0) | .symbol' | tr '\n' ' ')
u=$(echo "$c" | wc -w)
[ $? -eq 0 ] && echo -e "${green}Found $u coins purchased: $c ...${reset}" || echo -e "${red}Fail to load coins list purchased...${reset}"
else
echo -e "${red}$cryptos_list not present !${reset}"
echo ""
fi
while IFS= read -r obj; do
stock=${obj:2}
cryptos+=("$stock")
done < <(echo "$a")
[ $status -eq 0 ] && echo -e "${green}Found ${#cryptos[@]} coins to display: ${cryptos[@]} ...${reset}" || echo -e "${red}No coins to display...${reset}"
if [ -z "$cryptos" ]; then
echo "Usage: ./crypto.sh"
echo " - add coins to ~/.cryptos.yaml file"
exit 1
fi
fi
nb_cryptos=${#cryptos[@]}
symbols=$(echo ${cryptos[@]} | tr [:space:] ',' | awk '{ print substr( $0, 1, length($0)-1 ) }')
# Free version does not support more than 1 currency.
# So, 2 requests for Euro and Dollar exchange rates
if [ -n "$currency2" ]; then
# 2 currency
START_TIME=`echo $(($(date +%s%N)/1000000))`
request=$(curl --silent -H "X-CMC_PRO_API_KEY: $CMC_API_KEY" -H "Accept: application/json" -d "convert=$currency" -G "${DATAURL}quotes"/latest?symbol=$symbols)
status=$?
END_TIME=`echo $(($(date +%s%N)/1000000))`
ELAPSED_TIME=$(($END_TIME - $START_TIME))
[ $status -eq 0 ] && echo -e "${green}Request to CMC API with currency $currency successfull in $ELAPSED_TIME ms !${reset}" || echo -e "${red}Request to CMC API with currency $currency failed !${reset}"
START_TIME=`echo $(($(date +%s%N)/1000000))`
request2=$(curl --silent -H "X-CMC_PRO_API_KEY: $CMC_API_KEY" -H "Accept: application/json" -d "convert=$currency2" -G "${DATAURL}quotes"/latest?symbol=$symbols)
status2=$?
END_TIME=`echo $(($(date +%s%N)/1000000))`
ELAPSED_TIME=$(($END_TIME - $START_TIME))
[ $status2 -eq 0 ] && echo -e "${green}Request to CMC API with currency $currency2 successfull in $ELAPSED_TIME ms !${reset}" || echo -e "${red}Request to CMC API with currency $currency2 failed !${reset}"
echo -e "${green}Displaying quotes for ${cryptos[@]} ...${reset}\n"
printf "${bold}| %-15s | %-4s | %-9s | %-9s | %-14s | %-7s | %-7s | %-7s | %-7s | %-7s | %-25s ${reset}\n" "Nom" "Symb" " Prix ($symb_currency)" " Prix ($symb_currency2)" " Volume" " % 24h" " % 7j" " % 30j" " % 60j" " % 90j" "Dernière mise-à-jour"
printf "${bold}%141s${reset}\n" " " | tr ' ' '-'
else
# 1 currency
START_TIME=`echo $(($(date +%s%N)/1000000))`
request=$(curl --silent -H "X-CMC_PRO_API_KEY: $CMC_API_KEY" -H "Accept: application/json" -d "convert=$currency" -G "${DATAURL}quotes"/latest?symbol=$symbols)
status=$?
END_TIME=`echo $(($(date +%s%N)/1000000))`
ELAPSED_TIME=$(($END_TIME - $START_TIME))
[ $status -eq 0 ] && echo -e "${green}Request to CMC API with currency $currency successfull in $ELAPSED_TIME ms !${reset}" || echo -e "${red}Request to CMC API with currency $currency failed !${reset}"
echo -e "${green}Displaying quotes for ${cryptos[@]} ...${reset}\n"
printf "${bold}| %-15s | %-4s | %-9s | %-14s | %-7s | %-7s | %-7s | %-7s | %-7s | %-25s ${reset}\n" "Nom" "Symb" " Prix ($symb_currency)" " Volume" " % 24h" " % 7j" " % 30j" " % 60j" " % 90j" "Dernière mise-à-jour"
printf "${bold}%141s${reset}\n" " " | tr ' ' '-'
fi
# Check if NO_COLOR is set to disable colorization
if [ -z "$NO_COLOR" ]; then
: "${COLOR_GREEN:=$'\e[32m'}"
: "${COLOR_RED:=$'\e[31m'}"
: "${COLOR_RESET:=$'\e[00m'}"
fi
#b=$(cat $cryptos_list | yq '.lots')
#echo "$b"
#d=$(cat $cryptos_list | yq '.lots[]')
#echo "$d"
#c=$(cat $cryptos_list | yq '.lots[] | select(.quantity != 0) | .symbol')
counter=0
while [ $counter -lt $nb_cryptos ]; do
c=$(echo "${cryptos[counter]}")
filter=".$c"
#echo "value (crypto): $c"
# devise 1 (EUR)
coin=$(echo "$request" | jq -r '.data' | jq -r $filter)
if [ "$coin" == null ]; then
echo -e "${red}$c n'est pas une crypto !${reset}"
((counter++))
continue
fi
name=$(echo "$coin" | jq -r '.name')
symbol=$(echo "$coin" | jq -r '.symbol')
slug=$(echo "$coin" | jq -r '.slug')
max_supply=$(echo "$coin" | jq -r '.max_supply') # Volume (Mrd) ($2,07 Mrd)
circulating_supply=$(echo "$coin" | jq -r '.circulating_supply') # Supply en circulation (Mio BNB) (144,01 Mio BNB)
total_supply=$(echo "$coin" | jq -r '.total_supply') # Montant total fourni (Mio BNB) (144,01 Mio BNB)
cmc_rank=$(echo "$coin" | jq -r '.cmc_rank') # Rang (N°5)
filter2=".$currency"
quote=$(echo "$coin" | jq -r '.quote'| jq -r $filter2)
price=$(echo "$quote" | jq -r '.price')
volume_24h=$(echo "$quote" | jq -r '.volume_24h')
percent_change_24h=$(echo "$quote" | jq -r '.percent_change_24h')
percent_change_7d=$(echo "$quote" | jq -r '.percent_change_7d')
percent_change_30d=$(echo "$quote" | jq -r '.percent_change_30d')
percent_change_60d=$(echo "$quote" | jq -r '.percent_change_60d')
percent_change_90d=$(echo "$quote" | jq -r '.percent_change_90d')
last_updated=$(echo "$quote" | jq -r '.last_updated') # 2025-01-07T16:35:00.000Z UTC (ZULU)
last_updated_fr=$(date --date="$last_updated" +"%d/%m/%Y %H:%M:%S %Z")
market_cap=$(echo "$quote" | jq -r '.market_cap') # Capitalisation (Mrd) ($103,47 Mrd ~= €95,81 Mrd)
market_cap_dominance=$(echo "$quote" | jq -r '.market_cap_dominance') # Dominance sur le marché (%) (2,94%)
fully_diluted_market_cap=$(echo "$quote" | jq -r '.fully_diluted_market_cap') # Cap. du marché totlalement diluée (Mrd) ($103,47 Mrd ~= €95,81 Mrd)
# devise 2 (USD)
if [ -n "$currency2" ]; then
coin2=$(echo "$request2" | jq -r '.data' | jq -r $filter)
filter3=".$currency2"
quote2=$(echo "$coin2" | jq -r '.quote' | jq -r $filter3)
price2=$(echo "$quote2" | jq -r '.price')
fi
: <<'END_COMMENT'
blabla
END_COMMENT
# Check cryptos we have (from .cryptos.yaml)
z=$(cat $cryptos_list | yq '.lots[] | select(.symbol == "'${c}'")')
### OK ###
#z=$(yq '.[] | select(.symbol == "'${c}'")' <<< "$b")
#echo "z: $z"
if [ ! -z "$z" ]; then
quantity=$(yq '.quantity' <<< $z)
else
quantity=0
fi
### /OK ###
# Côté variables
#COLOR_24h=$([ $(CheckSign "$percent_change_24h") == "+" ] && echo ${COLOR_GREEN} || echo ${COLOR_RED})
#COLOR_7d=$([ $(CheckSign "$percent_change_7d") == "+" ] && echo ${COLOR_GREEN} || echo ${COLOR_RED})
#COLOR_30d=$([ $(CheckSign "$percent_change_30d") == "+" ] && echo ${COLOR_GREEN} || echo ${COLOR_RED})
#COLOR_60d=$([ $(CheckSign "$percent_change_60d") == "+" ] && echo ${COLOR_GREEN} || echo ${COLOR_RED})
#COLOR_90d=$([ $(CheckSign "$percent_change_90d") == "+" ] && echo ${COLOR_GREEN} || echo ${COLOR_RED})
# Côté mise-en-page
COLOR_24h=$([ $(CheckSign "$percent_change_24h") == "+" ] && echo ${green} || echo ${red})
COLOR_7d=$([ $(CheckSign "$percent_change_7d") == "+" ] && echo ${green} || echo ${red})
COLOR_30d=$([ $(CheckSign "$percent_change_30d") == "+" ] && echo ${green} || echo ${red})
COLOR_60d=$([ $(CheckSign "$percent_change_60d") == "+" ] && echo ${green} || echo ${red})
COLOR_90d=$([ $(CheckSign "$percent_change_90d") == "+" ] && echo ${green} || echo ${red})
if [ -n "$currency2" ]; then
# 2 currency
if [ "$color_display" == true ];then
# With % colors
printf "${yellowbold}| %-15s | %-4s | %'9.2f | %'9.2f | %'14d |${reset}${COLOR_24h} %+6.1f%% ${COLOR_RESET}${yellowbold}|${COLOR_RESET}${COLOR_7d} %+6.1f%% ${COLOR_RESET}${yellowbold}|${COLOR_RESET}${COLOR_30d} %+6.1f%% ${COLOR_RESET}${yellowbold}|${COLOR_RESET}${COLOR_60d} %+6.1f%% ${COLOR_RESET}${yellowbold}|${COLOR_RESET}${COLOR_90d} %+6.1f%% ${COLOR_RESET}${yellowbold}| %-25s ${reset}\n" \
"$name" "$symbol" "$price" "$price2" "$volume_24h" "$percent_change_24h" "$percent_change_7d" "$percent_change_30d" "$percent_change_60d" "$percent_change_90d" "$last_updated_fr" 2>/dev/null
else
# Without colors
printf "| %-15s | %-4s | %'9.2f | %'9.2f | %'14d | %+6.1f%% | %+6.1f%% | %+6.1f%% | %+6.1f%% | %+6.1f%% | %-25s \n" "$name" "$symbol" "$price" "$price2" "$volume_24h" "$percent_change_24h" "$percent_change_7d" "$percent_change_30d" "$percent_change_60d" "$percent_change_90d" "$last_updated_fr" 2>/dev/null
fi
else
# 1 currency
if [ "$color_display" == true ];then
# With % colors
printf "${yellowbold}| %-15s | %-4s | %'9.2f | %'14d |${reset}${COLOR_24h} %+6.1f%% ${COLOR_RESET}${yellowbold}|${COLOR_RESET}${COLOR_7d} %+6.1f%% ${COLOR_RESET}${yellowbold}|${COLOR_RESET}${COLOR_30d} %+6.1f%% ${COLOR_RESET}${yellowbold}|${COLOR_RESET}${COLOR_60d} %+6.1f%% ${COLOR_RESET}${yellowbold}|${COLOR_RESET}${COLOR_90d} %+6.1f%% ${COLOR_RESET}${yellowbold}| %-25s ${reset}\n" \
"$name" "$symbol" "$price" "$volume_24h" "$percent_change_24h" "$percent_change_7d" "$percent_change_30d" "$percent_change_60d" "$percent_change_90d" "$last_updated_fr" 2>/dev/null
else
# Without colors
printf "| %-15s | %-4s | %'9.2f | %'14d | %+6.1f%% | %+6.1f%% | %+6.1f%% | %+6.1f%% | %+6.1f%% | %-25s \n" "$name" "$symbol" "$price" "$volume_24h" "$percent_change_24h" "$percent_change_7d" "$percent_change_30d" "$percent_change_60d" "$percent_change_90d" "$last_updated_fr" 2>/dev/null
fi
fi
: <<'END_COMMENT2'
END_COMMENT2
if (( $(bc <<< "$quantity > 0") )); then
quantity=$(yq '.quantity' <<<$z)
unit_cost=$(yq '.unit_cost' <<<$z) # EUR
purchased=$(echo "$quantity * $unit_cost" | bc -l)
valuations=$(echo "$quantity * $price" | bc -l)
profit=$(echo "$valuations - $purchased" | bc -l)
performance=$(echo "($valuations / $purchased - 1) * 100" | bc -l)
# quantity -> Disponible
# unit_cost -> Coût moyen
# purchased -> Acheté
# valuations -> Valoris.
# profit -> +/- value
# performance -> Perf.
printf "| %-11s | %-9s | %-10s | %-9s | %-9s | %-8s \n" "Disponible" "Coût moyen" "Acheté" "Valoris." "+/- value" "Perf." 2>/dev/null
if (( $(bc <<< "$performance > 0") )); then
printf "${green}| %11s | %10.2f | %9.2f | %9.2f | %9.2f | %8.2f%% ${reset}\n\n" "$quantity" "$unit_cost" "$purchased" "$valuations" "$profit" "$performance" 2>/dev/null
else
printf "${red}| %11s | %10.2f | %9.2f | %9.2f | %9.2f | %8.2f%% ${reset}\n\n" "$quantity" "$unit_cost" "$purchased" "$valuations" "$profit" "$performance" 2>/dev/null
fi
else
quantity=0
unit_cost=0
purchased=0
valuations=0
profit=0
fi
((counter++))
done
echo -e "\n${italic}Script finished in $SECONDS seconds.${reset}"

5000
list_coins.txt Normal file

File diff suppressed because it is too large Load Diff

14
loader.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
### Variables for self updating
ScriptArgs=( "$@" )
ScriptPath="$(readlink -f "$0")" # /Users/user/Documents/Scripts/stocks/crypto.sh
ScriptWorkDir="$(dirname "$ScriptPath")" # /Users/user/Documents/Scripts/stocks
$ScriptWorkDir/stocks.sh
echo
echo
$ScriptWorkDir/cryptos.sh

36
purchased.json Normal file
View File

@@ -0,0 +1,36 @@
{
"quotes": [
{
"symbol": "ALCLS.PA",
"price": "1.74",
"volume": "35596",
"last": "1736786105",
"symb": "€",
"percent": "-2.36"
},
{
"symbol": "AUB.PA",
"price": "44.35",
"volume": "3166",
"last": "1736786126",
"symb": "€",
"percent": "-1.44"
},
{
"symbol": "GOOG",
"price": "190.81",
"volume": "6494394",
"last": "1736789006",
"symb": "$",
"percent": "-1.22"
},
{
"symbol": "ORA.PA",
"price": "9.826",
"volume": "5715409",
"last": "1736786117",
"symb": "€",
"percent": "1.17"
}
]
}

587
stocks.sh
View File

@@ -1,181 +1,476 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e set -e
italic="\033[3m" # MIT License
underline="\033[4m" # forked from https://github.com/appatalks/ticker.sh
ita_under="\033[3;4m" # original script https://github.com/pstadler/ticker.sh
bgd="\033[1;4;31m"
red="\033[1;31m" VERSION="v1.0"
green="\033[1;32m"
bold="\033[1m" ### TODO
box="\033[1;41m" # Verif stocks in $@
reset="\033[0m" # Locale compliant
# Localization
### API
: ${TMPDIR:=/tmp}
SESSION_DIR="${TMPDIR%/}/ticker.sh-$(whoami)"
COOKIE_FILE="${SESSION_DIR}/cookies.txt"
API_ENDPOINT="https://query1.finance.yahoo.com/v8/finance/chart/"
API_SUFFIX="?interval=1d"
### Variables
ScriptArgs=( "$@" )
ScriptPath="$(readlink -f "$0")" # /Users/user/Documents/Scripts/stocks/crypto.sh
ScriptWorkDir="$(dirname "$ScriptPath")" # /Users/user/Documents/Scripts/stocks
####### Configurations ######
#NO_COLOR=1
LANG=C LANG=C
LC_NUMERIC=C LC_NUMERIC=C
stocks_list=~/.stocks.yaml # Check if NO_COLOR is set to disable colorization
if [ -z "$NO_COLOR" ]; then
query() { : "${COLOR_GREEN:=$'\e[32m'}"
echo "$obj" | jq -r ".$1" : "${COLOR_GREEN_BOLD:=$'\e[1;32m'}"
} : "${COLOR_RED:=$'\e[31m'}"
: "${COLOR_RED_BOLD:=$'\e[1;31m'}"
round() { : "${COLOR_YELLOW=$'\e[33m'}"
echo $(printf %.$2f $(echo "scale=$2;(((10^$2)*$1)+0.5)/(10^$2)" | bc)) : "${COLOR_YELLOW_BOLD:=$'\e[1;33m'}"
} : "${COLOR_SILVER=$'\e[37m'}"
: "${COLOR_LIGHT_GREY=$'\e[249m'}"
# Test if jq & yq executables are in $PATH : "${COLOR_BRIGHT_PURPLE=$'\e[35;1m'}"
: "${BOLD:=$'\e[1m'}"
if ! $(type jq >/dev/null 2>&1); then : "${ITALIC:=$'\e[3m'}"
echo "'jq' is not in the PATH. (See: https://stedolan.github.io/jq/)" : "${COLOR_RESET:=$'\e[00m'}"
exit 1
fi
if ! $(type yq >/dev/null 2>&1); then
echo "'yq' is not in the PATH. (See: https://github.com/kislyuk/yq"
exit 1
fi
# Read stocks from .stocks.yaml config file
if [ -f $stocks_list ]; then
#a=$(cat ~/.stocks.yaml | yq '.watchlist')
a=$(cat $stocks_list | yq '.watchlist')
b=$(cat $stocks_list | yq '.lots')
else else
echo "$stocks_list not present !" : "${BOLD:=$'\e[1m'}"
echo "" : "${COLOR_RESET:=$'\e[00m'}"
: "${ITALIC:=$'\e[3m'}"
fi fi
while IFS= read -r obj; do
# Help
Help() {
clear
echo -e "\n${COLOR_GREEN_BOLD}Bash Stocks $VERSION${COLOR_RESET}\n"
echo "Syntax: stocks.sh [<stock>]"
echo
echo "Examples: stocks.sh AAPL ORA.PA AUB.PA"
echo " stocks.sh (need ~/.stocks.yaml file)"
echo
echo "Options:"
echo "-h Print this Help."
echo
echo "Requires jq and yq installed"
echo " -https://stedolan.github.io/jq/"
echo " -https://github.com/kislyuk/yq"
}
### Parse options
while getopts "h" opt; do
case ${opt} in
h|*) Help
Help
exit 2
;;
esac
done
shift $((OPTIND -1))
### Config file
stocks_list=$HOME/.stocks.yaml
#json_file="/tmp/purchased.json"
json_file="$ScriptWorkDir/purchased.json"
#json_file=""
####### /Configurations ######
### Requierements
if ! $(type jq >/dev/null 2>&1); then
echo -e "${COLOR_RED}'jq' is not in the PATH. (See: https://stedolan.github.io/jq/)${COLOR_RESET}"
exit 1
fi
if ! $(type yq >/dev/null 2>&1); then
echo -e "${COLOR_RED}'yq' is not in the PATH. (See: https://github.com/kislyuk/yq)${COLOR_RESET}"
exit 1
fi
cat < /dev/null > /dev/tcp/1.1.1.1/53
if [[ $? -ne 0 ]]; then
echo -e "\n${COLOR_RED}No Internet connection !${COLOR_RESET}"
echo -e "Exit !"
exit 1
fi
####### Functions ######
symbol_currency() {
case $currency in
EUR)
echo -n "€"
;;
USD)
echo -n "$"
;;
GBP)
echo -n "£"
;;
JPY)
echo -n "¥"
;;
esac
}
# Check if number is positive (or 0) or negative
CheckSign() {
local n="$1" # Capture the number passed as a parameter.
if [ $(echo "$n < 0" | bc -l) -eq 1 ] ; then
#printf "%+17.13f : %s\n" "$n" "negative"
p="-"
else
#printf "%+17.13f : %s\n" "$n" "positive or zero"
p="+"
fi
echo "$p"
}
[ ! -d "$SESSION_DIR" ] && mkdir -m 700 "$SESSION_DIR"
preflight () {
curl --silent --output /dev/null --cookie-jar "$COOKIE_FILE" "https://finance.yahoo.com" \
-H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"
}
fetch_chart () {
local symbol=$1
local url="${API_ENDPOINT}${symbol}${API_SUFFIX}"
curl --silent -b "$COOKIE_FILE" "$url"
}
[ ! -f "$COOKIE_FILE" ] && preflight
####### Start Display ######
echo -e "${COLOR_GREEN_BOLD}Bash Stocks${COLOR_RESET} $VERSION"
echo -e "Using Yahoo Finance API ($API_ENDPOINT)\n"
### Read quotes from arguments or from~/.stocks.yaml config file
SYMBOLS=()
# from arguments
if [ -n "$ScriptArgs" ]; then
REGEXP='^[a-zA-Z0-9.,_ ]+$'
q="$@"
if [[ "$q" =~ $REGEXP ]]; then
q=${q//,/\ }
q=${q//_/\ }
SYMBOLS+=($q)
else
Help
exit 1
fi
: <<'END_COMMENT'
# Check if quotes passed as argument really exist
for val in ${!SYMBOLS[@]}
do
k="${SYMBOLS[$val]}"
#echo $val # index
#echo $k # value
if [[ ! "$list_coins" == *"$k"* ]]; then
echo -e "${COLOR_RED}$k is not a crypto. Remove it !${COLOR_RESET}"
unset SYMBOLS[$val]
fi
done
# Recreate the array, because the gaps have to disappear
SYMBOLS=("${SYMBOLS[@]}")
END_COMMENT
echo -e "${COLOR_GREEN}Found ${#SYMBOLS[@]} stocks to display: ${SYMBOLS[@]} ...${COLOR_RESET}"
# from ~/.stocks.yaml config file
else
if [ -f "$stocks_list" ]; then
sl=$(cat $stocks_list)
[ $? -eq 0 ] && echo -e "${COLOR_GREEN}Reading $stocks_list ...${COLOR_RESET}" || echo -e "${COLOR_RED}Error while reading $stocks_list ...${COLOR_RESET}"
a=$(yq '.watchlist[]' <<< $sl)
SYMBOLS+=(${a})
[ $? -eq 0 ] && echo -e "${COLOR_GREEN}Found ${#SYMBOLS[@]} stocks to display: ${SYMBOLS[@]} ...${COLOR_RESET}" || echo -e "${COLOR_RED}No coins to display...${COLOR_RESET}"
b=$(yq '.lot' <<< $sl)
u=$((yq '.lots[] | select(.quantity != 0) | .symbol' | tr '\n' ' ') <<< $sl)
purchased=(${u})
[ $? -eq 0 ] && echo -e "${COLOR_GREEN}Found ${#purchased[@]} quotes purchased: ${purchased[@]} ...${COLOR_RESET}" || echo -e "${COLOR_RED}Fail to load quotes list purchased...${COLOR_COLOR_RESET}"
# Check that purchased's stocks are in display list
for val in ${!purchased[@]}
do
x="${purchased[$val]}"
if [[ " ${SYMBOLS[*]} " != *"$x"* ]]; then
SYMBOLS+=(${x})
echo -e "${COLOR_RED}$x is not in stocks's list to display. We add it !${COLOR_GREEN}"
fi
done
[ $? -eq 0 ] && echo -e "${COLOR_GREEN}Found ${#SYMBOLS[@]} stocks to display: ${SYMBOLS[@]} ...${COLOR_RESET}" || echo -e "${COLOR_RED}No coins to display...${COLOR_RESET}"
else
echo -e "${COLOR_RED}$stocks_list not present !${COLOR_RESET}"
echo ""
fi
#echo "${SYMBOLS[@]}"
#echo "${purchased[@]}"
: <<'END_COMMENT'
while IFS= read -r obj; do
#echo "$obj"
stock=${obj:2} stock=${obj:2}
list+="$stock," #echo "$stock"
done < <(echo "$a") SYMBOLS+=("$stock")
done < <(echo "$a")
[ $status -eq 0 ] && echo -e "${COLOR_GREEN}Found ${#SYMBOLS[@]} stocks to display: ${SYMBOLS[@]} ...${COLOR_RESET}" || echo -e "${COLOR_RED}No coins to display...${COLOR_RESET}"
END_COMMENT
if [ -z "$SYMBOLS" ]; then
echo "Usage: ./crypto.sh"
echo " - add quotes to ~/.stocks.yaml file"
exit 1
fi
symbols=$(echo "$list" | sed 's/.$//')
# AC.PA ATO.PA AUB.PA CS.PA DG.PA ^FCHI VIRP.PA WLN.PA
#####
if [ -z "$symbols" ]; then
echo "Usage: ./stocks.sh"
echo " - add stocks to ~/.stocks.yaml file"
exit
fi fi
# Get stocks data from yahoo finance : <<'END_COMMENT'
### Parse options
API_ENDPOINT="https://query1.finance.yahoo.com/v7/finance/quote?lang=fr-FR&region=FR&corsDomain=finance.yahoo.com" while getopts "g" opt; do
case ${opt} in
h )
echo "Usage: ./ticker.sh [-g] AAPL GOOG ORA.PA"
exit 1
;;
esac
done
shift $((OPTIND -1))
results=$(curl --silent "$API_ENDPOINT&fields=$fields&symbols=$symbols" | jq '.quoteResponse .result')
# Display stocks data
t=0 END_COMMENT
echo -e "${bold}stocks.sh${reset}\n"
while IFS= read -r obj; do # volume %11s
marketState=$(query 'marketState') echo
symbol=$(query 'symbol') printf "%s%-22s %-8s %9s %8s %8s %9s %9s %11s %9s %9s %18s %18s %s \n" \
"${BOLD}" "ShortName" "Symbol" \
"Current" "Change" "Percent" \
"Day High" "Day Low" "Volume" \
"52w +" "52w -" "Last Date" "Timezone" \
"${COLOR_RESET}"
# Sort arrays (lists of quotes / purchased)
IFS=$'\n'
SYMBOLS=($(sort <<<"${SYMBOLS[*]}"))
purchased=($(sort <<<"${purchased[*]}"))
unset IFS
### Create JSON purchased quotes
# Create an initial block
jq -n '{"quotes":[]}' > "${json_file}"
ii=1
# Initialize an array to hold background process IDs (avec les PID activés => affichage dans le désordre (au fil de l'eau))
pids=()
echo "quotes: ${#SYMBOLS[@]} - ${SYMBOLS[@]}"
echo "purchased: ${#purchased[@]} - ${purchased[@]}"
for symbol in "${SYMBOLS[@]}"; do
(
# Running in subshell
results=$(fetch_chart "$symbol")
regularMarketTime=$(echo "$results" | jq -r '.chart.result[0].meta.regularMarketTime')
#rdate -d @$regularMarketTime +"%c"
rmt=$(LC_ALL=fr_FR.UTF-8 date -d @$regularMarketTime +"%d/%m/%y %H:%M:%S" 2>/dev/null || LC_ALL=fr_FR.UTF-8 date -r $regularMarketTime +"%d/%m/%y %H:%M:%S")
exchangeTimezoneName=$(echo "$results" | jq -r '.chart.result[0].meta.exchangeTimezoneName')
fiftyTwoWeekHigh=$(echo "$results" | jq -r '.chart.result[0].meta.fiftyTwoWeekHigh')
fiftyTwoWeekLow=$(echo "$results" | jq -r '.chart.result[0].meta.fiftyTwoWeekLow')
regularMarketDayHigh=$(echo "$results" | jq -r '.chart.result[0].meta.regularMarketDayHigh')
regularMarketDayLow=$(echo "$results" | jq -r '.chart.result[0].meta.regularMarketDayLow')
regularMarketVolume=$(echo "$results" | jq -r '.chart.result[0].meta.regularMarketVolume')
longName=$(echo "$results" | jq -r '.chart.result[0].meta.longName')
shortName=$(echo "$results" | jq -r '.chart.result[0].meta.shortName')
currentPrice=$(echo "$results" | jq -r '.chart.result[0].meta.regularMarketPrice')
previousClose=$(echo "$results" | jq -r '.chart.result[0].meta.chartPreviousClose')
currency=$(echo "$results" | jq -r '.chart.result[0].meta.currency')
symb=$(symbol_currency $currency)
symbol=$(echo "$results" | jq -r '.chart.result[0].meta.symbol')
[ "$previousClose" = "null" ] && previousClose="1.0"
priceChange=$(awk -v currentPrice="$currentPrice" -v previousClose="$previousClose" 'BEGIN {printf "%.2f", currentPrice - previousClose}')
percentChange=$(awk -v currentPrice="$currentPrice" -v previousClose="$previousClose" 'BEGIN {printf "%.2f", ((currentPrice - previousClose) / previousClose) * 100}')
open=$(echo "$results" | jq -r '.chart.result[0].indicators.quote' | jq -r '.[].open[0]')
#firstTradeDate=$(echo "$results" | jq -r '.chart.result[0].meta.firstTradeDate')
#ftd==$(LC_ALL=fr_FR.UTF-8 date -d @$firstTradeDate +"%c" 2>/dev/null || LC_ALL=fr_FR.UTF-8 date -r $ts +"%c")
#volume=$(echo "$results" | jq -r '.chart.result[0].indicators.quote' | jq -r '.[].volume[0]')
#high=$(echo "$results" | jq -r '.chart.result[0].indicators.quote' | jq -r '.[].high[0]')
#close=$(echo "$results" | jq -r '.chart.result[0].indicators.quote' | jq -r '.[].close[0]')
if [[ " ${purchased[*]} " == *"$symbol"* ]]; then
# Create a temporary JSON file for purchased quotes because requests arrive as they come in (not in order)
# https://www.codegix.com/use-jq-to-create-a-dynamic-json-data-in-bash/
json_data=$(jq \
--arg symbol "$symbol" \
--arg price "$currentPrice" \
--arg volume "$regularMarketVolume" \
--arg last "$regularMarketTime" \
--arg symb "$symb" \
--arg percent "$percentChange" \
--argjson value "$ii" '.quotes += [{ "symbol": $symbol, "price": $price, "volume": $volume, "last": $last, "symb": $symb, "percent": $percent }]' "${json_file}")
echo "${json_data}" > "${json_file}"
((ii++))
: <<'END_COMMENT'
# Check quotes we have (from .stocks.yaml)
z=$(echo "$sl" | yq '.lots[] | select(.symbol == "'${symbol}'")')
q=$(echo "$z" | yq '.quantity')
uc=$(echo "$z" | yq '.unit_cost')
purchase_cost=$(echo "$q * $uc" | bc -l)
valuations=$(echo "$q * $currentPrice" | bc -l)
profit=$(echo "$valuations - $purchase_cost" | bc -l)
performance=$(echo "($valuations / $purchase_cost - 1) * 100" | bc -l)
#performance=$(round $performance 2)
echo "$q - $uc - $purchase_cost - $valuations - $profit - $performance"
END_COMMENT
preMarketChange=$(query 'preMarketChange')
postMarketChange=$(query 'postMarketChange')
longName=$(query 'longName')
if [ "$longName" == "null" ] && [ $symbol == "^FCHI" ]; then
longName="CAC 40"
fi fi
if [ $t == "0" ]; then
printf "${bold}%-10s | %-21s | %-7s | " "Symbole" "Entreprise" "Cours"
printf "%-6s | %-7s | %-7s | " "Diff" "%" "Veille"
printf "%-7s | %-7s | %-8s | " "+ Haut" "+ Bas" "Volume"
printf "%-9s | %-25s | %-18s | " "Ouverture" "Heure" "52 semaines"
printf "%-7s | %-9s | %-9s | %-6s${reset}\n" "Qté" "Valoris." "+/- value" "Perf."
fi
t=1
if [ $marketState == "PRE" ] && [ $preMarketChange != "0" ] && [ $preMarketChange != "null" ]; then COLOR_pri=$([ $(CheckSign "$priceChange") == "-" ] && echo ${COLOR_RED} || echo ${COLOR_GREEN})
nonRegularMarketSign='*' COLOR_per=$([ $(CheckSign "$percentChange") == "-" ] && echo ${COLOR_RED} || echo ${COLOR_GREEN})
price=$(query 'preMarketPrice')
diff=$preMarketChange if [ -z "$NO_COLOR" ]; then
percent=$(query 'preMarketChangePercent') printf "%s%-22s %-8s %8.2f%s%s %s%7.2f%s%s %s%7.2f%%%s %s%8.2f%s %8.2f%s %11s %8.2f%s %8.2f%s %18s %18s %s\n" \
elif [ $marketState != "REGULAR" ] && [ $postMarketChange != "0" ] && [ $postMarketChange != "null" ]; then "${COLOR_YELLOW_BOLD}" "$shortName" "$symbol" \
nonRegularMarketSign='*' "$currentPrice" "$symb" "${COLOR_RESET}" "${COLOR_pri}" "$priceChange" "$symb" "${COLOR_RESET}" "${COLOR_per}" "$percentChange" "${COLOR_RESET}" \
price=$(query 'postMarketPrice') "${COLOR_YELLOW_BOLD}" "$regularMarketDayHigh" "$symb" "$regularMarketDayLow" "$symb" "$regularMarketVolume" \
diff=$postMarketChange "$fiftyTwoWeekHigh" "$symb" "$fiftyTwoWeekLow" "$symb" "$rmt" "$exchangeTimezoneName" "${COLOR_RESET}"\
percent=$(query 'postMarketChangePercent')
else else
nonRegularMarketSign='' printf "%-22s %-8s %8.2f%s %7.2f%s %7.2f%% %8.2f%s %8.2f%s %11s %8.2f%s %8.2f%s %18s %18s\n" \
price=$(query 'regularMarketPrice') "$shortName" "$symbol" \
diff=$(query 'regularMarketChange') "$currentPrice" "$symb" "$priceChange" "$symb" "$percentChange" \
percent=$(query 'regularMarketChangePercent') "$regularMarketDayHigh" "$symb" "$regularMarketDayLow" "$symb" "$regularMarketVolume" \
previous=$(query 'regularMarketPreviousClose') "$fiftyTwoWeekHigh" "$symb" "$fiftyTwoWeekLow" "$symb" "$rmt" "$exchangeTimezoneName"
volume=$(query 'regularMarketVolume')
haut=$(query 'regularMarketDayHigh')
bas=$(query 'regularMarketDayLow')
ouverture=$(query 'regularMarketOpen')
ts=$(query 'regularMarketTime')
heure=$(LC_ALL=fr_FR.UTF-8 date -d @$ts +"%c" 2>/dev/null || LC_ALL=fr_FR.UTF-8 date -r $ts +"%c")
ftweeks=$(query 'fiftyTwoWeekRange')
fi fi
# Stocks owned ) #&
#z=$(jq -c '.[] | select(.symbol == "'${symbol}'")' <<<$b)
z=$(yq '.[] | select(.symbol == "'${symbol}'")' <<< "$b")
if [ -n "$z" ]; then # Stack PIDs
#echo "$z" pids+=($!)
#quantity=$(jq -c '.[] | select(.symbol == "'${symbol}'") | .quantity' <<< $b)
#quantity=$(jq -c '.quantity' <<<$z)
#unit_cost=$(jq -c '.unit_cost' <<<$z)
quantity=$(yq '.quantity' <<<$z)
unit_cost=$(yq '.unit_cost' <<<$z)
purchase=$(echo "$quantity * $unit_cost" | bc -l)
valuations=$(echo "$quantity * $price" | bc -l)
profit=$(echo "$valuations - $purchase" | bc -l)
performance=$(echo "($valuations / $purchase - 1) * 100" | bc -l)
performance=$(round $performance 2)
#valuation=$(($quantity * $price)) done
#profit=$((valuations-purchase))
#profit=$(($valuations-$purchase))
#profit=$(($purchase-$valuations))
#((profit = valuations - purchase))
#echo "Qte: $quantity - PM: $unit_cost - Achat: $purchase - Valorisation: $valuations" # Wait for all background processes to finish
#echo "+-value: $profit - Perf: $performance%" for pid in "${pids[@]}"; do
fi wait "$pid"
done
# Lines colors: + green ; - red ; 0 no color sleep 1
if [ "$diff" == "0" ] || [ "$diff" == "0.0" ]; then
color=
elif (echo "$diff" | grep -q ^-); then
color=$red
#cours_color="\033[3;31m"
cours_color="\033[1;37;1;41m"
else
color=$green
#cours_color="\033[3;32m"
cours_color="\033[1;37;1;42m"
fi
# Displaying echo "quotes: ${#SYMBOLS[@]} - ${SYMBOLS[@]}"
if [ "$price" != "null" ]; then echo "purchased: ${#purchased[@]} - ${purchased[@]}"
printf "${color}%-10s | %-21s | ${reset}${cours_color}%7.2f${reset}${color} | " $symbol "$longName" $price #cat $json_file
printf "% 6.2f | % 6.2f%% | %7.2f | " $diff $percent $previous
printf "%7.2f | %7.2f | %8d | " $haut $bas $volume
printf "%9.2f | %25s | %18s | " $ouverture "$heure" "$ftweeks"
if [ -n "$z" ]; then
printf "%6d | %9.2f | %9.2f | % 6.2f%%${reset}\n" $quantity $valuations $profit $performance
else
echo ""
fi
fi
done < <(echo "$results" | jq -c '.[]') if [ ${#purchased[@]} -gt 0 ]; then
d=$(LC_ALL=fr_FR.UTF-8 TZ='Europe/Paris' date +"%c") echo
echo -e "\n${reset}Dernière mise-à-jour: ${bold}$d${reset}" echo -e "${COLOR_GREEN}Displaying purchased quotes ...${COLOR_RESET}"
echo
quotes="$(cat $json_file)"
counter=0
printf "%s%-8s %13s %10s %9s %11s %16s %13s %13s %13s %11s %18s %s\n" \
"${BOLD}" "Symbol" "Percent" "Current" "Quantity" "Unit cost" "Purchase cost" "Valuations" "Profit" "Performance" "Volume" "Last quote" "${COLOR_RESET}"
while [ "$counter" -lt "${#purchased[@]}" ]; do
purchase="${purchased[$counter]}"
s=$(echo "$quotes" | jq -r '.quotes | .[] | select(.symbol == "'${purchase}'") | (.symbol)') # Symbol
p=$(echo "$quotes" | jq -r '.quotes | .[] | select(.symbol == "'${purchase}'") | (.price)') # Price
v=$(echo "$quotes" | jq -r '.quotes | .[] | select(.symbol == "'${purchase}'") | (.volume)') # Volume
l=$(echo "$quotes" | jq -r '.quotes | .[] | select(.symbol == "'${purchase}'") | (.last)') # Last quote
rmt=$(LC_ALL=fr_FR.UTF-8 date -d @$l +"%d/%m/%y %H:%M:%S" 2>/dev/null || LC_ALL=fr_FR.UTF-8 date -r $l +"%d/%m/%y %H:%M:%S")
#echo $l # 1736526916
#rmt=$(LC_ALL=fr_FR.UTF-8 date -d "@$l" +"%c" 2>/dev/null || LC_ALL=fr_FR.UTF-8 date -r "$l" +"%c")
sy=$(echo "$quotes" | jq -r '.quotes | .[] | select(.symbol == "'${purchase}'") | (.symb)') # Symb (€/$/£)
pc=$(echo "$quotes" | jq -r '.quotes | .[] | select(.symbol == "'${purchase}'") | (.percent)') # Percent change
z=$(echo "$sl" | yq '.lots[] | select(.symbol == "'${purchase}'")')
q=$(echo "$z" | yq '.quantity')
uc=$(echo "$z" | yq '.unit_cost')
purchase_cost=$(echo "$q * $uc" | bc -l)
valuations=$(echo "$q * $p" | bc -l)
profit=$(echo "$valuations - $purchase_cost" | bc -l)
performance=$(echo "($valuations / $purchase_cost - 1) * 100" | bc -l)
#performance=$(round $performance 2)
COLOR_performance=$([ $(CheckSign "$performance") == "-" ] && echo ${COLOR_RED} || echo ${COLOR_GREEN})
#echo "$s - $p - $v - $rmt - $q - $uc - $purchase_cost - $valuations - $profit - $performance"
#echo "$purchase_cost - $valuations - $profit - $performance"
# symbol(s) percent (pc) price(p) quantity(q) unit_cost(uc) purchase_cost valuations profit performance volume(v) last_quote(l)
printf "%s%-8s %12.2f%% %9.2f%s %9d %10.2f%s %15.2f%s %12.2f%s %12.2f%s %12.2f%% %11d %18s %s\n" \
"${COLOR_performance}" "$s" "$pc" "$p" "$sy" "$q" "$uc" "$sy" "$purchase_cost" "$sy" "$valuations" "$sy" "$profit" "$sy" "$performance" "$v" "$rmt" "${COLOR_RESET}"
((++counter))
done
fi
echo -e "\n${ITALIC}Script with ${#SYMBOLS[@]} requests to Yahoo Finance API finished in $SECONDS seconds.${COLOR_RESET}"

185
stocks_legacy.sh Executable file
View File

@@ -0,0 +1,185 @@
#!/usr/bin/env bash
set -e
italic="\033[3m"
underline="\033[4m"
ita_under="\033[3;4m"
bgd="\033[1;4;31m"
red="\033[1;31m"
green="\033[1;32m"
bold="\033[1m"
box="\033[1;41m"
reset="\033[0m"
LANG=C
LC_NUMERIC=C
stocks_list=~/.stocks.yaml
query() {
echo "$obj" | jq -r ".$1"
}
round() {
echo $(printf %.$2f $(echo "scale=$2;(((10^$2)*$1)+0.5)/(10^$2)" | bc))
}
# Test if jq & yq executables are in $PATH
if ! $(type jq >/dev/null 2>&1); then
echo "'jq' is not in the PATH. (See: https://stedolan.github.io/jq/)"
exit 1
fi
if ! $(type yq >/dev/null 2>&1); then
echo "'yq' is not in the PATH. (See: https://github.com/kislyuk/yq"
exit 1
fi
# Read stocks from .stocks.yaml config file
if [ -f $stocks_list ]; then
#a=$(cat ~/.stocks.yaml | yq '.watchlist')
a=$(cat $stocks_list | yq '.watchlist')
b=$(cat $stocks_list | yq '.lots')
else
echo "$stocks_list not present !"
echo ""
fi
while IFS= read -r obj; do
stock=${obj:2}
list+="$stock,"
done < <(echo "$a")
symbols=$(echo "$list" | sed 's/.$//')
# AC.PA ATO.PA AUB.PA CS.PA DG.PA ^FCHI VIRP.PA WLN.PA
#####
if [ -z "$symbols" ]; then
echo "Usage: ./stocks_legacy.sh"
echo " - add stocks to ~/.stocks.yaml file"
exit
fi
# Get stocks data from yahoo finance
API_ENDPOINT="https://query1.finance.yahoo.com/v7/finance/quote?lang=fr-FR&region=FR&corsDomain=finance.yahoo.com"
results=$(curl --silent "$API_ENDPOINT&fields=$fields&symbols=$symbols" | jq '.quoteResponse .result')
# Display stocks data
t=0
echo -e "${bold}stocks_legacy.sh${reset}\n"
#query() {
# echo "$obj" | jq -r ".$1"
#}
while IFS= read -r obj; do
marketState=$(query 'marketState')
symbol=$(query 'symbol')
preMarketChange=$(query 'preMarketChange')
postMarketChange=$(query 'postMarketChange')
longName=$(query 'longName')
if [ "$longName" == "null" ] && [ $symbol == "^FCHI" ]; then
longName="CAC 40"
fi
if [ $t == "0" ]; then
printf "${bold}%-10s | %-21s | %-7s | " "Symbole" "Entreprise" "Cours"
printf "%-6s | %-7s | %-7s | " "Diff" "%" "Veille"
printf "%-7s | %-7s | %-8s | " "+ Haut" "+ Bas" "Volume"
printf "%-9s | %-25s | %-18s | " "Ouverture" "Heure" "52 semaines"
printf "%-7s | %-9s | %-9s | %-6s${reset}\n" "Qté" "Valoris." "+/- value" "Perf."
fi
t=1
if [ $marketState == "PRE" ] && [ $preMarketChange != "0" ] && [ $preMarketChange != "null" ]; then
nonRegularMarketSign='*'
price=$(query 'preMarketPrice')
diff=$preMarketChange
percent=$(query 'preMarketChangePercent')
elif [ $marketState != "REGULAR" ] && [ $postMarketChange != "0" ] && [ $postMarketChange != "null" ]; then
nonRegularMarketSign='*'
price=$(query 'postMarketPrice')
diff=$postMarketChange
percent=$(query 'postMarketChangePercent')
else
nonRegularMarketSign='' #nok
price=$(query 'regularMarketPrice')
diff=$(query 'regularMarketChange') #nok
percent=$(query 'regularMarketChangePercent') #nok
previous=$(query 'regularMarketPreviousClose') #nok
volume=$(query 'regularMarketVolume')
haut=$(query 'regularMarketDayHigh')
bas=$(query 'regularMarketDayLow')
ouverture=$(query 'regularMarketOpen') #nok
ts=$(query 'regularMarketTime')
heure=$(LC_ALL=fr_FR.UTF-8 date -d @$ts +"%c" 2>/dev/null || LC_ALL=fr_FR.UTF-8 date -r $ts +"%c")
ftweeks=$(query 'fiftyTwoWeekRange') #nok
fi
# Stocks owned
#z=$(jq -c '.[] | select(.symbol == "'${symbol}'")' <<<$b)
z=$(yq '.[] | select(.symbol == "'${symbol}'")' <<< "$b")
if [ -n "$z" ]; then
#echo "$z"
#quantity=$(jq -c '.[] | select(.symbol == "'${symbol}'") | .quantity' <<< $b)
#quantity=$(jq -c '.quantity' <<<$z)
#unit_cost=$(jq -c '.unit_cost' <<<$z)
quantity=$(yq '.quantity' <<<$z)
unit_cost=$(yq '.unit_cost' <<<$z)
purchase=$(echo "$quantity * $unit_cost" | bc -l)
valuations=$(echo "$quantity * $price" | bc -l)
profit=$(echo "$valuations - $purchase" | bc -l)
performance=$(echo "($valuations / $purchase - 1) * 100" | bc -l)
performance=$(round $performance 2)
#valuation=$(($quantity * $price))
#profit=$((valuations-purchase))
#profit=$(($valuations-$purchase))
#profit=$(($purchase-$valuations))
#((profit = valuations - purchase))
#echo "Qte: $quantity - PM: $unit_cost - Achat: $purchase - Valorisation: $valuations"
#echo "+-value: $profit - Perf: $performance%"
fi
# Lines colors: + green ; - red ; 0 no color
if [ "$diff" == "0" ] || [ "$diff" == "0.0" ]; then
color=
elif (echo "$diff" | grep -q ^-); then
color=$red
#cours_color="\033[3;31m"
cours_color="\033[1;37;1;41m"
else
color=$green
#cours_color="\033[3;32m"
cours_color="\033[1;37;1;42m"
fi
# Displaying
if [ "$price" != "null" ]; then
printf "${color}%-10s | %-21s | ${reset}${cours_color}%7.2f${reset}${color} | " $symbol "$longName" $price
printf "% 6.2f | % 6.2f%% | %7.2f | " $diff $percent $previous
printf "%7.2f | %7.2f | %8d | " $haut $bas $volume
printf "%9.2f | %25s | %18s | " $ouverture "$heure" "$ftweeks"
if [ -n "$z" ]; then
printf "%6d | %9.2f | %9.2f | % 6.2f%%${reset}\n" $quantity $valuations $profit $performance
else
echo ""
fi
fi
done < <(echo "$results" | jq -c '.[]')
d=$(LC_ALL=fr_FR.UTF-8 TZ='Europe/Paris' date +"%c")
echo -e "\n${reset}Dernière mise-à-jour: ${bold}$d${reset}"

View File

@@ -1,94 +0,0 @@
#!/usr/bin/env bash
set -e
LANG=C
LC_NUMERIC=C
SYMBOLS=("$@")
if ! $(type jq > /dev/null 2>&1); then
echo "'jq' is not in the PATH. (See: https://stedolan.github.io/jq/)"
exit 1
fi
if [ -z "$SYMBOLS" ]; then
echo "Usage: ./ticker.sh AAPL MSFT GOOG BTC-USD"
exit
fi
FIELDS=(symbol marketState regularMarketPrice regularMarketChange regularMarketChangePercent regularMarketVolume regularMarketPreviousClose \
preMarketPrice preMarketChange preMarketChangePercent postMarketPrice postMarketChange postMarketChangePercent \
shortName longName fullExchangeName)
#API_ENDPOINT="https://query1.finance.yahoo.com/v7/finance/quote?lang=en-US&region=US&corsDomain=finance.yahoo.com"
API_ENDPOINT="https://query1.finance.yahoo.com/v7/finance/quote?lang=fr-FR&region=FR&corsDomain=finance.yahoo.com"
if [ -z "$NO_COLOR" ]; then
: "${COLOR_BOLD:=\e[1;37m}"
: "${COLOR_GREEN:=\e[32m}"
: "${COLOR_RED:=\e[31m}"
: "${COLOR_RESET:=\e[00m}"
fi
symbols=$(IFS=,; echo "${SYMBOLS[*]}")
echo "$symbols"
fields=$(IFS=,; echo "${FIELDS[*]}")
results=$(curl --silent "$API_ENDPOINT&fields=$fields&symbols=$symbols" | jq '.quoteResponse .result')
#echo "$results"
query () {
echo $results | jq -r ".[] | select(.symbol == \"$1\") | .$2"
}
for symbol in $(IFS=' '; echo "${SYMBOLS[*]}" | tr '[:lower:]' '[:upper:]'); do
marketState="$(query $symbol 'marketState')"
if [ -z $marketState ]; then
printf 'No results for symbol "%s"\n' $symbol
continue
fi
preMarketChange="$(query $symbol 'preMarketChange')"
postMarketChange="$(query $symbol 'postMarketChange')"
longName="$(query $symbol 'longName')"
#regularMarketPreviousClose="$(query $symbol 'regularMarketPreviousClose')"
if [ $marketState == "PRE" ] \
&& [ $preMarketChange != "0" ] \
&& [ $preMarketChange != "null" ]; then
nonRegularMarketSign='*'
price=$(query $symbol 'preMarketPrice')
diff=$preMarketChange
percent=$(query $symbol 'preMarketChangePercent')
elif [ $marketState != "REGULAR" ] \
&& [ $postMarketChange != "0" ] \
&& [ $postMarketChange != "null" ]; then
nonRegularMarketSign='*'
price=$(query $symbol 'postMarketPrice')
diff=$postMarketChange
percent=$(query $symbol 'postMarketChangePercent')
else
nonRegularMarketSign=''
price=$(query $symbol 'regularMarketPrice')
diff=$(query $symbol 'regularMarketChange')
percent=$(query $symbol 'regularMarketChangePercent')
previous=$(query $symbol 'regularMarketPreviousClose')
fi
if [ "$diff" == "0" ] || [ "$diff" == "0.0" ]; then
color=
elif ( echo "$diff" | grep -q ^- ); then
color=$COLOR_RED
else
color=$COLOR_GREEN
fi
if [ "$price" != "null" ]; then
#printf "%-10s$COLOR_BOLD%8.2f$COLOR_RESET" $symbol $price
printf "%-10s%-35s$COLOR_BOLD%8.2f$COLOR_RESET" $symbol "$longName" $price
printf "$color%10.2f%12s$COLOR_RESET%+10s" $diff $(printf "(%.2f%%)" $percent) $previous
printf " %s\n" "$nonRegularMarketSign"
fi
done