Open carlos1172 opened 1 year ago
right now it looks like this, and takes up way too much of my menu bar
Here's @longpdo's adapted code
#
# <xbar.title>USD Ticker</xbar.title>
# <xbar.version>v1.1</xbar.version>
# <xbar.author>Long Do</xbar.author>
# <xbar.author.github>longpdo</xbar.author.github>
# <xbar.desc>Shows major stock indices in the menu bar and stock symbols in the dropdown menu by pulling data from the Yahoo Finance API. Similar to finance.yahoo.com the prices are delayed, but no API key is necessary. You can also set price alarms for BUY/SELL limits, which will notify you when the limit is reached.</xbar.desc>
# <xbar.image>https://github.com/longpdo/bitbar-plugins-custom/raw/master/images/yahoo-stock-ticker.png</xbar.image>
# <xbar.dependencies>python3</xbar.dependencies>
# <xbar.abouturl>https://github.com/longpdo/bitbar-plugins-custom/blob/master/README.md#yahoo-stock-ticker</xbar.abouturl>
#
# by longpdo (https://github.com/longpdo)
from datetime import datetime
import json
import os
import re
import sys
import subprocess
import pandas as pd
SHEET_ID = '13MHMYt9obuVuwYjhbq4ZKqNapv8Uw7nsunwBO-zCU_Y'
SHEET_NAME = 'Sheet1'
url = f'https://docs.google.com/spreadsheets/d/{SHEET_ID}/gviz/tq?tqx=out:csv&sheet={SHEET_NAME}'
df = pd.read_csv(url)
# ---------------------------------------------------------------------------------------------------------------------
# Enter your stock symbols here in the format: ["symbol1", "symbol2", ...]
symbols = ["USDPHP=X","SWRD.L","WSML.L","EIMI.L","IMID.L"]
# Enter the order how you want to sort the stock list:
# 'name' : Sort alphabetically by name from A to Z
# 'market_change_winners' : Sort by value from top winners to losers
# 'market_change_losers' : Sort by value from top losers to winners
# 'market_change_volatility' : Sort by absolute value from top to bottom
# '' or other values : Sort by your custom order from the symbols array above
sort_by = ''
# ---------------------------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------------------------
# CODE STARTING BELOW HERE, DO NOT EDIT IF YOU ARE A REGULAR USER
# Variables
indices_dict = {
'USDPHP=X':'USD',
'SWRD.L':'SWRD',
'WSML.L':'WSML',
'EIMI.L':'EIMI',
'IMID.L':' IMID',
}
GREEN = '\033[32m'
RED = '\033[31m'
RESET = '\033[0m'
WHITE = '\033[97m'
FONT = "| font='Menlo'"
AVECOSTSWRD = float(df.iat[0, 1])
AVECOSTWSML = float(df.iat[1, 1])
AVECOSTEIMI = float(df.iat[2, 1])
AVECOSTVDST = 51.3587
WTSWRD = float(df.iat[0, 0])
WTWSML = float(df.iat[1, 0])
WTEIMI = float(df.iat[2, 0])
IMIDHLD = str(int(df.iat[0, 3]))
IMIDAUM = "$" + str(int(float(df.iat[1, 3])/1000000)) + "M"
THREEFPHLD = str(int(df.iat[0, 4]))
THREEFPAUM = "$" + str(round(float(df.iat[1, 4])/1000000000,1)) + "B"
# Price of SWRD*WT+WSML*WT+EIMI*WT since Inception (4/24/2023)
SWRDSTART = 29.09
WSMLSTART = 6.314
EIMISTART = 29.38
IMIDSTART = 182.43
SWRDNAVSTART = 29.11970
WSMLNAVSTART = 6.32
EIMINAVSTART = 29.39
IMIDNAVSTART = 182.39530
SWRDNAVLAST = round(float(df.iat[1, 6]),2)/SWRDNAVSTART*100
WSMLNAVLAST = round(float(df.iat[1, 7]),2)/WSMLNAVSTART*100
EIMINAVLAST = round(float(df.iat[1, 8]),2)/EIMINAVSTART*100
IMIDNAVLAST = round(float(df.iat[1, 5]),2)/IMIDNAVSTART*100
colorIMIDHLDPct = ''
if round(int(df.iat[0, 3])-int(df.iat[2, 3]),2) > 0:
colorIMIDHLDPct = GREEN
if round(int(df.iat[0, 3])-int(df.iat[2, 3]),2) < 0:
colorIMIDHLDPct = RED
IMIDHLDPct = colorIMIDHLDPct + "(" + str(round(int(df.iat[0, 3])-int(df.iat[2, 3]),2)) + ")" + RESET
colorIMIDAUMPct = ''
if round((float(df.iat[1, 3])-float(df.iat[3, 3]))/float(df.iat[3, 3])*100,2) > 0:
colorIMIDAUMPct = GREEN
if round((float(df.iat[1, 3])-float(df.iat[3, 3]))/float(df.iat[3, 3])*100,2) < 0:
colorIMIDAUMPct = RED
IMIDAUMPct = colorIMIDAUMPct + "(" + str(round((float(df.iat[1, 3])-float(df.iat[3, 3]))/float(df.iat[3, 3])*100,2)) + " bps)" + RESET
colorTHREEFPHLDPct = ''
if round(int(df.iat[0, 4])-int(df.iat[2, 4]),2) > 0:
colorTHREEFPHLDPct = GREEN
if round(int(df.iat[0, 4])-int(df.iat[2, 4]),2) < 0:
colorTHREEFPHLDPct = RED
THREEFPHLDPct = colorTHREEFPHLDPct + "(" + str(round(int(df.iat[0, 4])-int(df.iat[2, 4]),2)) + ")" + RESET
colorTHREEFPAUMPct = ''
if round((float(df.iat[1, 4])-float(df.iat[3, 4]))/float(df.iat[3, 4])*100,2) > 0:
colorTHREEFPAUMPct = GREEN
if round((float(df.iat[1, 4])-float(df.iat[3, 4]))/float(df.iat[3, 4])*100,2) < 0:
colorTHREEFPAUMPct = RED
THREEFPAUMPct = colorTHREEFPAUMPct + "(" + str(round((float(df.iat[1, 4])-float(df.iat[3, 4]))/float(df.iat[3, 4])*100,2)) + " bps)" + RESET
# ---------------------------------------------------------------------------------------------------------------------
# macOS Alerts, Prompts and Notifications -----------------------------------------------------------------------------
# Display a macOS specific alert dialog to get confirmation from user to continue
def alert(alert_title='', alert_text='', alert_buttons=['Cancel', 'OK']):
try:
d = locals()
user_input = subprocess.check_output(['osascript', '-l', 'JavaScript', '-e', '''
const app = Application.currentApplication()
app.includeStandardAdditions = true
const response = app.displayAlert('{alert_title}', {{
message: '{alert_text}',
as: 'critical',
buttons: ['{alert_buttons[0]}', '{alert_buttons[1]}'],
defaultButton: '{alert_buttons[1]}',
cancelButton: '{alert_buttons[0]}'
}})
response
'''.format(**d)]).decode('ascii').rstrip()
return user_input
except subprocess.CalledProcessError:
pass
# Display a macOS specific prompt dialog to get text input from the user
def prompt(prompt_text=''):
try:
d = locals()
user_input = subprocess.check_output(['osascript', '-l', 'JavaScript', '-e', '''
const app = Application.currentApplication()
app.includeStandardAdditions = true
const response = app.displayDialog('{prompt_text}', {{
defaultAnswer: '',
buttons: ['Cancel', 'OK'],
defaultButton: 'OK'
}})
response.textReturned
'''.format(**d)]).decode('ascii').rstrip()
if user_input == '':
sys.exit()
return user_input
except subprocess.CalledProcessError:
pass
# Display a macOS specific prompt dialog prompting user for a choice from a list
def prompt_selection(prompt_text='', choices=''):
try:
d = locals()
user_selection = subprocess.check_output(['osascript', '-l', 'JavaScript', '-e', '''
const app = Application.currentApplication()
app.includeStandardAdditions = true
var choices = {choices}
const response = app.chooseFromList(choices, {{
withPrompt: '{prompt_text}',
defaultItems: [choices[0]]
}})
response
'''.format(**d)]).decode('ascii').rstrip()
if user_selection == 'false':
sys.exit()
return user_selection
except subprocess.CalledProcessError:
pass
# Display a macOS specific notification
def notify(text, title, subtitle, sound='Glass'):
cmd = 'osascript -e \'display notification "{}" with title "{}" subtitle "{}" sound name "{}"\''
os.system(cmd.format(text, title, subtitle, sound))
# ---------------------------------------------------------------------------------------------------------------------
# Methods to read, write, remove data from the hidden .db file --------------------------------------------------------
def read_data_file(data_file):
with open(data_file, 'r') as f:
content = f.readlines()
f.close()
content = [x.strip() for x in content]
return content
def write_data_file(data_file, imit_type, symbol, price):
with open(data_file, 'a') as f:
f.write(limit_type + ' ' + symbol + ' ' + price + '\n')
f.close()
def remove_line_from_data_file(data_file, line_to_be_removed):
with open(data_file, 'r') as f:
content = f.readlines()
with open(data_file, 'w') as f:
for line in content:
if line.strip('\n') != line_to_be_removed:
f.write(line)
f.close()
# ---------------------------------------------------------------------------------------------------------------------
# Curl the yahoo api for data
def get_stock_data(symbol):
# Building the curl command as a string
library = 'curl --silent '
api = 'https://query1.finance.yahoo.com/v7/finance/quote?'
fields = [
'symbol',
'marketState',
'regularMarketTime',
'regularMarketPrice',
'regularMarketChangePercent',
'fullExchangeName',
'currency',
'navPrice',
'regularMarketPreviousClose',
'regularMarketOpen',
'bid',
'ask',
'regularMarketDayRange',
'fiftyTwoWeekRange',
'fiftyDayAverage',
'twoHundredDayAverage',
'shortName',
'fiftyDayAverageChangePercent',
'twoHundredDayAverageChangePercent'
]
fields_string = 'fields=' + ','.join(fields)
cmd = library + "'" + api + fields_string + '&symbols=' + symbol + "'"
# Popen to run the curl command and retrieve the output
output = os.popen(cmd).read()
# Jsonify the output from the curl command
json_output = json.loads(output)
# Check if a valid symbol was used
try:
stock_data = json_output['quoteResponse']['result'][0]
except IndexError:
alert('Error', 'Invalid stock symbol: ' + symbol)
sys.exit()
return stock_data
# Check a given stock symbol against the price limit list
def check_price_limits(symbol_to_be_checked, current_price, price_limit_list, data_file):
for limit_entry in price_limit_list:
if symbol_to_be_checked in limit_entry:
# Get the limit price, limits are saved in the format: TYPE SYMBOL PRICE
limit_price = float(limit_entry.split()[2])
notification_text = 'Current price is: ' + str(current_price)
notification_title = 'Price Alarm'
# Notify user if current price is lower than the BUY limit, then remove the limit from list
if 'BUY' in limit_entry and current_price < limit_price:
notification_subtitle = 'BUY Limit: ' + str(limit_price)
notify(notification_text, notification_title, notification_subtitle)
remove_line_from_data_file(data_file, limit_entry)
# Notify user if current price is higher than the SELL limit, then remove the limit from list
if 'SELL' in limit_entry and current_price > limit_price:
notification_subtitle = 'SELL Limit: ' + str(limit_price)
notify(notification_text, notification_title, notification_subtitle)
remove_line_from_data_file(data_file, limit_entry)
# Print the indices information in the menu bar
def print_index(index1, name1, index2, name2, index3, name3, index4, name4, index5, name5):
market_state1 = index1['marketState']
current_price1 = index1['regularMarketPrice']
change1 = index1['regularMarketChangePercent']
# Setting color1 and emojis depending on the market state and the market change1
if market_state1 != 'REGULAR':
# Set color1 for positive and negative values
color1 = ''
if change1 > 0:
color1 = GREEN
if change1 < 0:
color1 = RED
# Set change1 with a moon emoji for closed markets
colored_change1 = color1 + '{:.2f} '.format(current_price1) + RESET
if market_state1 == 'REGULAR':
# Set color1 for positive and negative values
color1 = ''
if change1 > 0:
color1 = GREEN
if change1 < 0:
color1 = RED
# Format change1 to decimal with a precision of two and reset ansi color1 at the end
colored_change1 = color1 + '{:.2f} '.format(current_price1) + RESET
market_state2 = index2['marketState']
current_price2 = index2['regularMarketPrice']
change2 = index2['regularMarketChangePercent']
# Setting color2 and emojis depending on the market state and the market change2
if market_state2 != 'REGULAR':
# Set color2 for positive and negative values
color2 = ''
if change2 > 0:
color2 = GREEN
if change2 < 0:
color2 = RED
# Set change2 with a moon emoji for closed markets
colored_change2 = color2 + '({:.2f}'.format(change2) + '%)' + RESET
if market_state2 == 'REGULAR':
# Set color2 for positive and negative values
color2 = ''
if change2 > 0:
color2 = GREEN
if change2 < 0:
color2 = RED
# Format change2 to decimal with a precision of two and reset ansi color2 at the end
colored_change2 = color2 + '({:.2f}'.format(change2) + '%)' + RESET
market_state3 = index3['marketState']
current_price3 = index3['regularMarketPrice']
change3 = index3['regularMarketChangePercent']
# Setting color3 and emojis depending on the market state and the market change3
if market_state3 != 'REGULAR':
# Set color3 for positive and negative values
color3 = ''
if change3 > 0:
color3 = GREEN
if change3 < 0:
color3 = RED
# Set change3 with a moon emoji for closed markets
colored_change3 = color3 + '({:.2f}'.format(change3) + '%)' + RESET
if market_state3 == 'REGULAR':
# Set color3 for positive and negative values
color3 = ''
if change3 > 0:
color3 = GREEN
if change3 < 0:
color3 = RED
# Format change3 to decimal with a precision of two and reset ansi color3 at the end
colored_change3 = color3 + '({:.2f}'.format(change3) + '%)' + RESET
market_state4 = index4['marketState']
current_price4 = index4['regularMarketPrice']
change4 = index4['regularMarketChangePercent']
# Setting color4 and emojis depending on the market state and the market change4
if market_state4 != 'REGULAR':
# Set color4 for positive and negative values
color4 = ''
if change4 > 0:
color4 = GREEN
if change4 < 0:
color4 = RED
# Set change4 with a moon emoji for closed markets
colored_change4 = color4 + '({:.2f}'.format(change4) + '%)' + RESET
if market_state4 == 'REGULAR':
# Set color4 for positive and negative values
color4 = ''
if change4 > 0:
color4 = GREEN
if change4 < 0:
color4 = RED
# Format change4 to decimal with a precision of two and reset ansi color4 at the end
colored_change4 = color4 + '({:.2f}'.format(change4) + '%)' + RESET
market_state5 = index5['marketState']
current_price5 = index5['regularMarketPrice']
change5 = index5['regularMarketChangePercent']
# Setting color4 and emojis depending on the market state and the market change4
if market_state5 != 'REGULAR':
# Set color4 for positive and negative values
color5 = ''
if change5 > 0:
color5 = GREEN
if change5 < 0:
color5 = RED
# Set change4 with a moon emoji for closed markets
colored_change5 = color5 + '({:.2f}'.format(change5) + '%)' + RESET
if market_state5 == 'REGULAR':
# Set color4 for positive and negative values
color5 = ''
if change5 > 0:
color5 = GREEN
if change5 < 0:
color5 = RED
# Format change to decimal with a precision of two and reset ansi color4 at the end
colored_change5 = color5 + '({:.2f}'.format(change5) + '%)' + RESET
# Set color3FP for positive and negative values
color3FP = ''
if (change2*WTSWRD + change3*WTWSML + change4*WTEIMI) > 0:
color3FP = GREEN
if (change2*WTSWRD + change3*WTWSML + change4*WTEIMI) < 0:
color3FP = RED
threeFundPortfolioName = '3FP'
threeFundPortfolioChange = color3FP + '({:.2f}'.format((change2*WTSWRD + change3*WTWSML + change4*WTEIMI)) + '%)' + RESET
# Print the index info only to the menu bar
TREEFPPRICE = "$" + str(round((index2['regularMarketPrice'])/SWRDSTART*WTSWRD*100 + index3['regularMarketPrice']/WSMLSTART*WTWSML*100 + index4['regularMarketPrice']/EIMISTART*WTEIMI*100,2))
# THREEFPAUM = "$" + str(round((index2['totalAssets'] + index3['totalAssets'] + index4['totalAssets'])/1000000000,1)) + "B"
IMIDPRICE = "$" + str(round((index5['regularMarketPrice'])/IMIDSTART*100,2))
# IMIDAUM = "$" + str(round((index5['totalAssets'])/1000000,1)) + "M"
toprint = name1 + " " + colored_change1 + " " + threeFundPortfolioName + " " + TREEFPPRICE + " " + threeFundPortfolioChange + " " + THREEFPHLD + " " + THREEFPHLDPct + " " + THREEFPAUM + " " + THREEFPAUMPct
otherthingtoprint = name5 + " " + IMIDPRICE + " " + colored_change5 + " " + IMIDHLD + " " + IMIDHLDPct + " " + IMIDAUM + " " + IMIDAUMPct
print("{} {}| dropdown=false".format(toprint,otherthingtoprint), sep=' ')
# Print the stock info in the dropdown menu with additional info in the submenu
def print_stock(s):
if s['symbol'] == symbols[0]:
print("Symbol Price %Δ Nav %Δ")
market_state = s['marketState']
change = s['regularMarketChangePercent']
# Setting color and emojis depending on the market state and the market change
if market_state != 'REGULAR':
# Set color for positive and negative values
color = ''
if change > 0:
color = GREEN
if change < 0:
color = RED
market = 'CLOSED'
# Set change with a moon emoji for closed markets
colored_change = color + '{:.2f}'.format(change) + '%' + RESET
if market_state == 'REGULAR':
# Set color for positive and negative values
color = ''
market = 'OPEN'
if change > 0:
color = GREEN
if change < 0:
color = RED
# Format change to decimal with a precision of two and reset ansi color at the end
change_in_percent = color + '{:.2f}'.format(change) + '%' + RESET
colored_change = change_in_percent
# Remove appending stock exchange symbol for foreign exchanges, e.g. Apple stock symbol in Frankfurt: APC.F -> APC
symbol = s['symbol'].split('.')[0]
# Convert epoch to human readable time HH:MM:SS
time = datetime.fromtimestamp(s['regularMarketTime']).strftime('%X')
# Convert float values to decimals with a precision of two
fifty_day = '{:.2f}'.format(s['fiftyDayAverage'])
two_hundred_day = '{:.2f}'.format(s['twoHundredDayAverage'])
fifty_day_change = '(' + '{:.2f}'.format(s['fiftyDayAverageChangePercent'] * 100) + '%)'
two_hundred_day_change = '(' + '{:.2f}'.format(s['twoHundredDayAverageChangePercent'] * 100) + '%)'
# Print the stock info seen in the dropdown menu
if s['symbol'] == "SWRD.L":
stock_info = '{:<8} {:<6.2f} {:<15} {:<5.2f} {:<10}' + FONT
NormSWRD = round(s['regularMarketPrice']/(SWRDSTART/100),2)
NavSWRD = round(float(s['navPrice'])/SWRDNAVSTART*100,2)
NavSWRDPct = round((NavSWRD-SWRDNAVLAST)/SWRDNAVLAST*100,2)
if NavSWRDPct < 0:
color = RED
if NavSWRDPct > 0:
color = GREEN
NavSWRDPct = color + "(" + '{:.2f}'.format(NavSWRDPct) + "%)" + RESET
print(stock_info.format(symbol, (NormSWRD), colored_change, (NavSWRD), (NavSWRDPct)))
elif s['symbol'] == "WSML.L":
stock_info = '{:<8} {:<6.2f} {:<15} {:<5.2f} {:<10}' + FONT
NormWSML = round(s['regularMarketPrice']/(WSMLSTART/100),2)
NavWSML = round(float(s['navPrice'])/WSMLNAVSTART*100,2)
NavWSMLPct = round((NavWSML-WSMLNAVLAST)/WSMLNAVLAST*100,2)
if NavWSMLPct < 0:
color = RED
if NavWSMLPct > 0:
color = GREEN
NavWSMLPct = color + "(" + '{:.2f}'.format(NavWSMLPct) + "%)" + RESET
print(stock_info.format(symbol, (NormWSML), colored_change, (NavWSML), (NavWSMLPct)))
elif s['symbol'] == "EIMI.L":
stock_info = '{:<8} {:<6.2f} {:<15} {:<5.2f} {:<10}' + FONT
NormEIMI = round(s['regularMarketPrice']/(EIMISTART/100),2)
NavEIMI = round(float(s['navPrice'])/EIMINAVSTART*100,2)
NavEIMIPct = round((NavEIMI-EIMINAVLAST)/EIMINAVLAST*100,2)
if NavEIMIPct < 0:
color = RED
if NavEIMIPct > 0:
color = GREEN
NavEIMIPct = color + "(" + '{:.2f}'.format(NavEIMIPct) + "%)" + RESET
print(stock_info.format(symbol, (NormEIMI), colored_change, (NavEIMI), (NavEIMIPct)))
elif s['symbol'] == "IMID.L":
stock_info = '{:<8} {:<6.2f} {:<15} {:<5.2f} {:<10}' + FONT
NormIMID = round(s['regularMarketPrice']/(IMIDSTART/100),2)
NavIMID = round(float(s['navPrice'])/IMIDNAVSTART*100,2)
NavIMIDPct = round((NavIMID-IMIDNAVLAST)/IMIDNAVLAST*100,2)
if NavIMIDPct < 0:
color = RED
if NavIMIDPct > 0:
color = GREEN
NavIMIDPct = color + "(" + '{:.2f}'.format(NavIMIDPct) + "%)" + RESET
print(stock_info.format(symbol, NormIMID, colored_change, NavIMID, NavIMIDPct))
else:
stock_info = '{:<5} {:<6.2f} {:<15}' + FONT
print(stock_info.format(symbol, float(s['regularMarketPrice']), colored_change))
# Print additional stock info in the submenu
stock_submenu = '{:<17} {:<17}' + FONT
print('--' + s['shortName'] + FONT)
print('--' + s['fullExchangeName'] + ' - Currency in ' + s['currency'] + FONT)
print('--' + time + ' - Market is ' + market + FONT)
print('-----')
print(stock_submenu.format('--Previous Close:', s['regularMarketPreviousClose']))
print(stock_submenu.format('--Open:', s['regularMarketOpen']))
print(stock_submenu.format('--Bid:', s['bid']))
print(stock_submenu.format('--Ask:', s['ask']))
print(stock_submenu.format("--Day's Range:", s['regularMarketDayRange']))
print(stock_submenu.format('--52 Week Range:', s['fiftyTwoWeekRange']))
print(stock_submenu.format('--50 MA:', fifty_day + ' ' + fifty_day_change))
print(stock_submenu.format('--200 MA:', two_hundred_day + ' ' + two_hundred_day_change))
# Print the price limits in the dropdown menu
def print_price_limits(price_limit_list):
PARAMETERS = FONT + " refresh=true terminal='false' bash='" + __file__ + "'"
print('---')
print('Price Limits' + FONT)
# Print available price limits in the submenu
for limit_entry in price_limit_list:
# Split the limit entry, limits are saved in the format: TYPE SYMBOL PRICE
limit_type = limit_entry.split()[0]
symbol = limit_entry.split()[1]
limit_price = limit_entry.split()[2]
price_limit_submenu = '{:<6} {:<4} {:<10}'
# Print the price limit data into the submenu
# onClick will rerun this script with parameters 'remove' and the {limit_entry} to remove clicked the limit
print(price_limit_submenu.format('--' + limit_type, symbol, limit_price + PARAMETERS + " param1='remove' param2='" + limit_entry + "'"))
print('-----')
print('--To remove a limit, click on it.' + FONT)
# Print the clickable fields to set new limits or clear all price limits
# onClick will rerun this script with parameters 'set' to set a new limit
print('Set new Price Limit...' + PARAMETERS + " param1='set'")
# onClick will rerun this script with parameters 'clear' to clear the hidden .db file
print('Clear all Price Limits...' + PARAMETERS + " param1='clear'")
if __name__ == '__main__':
data_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), '.' + os.path.basename(__file__) + '.db')
# Normal execution by BitBar without any parameters
if len(sys.argv) == 1:
stocks = []
# Check if hidden .db file exists
try:
price_limit_list = read_data_file(data_file)
except FileNotFoundError:
price_limit_list = []
# Print the menu bar information
indexsymbol = list(indices_dict.keys())
indexnames = list(indices_dict.values())
print_index(
get_stock_data(indexsymbol[0]), indexnames[0],
get_stock_data(indexsymbol[1]), indexnames[1],
get_stock_data(indexsymbol[2]), indexnames[2],
get_stock_data(indexsymbol[3]), indexnames[3],
get_stock_data(indexsymbol[4]), indexnames[4])
# for symbol, name in indices_dict.items():
# # For each symbol: curl the data, then print it
# index = get_stock_data(symbol)
# print_index(index, name)
# For each symbol: curl the data, check against the .db file for limits
for symbol in symbols:
stock = get_stock_data(symbol)
stocks.append(stock)
check_price_limits(symbol, stock['regularMarketPrice'], price_limit_list, data_file)
# Set order of stocks
if sort_by == 'name':
stocks = sorted(stocks, key=lambda k: k['shortName'])
if sort_by == 'market_change_winners':
stocks = sorted(stocks, key=lambda k: k['regularMarketChangePercent'], reverse=True)
if sort_by == 'market_change_losers':
stocks = sorted(stocks, key=lambda k: k['regularMarketChangePercent'])
if sort_by == 'market_change_volatility':
stocks = sorted(stocks, key=lambda k: abs(k['regularMarketChangePercent']), reverse=True)
# Print the stock information inside the dropdown menu
print('---')
for stock in stocks:
print_stock(stock)
# Print the price limit section inside the dropdown
print_price_limits(price_limit_list)
# Script execution with parameter 'set' to set new price limits
if len(sys.argv) == 2 and sys.argv[1] == 'set':
# Run this until user does not want to continue
while True:
# Get the user selection of whether he wants to set 'BUY' or 'SELL'
limit_type_prompt = 'Select the type of your limit: BUY (SELL) limits are triggered, when the price is lower (higher) than the limit.'
limit_type_choices = '["BUY", "SELL"]'
limit_type = prompt_selection(limit_type_prompt, limit_type_choices)
# Get the user selection of all tracked symbols
symbol = prompt_selection('Select stock symbol:', symbols)
# Get the user input for a price limit, info message includes the current market price
price = prompt('Current price of ' + symbol + ' is ' + str(get_stock_data(symbol)['regularMarketPrice']) + '. Enter a value for your price limit.')
# Check if the user input are decimals with a precision of two
if not re.match(r'^\d+(\.\d{1,2})?$', price):
# Alert the user on invalid value and stop the script
alert('Error', 'You entered an invalid value: ' + price + ' - valid values are decimals with a precision of 2, e.g 25.70!')
sys.exit()
# Write the limit to the hidden .db file
write_data_file(data_file, limit_type, symbol, price)
# Ask user if he wants to add another limit
add_another_limit = alert('Question', 'Do you want to add another price limit?', ['No', 'Yes'])
# If the user clicked the 'No' button, stop the script
if add_another_limit is None:
sys.exit()
# Script execution with parameter 'clear' to clear the .db file
if len(sys.argv) == 2 and sys.argv[1] == 'clear':
# Ask for user confirmation
warning = alert('Warning', 'This will clear your price limits! Do you want to continue?')
if warning is None:
sys.exit()
# Clear the file
open(data_file, 'w').close()
# Script execution with the parameters 'remove' and the line to be removed
if len(sys.argv) == 3 and sys.argv[1] == 'remove':
limit_to_be_removed = sys.argv[2]
remove_line_from_data_file(data_file, limit_to_be_removed)
Is it possible to have xbar print two rows such that the first row is on top of the either?
I'm using a modified version of @longpdo's Yahoo Stock Ticker but when I try to accomplish this, by adding "\n" to the function, it indeed prints a new line, but it cycles through line 1 and line 2 instead of printing them at the same time on top of each other.