Compare commits

..
10 Commits
Author SHA1 Message Date
cmallwitz 520d2441cb Refresh Yahoo cookies 2022-06-02 21:19:49 +01:00
cmallwitz 8b370dfc73 Updated version 2022-01-07 21:42:07 +00:00
cmallwitz af82f16740 Updated Yahoo EU consent 2022-01-07 21:33:55 +00:00
cmallwitz 4e626c272e Add shares outstanding and free float information 2021-09-28 20:28:00 +01:00
cmallwitz e22dc7448f Fix SSL issue on MacOS 2021-09-11 23:46:42 +01:00
cmallwitz ab34a246e3 Update Readme 2021-08-19 19:50:41 +01:00
cmallwitz aa20845ae4 Fix yield for US mutuals and EFTs 2021-06-08 17:29:47 +01:00
cmallwitz 7fccc7c0ad Fix issue with FT avg vol 2021-05-19 21:44:06 +01:00
cmallwitz 7ddfbd084f Fix for FT symbols containing space 2021-05-15 18:31:10 +01:00
cmallwitz e22d4f46b8 Package all Python dependencies and disable Google as source 2021-04-03 11:23:45 +01:00
16 changed files with 336 additions and 193 deletions
+66 -69
View File
@@ -1,47 +1,30 @@
# Financials-Extension
This is a Python based extension for LibreOffice Calc to make stock market, index and FX data available in Calc
spreadsheets - currently supporting Yahoo's, Financial Times' and Google's (without FX data) finance websites
using old-fashioned web scraping.
spreadsheets - currently supporting Yahoo's and Financial Times' finance websites using old-fashioned web scraping.
Starting with version 3 of the extension, Google was dropped as source as its finance website was changed dramatically
requiring a complete re-work of the logic to pull data of it. No one seems to have complained, so I guess not a lot of
people rely on it. If there is popular demand, I can take a look at adding it back. Alternatively I'm open for
suggestions to add another provider with global exchange and asset class coverage.
Additionally, starting with version 3 of the extension, it contains all non-standard Python packages it uses
(dateutil, pytz, pyparsing). Consequently, it is no longer necessary to install anything to your system.
### Feedback requested:
Please provide feedback about using the extension here: https://github.com/cmallwitz/Financials-Extension/issues/10
### System requirements:
This extension requires the following Python 3 packages (on top of standard libs): dateutil, pytz, pyparsing. These are
from within LibreOffice, so if your LibreOffice comes with it's own Python runtime (e.g. Windows) they need to be
installed there instead somewhere else in the system.
On Ubuntu 18 and 20, dateutil and pytz may already be installed, but you can get all you need by running
- sudo apt-get install python3-dateutil python3-tz python3-pyparsing
On Debian 10, the following steps have worked for me (as root)
- apt install python3-pip
- pip3 install python-dateutil pytz pyparsing
Users on Windows 10 have reported this to work - as LibreOffice on Windows ships with its own, but minimal
Python runtime a few more steps are required
- Download the script https://bootstrap.pypa.io/get-pip.py to your computer
- Start a Command Prompt (CMD) as Administrator
- on this command prompt run (change path as required)
"c:\Program Files\LibreOffice\program\python.exe" c:\temp\get-pip.py
- and then
"c:\Program Files\LibreOffice\program\python.exe" -m pip install python-dateutil pytz pyparsing
Please provide feedback about using the extension [here](https://github.com/cmallwitz/Financials-Extension/issues/10)
### Usage:
Under 'Releases' on GitHub there is downloadable **Financials-Extension.oxt** file - load it into Calc
Under 'Releases' on GitHub [there](https://github.com/cmallwitz/Financials-Extension/releases) is a downloadable **Financials-Extension.oxt** file - load it into Calc
under menu item: Tools, Extension Manager...
Please make sure, not to rename the OXT file when downloading and before installing: LO will mess up the installation otherwise and the extension won't work.
Getting data should be a simple as having this in a cell:
- `=GETREALTIME("IBM",21,"YAHOO")`
- `=GETREALTIME("NYSE:IBM",21,"GOOGLE")`
- `=GETREALTIME("IBM:NYQ",21,"FT")`
- `=GETREALTIME("EURUSD","LAST_PRICE","FT")`
- `=GETHISTORIC("IBM",90,"2020-12-01","YAHOO")`
Codes 21 and 90 stand for "last price" and "close" (see below), respectively.
@@ -56,6 +39,15 @@ assets in other regions, drop me a line (best to include full URLs and possibly
site for comparison). While data for last price is most likely consistent across sites, they may differ for other data
points.
List of example URLs for checking symbols. You can start from these and search for other symbols. If you think some data
is available on the website but not from the extension, a good place to start when raising an issue is to include a similar
URL to compare results.
|Website|Symbol|Example URL for Vodafone Group Plc UK |
| :--- | :--- | :--- |
|YAHOO|VOD.L|https://finance.yahoo.com/quote/VOD.L|
|FT|VOD:LSE|https://markets.ft.com/data/equities/tearsheet/summary?s=VOD:LSE|
### LibreOffice: using , (comma) vs ; (semicolon) to separate arguments in formula
There is a setting in "Tools" / "Options..." / "LibreOffice Calc" / "Formula" called "Functions". Here the user can specify the character used to separate arguments in formula.
@@ -68,43 +60,45 @@ Depending on your system's language and default LibreOffice settings, you maybe
You can either specify numbers or names (lower or upper case) - not all bits are available from all sources and they are not necessarily consistent across sources either.
|Name|Code|YAHOO|FT|GOOGLE|YAHOO (historic)|Notes|
| :--- | :--- | :---: | :---: | :---: | :---: | :---: |
|PREV_CLOSE|5|Yes|Yes|Yes|||
|OPEN|6|Yes|Yes|Yes|Yes||
|CHANGE|7|Yes|Yes|Yes|||
|LAST_PRICE_DATE|8|Yes|Yes|Yes| |yyyy-mm-dd|
|LAST_PRICE_TIME|10|Yes|Yes|Yes|||
|CHANGE_IN_PERCENT|11|Yes|Yes|Yes|||
|LOW|14|Yes|Yes|Yes|Yes||
|HIGH|16|Yes|Yes|Yes|Yes||
|LAST_PRICE|21|Yes|Yes|Yes|||
|BID|22|Yes|Yes|No|||
|ASK|25|Yes|Yes|No|||
|HIGH_52_WEEK|24|Yes|Yes|Yes|||
|LOW_52_WEEK|26|Yes|Yes|Yes|||
|MARKET_CAP|27|Yes|Yes|Yes|||
|BIDSIZE|30|Yes|Yes|No|||
|ASKSIZE|31|Yes|Yes|No|||
|VOLUME|35|Yes|Yes|No|Yes||
|AVG_DAILY_VOL_3MONTH|39|Yes|Yes|No|||
|BETA|67|Yes|Yes|No|||
|EPS|68|Yes|Yes|No|||
|PE_RATIO|69|Yes|Yes|No|||
|DIV|70|Yes|Yes|No|||
|DIV_YIELD|71|Yes|Yes|No|||
|EX_DIV_DATE|72|Yes|Yes|No| |yyyy-mm-dd|
|PAYOUT_RATIO|73|Yes|No|No|||
|EXPIRY_DATE|74|Yes (on options)|No|No| |yyyy-mm-dd|
|CLOSE|90|No|No|No|Yes||
|ADJ_CLOSE|91|No|No|No|Yes||
|SECTOR|98|Yes|Yes|No|||
|INDUSTRY|99|Yes|Yes|No|||
|TICKER|101|Yes|Yes|Yes|||
|EXCHANGE|102|Yes|No|Yes|||
|CURRENCY|103|Yes|Yes|Yes|||
|NAME|104|Yes|Yes|Yes|||
|TIMEZONE|105|Yes|Yes|Yes|||
|Name|Code|YAHOO|FT|YAHOO (historic)|Notes|
| :--- | :--- | :---: | :---: | :---: | :---: |
|PREV_CLOSE|5|Yes|Yes|||
|OPEN|6|Yes|Yes|Yes||
|CHANGE|7|Yes|Yes|||
|LAST_PRICE_DATE|8|Yes|Yes| |yyyy-mm-dd|
|LAST_PRICE_TIME|10|Yes|Yes|||
|CHANGE_IN_PERCENT|11|Yes|Yes|||
|LOW|14|Yes|Yes|Yes||
|HIGH|16|Yes|Yes|Yes||
|LAST_PRICE|21|Yes|Yes|||
|BID|22|Yes|Yes|||
|ASK|25|Yes|Yes|||
|HIGH_52_WEEK|24|Yes|Yes|||
|LOW_52_WEEK|26|Yes|Yes|||
|MARKET_CAP|27|Yes|Yes|||
|BIDSIZE|30|Yes|Yes|||
|ASKSIZE|31|Yes|Yes|||
|VOLUME|35|Yes|Yes|Yes||
|AVG_DAILY_VOL_3MONTH|39|Yes|Yes|||
|BETA|67|Yes|Yes|||
|EPS|68|Yes|Yes|||
|PE_RATIO|69|Yes|Yes|||
|DIV|70|Yes|Yes|||
|DIV_YIELD|71|Yes|Yes|||
|EX_DIV_DATE|72|Yes|Yes| |yyyy-mm-dd|
|PAYOUT_RATIO|73|Yes|No|||
|EXPIRY_DATE|74|Yes (on options)|No| |yyyy-mm-dd|
|SHARES_OUT|75|Yes|Yes| ||
|FREE_FLOAT|76|Yes|Yes| ||
|CLOSE|90|No|No|Yes||
|ADJ_CLOSE|91|No|No|Yes||
|SECTOR|98|Yes|Yes|||
|INDUSTRY|99|Yes|Yes|||
|TICKER|101|Yes|Yes|||
|EXCHANGE|102|Yes|No|||
|CURRENCY|103|Yes|Yes|||
|NAME|104|Yes|Yes|||
|TIMEZONE|105|Yes|Yes|||
### Dealing with missing data:
@@ -138,11 +132,14 @@ python3 -m unittest discover src
./compile.sh
### Tested with:
- Windows 10 / LibreOffice Calc 7.1.2.2 / Python 3.8.8
- Ubuntu 20.04 / LibreOffice Calc 6.4.3.2 / Python 3.8.2
- MacOS 10.15.7 / LibeOffice Calc 7.2.0.4 / Python 3.8.10
(Previous versions)
- Debian 10.3 / LibreOffice Calc 6.1.5.2 / Python 3.7.3
- Ubuntu 20.10 / LibreOffice Calc 7.0.3.1 / Python 3.8.6
- Ubuntu 20.04 / LibreOffice Calc 6.4.3.2 / Python 3.8.2
- Ubuntu 19.10 / LibreOffice Calc 6.3.5.2 / Python 3.7.6
- Ubuntu 19.04 / LibreOffice Calc 6.2 / Python 3.7.3
- Ubuntu 18.04.5 / LibreOffice Calc 6 / Python 3.6.9
- Ubuntu 18.04 / LibreOffice Calc 6 / Python 3.6.7
- Ubuntu 16.04 / LibreOffice Calc 5 (previous versions)
+21
View File
@@ -38,6 +38,27 @@ cp -f "${PWD}"/src/financials_ft.py "${PWD}"/build/
cp -f "${PWD}"/src/financials_google.py "${PWD}"/build/
cp -f "${PWD}"/src/financials_yahoo.py "${PWD}"/build/
# this copies python modules dateutil, pytz, pyparsing to extension so it doesn't have to be installed by user
TMPFILE=`mktemp`
wget "https://files.pythonhosted.org/packages/d4/70/d60450c3dd48ef87586924207ae8907090de0b306af2bce5d134d78615cb/python_dateutil-2.8.1-py2.py3-none-any.whl" -O $TMPFILE
unzip $TMPFILE dateutil/\* -d "${PWD}"/build/
rm $TMPFILE
wget "https://files.pythonhosted.org/packages/70/94/784178ca5dd892a98f113cdd923372024dc04b8d40abe77ca76b5fb90ca6/pytz-2021.1-py2.py3-none-any.whl" -O $TMPFILE
unzip $TMPFILE pytz/\* -d "${PWD}"/build/
rm $TMPFILE
wget "https://files.pythonhosted.org/packages/8a/bb/488841f56197b13700afd5658fc279a2025a39e22449b7cf29864669b15d/pyparsing-2.4.7-py2.py3-none-any.whl" -O $TMPFILE
unzip $TMPFILE pyparsing.py -d "${PWD}"/build/
rm $TMPFILE
# Windows LibreOffice 7.1 Python is missing this...
wget "https://files.pythonhosted.org/packages/ee/ff/48bde5c0f013094d729fe4b0316ba2a24774b3ff1c52d924a8a4cb04078a/six-1.15.0-py2.py3-none-any.whl" -O $TMPFILE
unzip $TMPFILE six.py -d "${PWD}"/build/
rm $TMPFILE
echo "Package into oxt file..."
pushd "${PWD}"/build/
zip -r "${PWD}"/Financials-Extension.zip ./*
BIN
View File
Binary file not shown.
+16 -12
View File
@@ -48,18 +48,14 @@ class BaseClient:
os.makedirs(self.basedir, exist_ok=True)
user_agents = [
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0',
'Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:76.0) Gecko/20100101 Firefox/76.0',
'Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:77.0) Gecko/20100101 Firefox/77.0',
'Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:79.0) Gecko/20100101 Firefox/79.0',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:81.0) Gecko/20100101 Firefox/81.0',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:82.0) Gecko/20100101 Firefox/82.0',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:90.0) Gecko/20100101 Firefox/90.0',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:92.0) Gecko/20100101 Firefox/92.0',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:93.0) Gecko/20100101 Firefox/93.0',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:94.0) Gecko/20100101 Firefox/94.0',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:97.0) Gecko/20100101 Firefox/97.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36',
@@ -195,6 +191,7 @@ class BaseClient:
tick[Datacode.EXCHANGE] = None
tick[Datacode.EXPIRY_DATE] = None
tick[Datacode.EX_DIV_DATE] = None
tick[Datacode.FREE_FLOAT] = None
tick[Datacode.HIGH] = None
tick[Datacode.HIGH_52_WEEK] = None
tick[Datacode.INDUSTRY] = None
@@ -210,6 +207,7 @@ class BaseClient:
tick[Datacode.PE_RATIO] = None
tick[Datacode.PREV_CLOSE] = None
tick[Datacode.SECTOR] = None
tick[Datacode.SHARES_OUT] = None
tick[Datacode.TICKER] = None
tick[Datacode.TIMEZONE] = None
tick[Datacode.VOLUME] = None
@@ -320,6 +318,12 @@ class BaseClient:
else:
return data[Datacode.EXPIRY_DATE]
elif datacode == Datacode.FREE_FLOAT.value and Datacode.FREE_FLOAT in data:
return data[Datacode.FREE_FLOAT]
elif datacode == Datacode.SHARES_OUT.value and Datacode.SHARES_OUT in data:
return data[Datacode.SHARES_OUT]
elif datacode == Datacode.CLOSE.value and Datacode.CLOSE in data:
return data[Datacode.CLOSE]
+2
View File
@@ -44,6 +44,8 @@ class Datacode(Enum):
EX_DIV_DATE = 72
PAYOUT_RATIO = 73
EXPIRY_DATE = 74
SHARES_OUT = 75
FREE_FLOAT = 76
CLOSE = 90
ADJ_CLOSE = 91
+19
View File
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<description xmlns="http://openoffice.org/extensions/description/2006"
xmlns:d="http://openoffice.org/extensions/description/2006"
xmlns:l="http://libreoffice.org/extensions/description/2011"
xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- only used for testing -->
<dependencies>
<l:LibreOffice-minimal-version value="5.0" d:name="LibreOffice 5.0" />
</dependencies>
<identifier value="com.financials.getinfo" />
<version value="3.0.0" />
<display-name><name lang="en">Financial Market Extension</name></display-name>
<publisher><name xlink:href="https://github.com/cmallwitz/Financials-Extension" lang="en">The Publisher</name></publisher>
<extension-description><src xlink:href="description-en-US.txt" lang="en" /></extension-description>
</description>
+36 -8
View File
@@ -14,10 +14,12 @@ import logging
import os
import pathlib
import platform
import ssl
import sys
import time
from functools import wraps
from importlib import util
import xml.etree.ElementTree as ET
import unohelper
from com.financials.getinfo import Financials
@@ -47,16 +49,28 @@ if dateutil_missing or pyparsing_missing or pytz_missing:
raise Exception("THIS EXTENSION NEEDS THE FOLLOWING PYTHON 3 LIBRARIES INSTALLED:" + msg)
import dateutil.parser
import pytz
import pyparsing
import six
from datacode import Datacode
import financials_google as google
import financials_yahoo as yahoo
import financials_ft as ft
from version import version
implementation_name = "com.financials.getinfo.python.FinancialsImpl" # as defined in Financials.xcu
implementation_services = ("com.sun.star.sheet.AddIn",)
# Disabling SSL certificate validation as Python setup on MacOS seems to be broken
# Only reading public data so this should be safe
try:
_create_unverified_https_context = ssl._create_unverified_context
except AttributeError:
pass
else:
ssl._create_default_https_context = _create_unverified_https_context
def profile(fn):
@wraps(fn)
@@ -87,7 +101,7 @@ class FinancialsImpl(unohelper.Base, Financials):
@profile
def getRealtime(self, ticker, datacode=None, source=None):
if ticker == 'SUPPORT':
if ticker == 'SUPPORT' or ticker == 'support':
return self.support(datacode)
try:
@@ -120,9 +134,10 @@ class FinancialsImpl(unohelper.Base, Financials):
ticker = str(ticker).strip()
source = str(source).upper()
if source == 'GOOGLE':
s = self.google.getRealtime(ticker, datacode)
elif source == 'YAHOO':
# if source == 'GOOGLE':
# s = self.google.getRealtime(ticker, datacode)
# el
if source == 'YAHOO':
s = self.yahoo.getRealtime(ticker, datacode)
elif source == 'FT':
s = self.ft.getRealtime(ticker, datacode)
@@ -219,7 +234,14 @@ class FinancialsImpl(unohelper.Base, Financials):
@profile
def support(self, datacode):
s = 'ctx={}\nid(self)={}\nversion={}\nfile={}\ncwd={}\nhome={}\nuname={}\npid={}\nsys.executable={}\nsys.version={}\nlocale={}\ndefaultlocale={}'.format(
version = '0.0.0'
description_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'description.xml')
for e in ET.parse(description_file).getroot():
if e.tag.endswith('version'):
version = e.attrib['value']
s = 'ctx={}\nid(self)={}\nversion={}\nfile={}\ncwd={}\nhome={}\nuname={}\npid={}\nsys.executable={}\nsys.version={}\nsys.path={}\nlocale={}\ndefaultlocale={}\ndateutil={}\npytz={}\npyparsing={}\nsix={}'.format(
self.ctx,
id(self),
version,
@@ -230,8 +252,14 @@ class FinancialsImpl(unohelper.Base, Financials):
os.getpid(),
sys.executable,
sys.version.replace("\n", " "),
sys.path,
locale.getlocale(),
locale.getdefaultlocale())
locale.getdefaultlocale(),
dateutil.__version__,
pytz.__version__,
pyparsing.__version__,
six.__version__,
)
if datacode:
s = '{}\ntype(datacode)={}\nstr(datacode)={}'.format(
@@ -246,6 +274,6 @@ def createInstance(ctx):
return FinancialsImpl(ctx)
# pythonloader looks for a static g_ImplementationHelper variable
# python loader looks for a static g_ImplementationHelper variable
g_ImplementationHelper = unohelper.ImplementationHelper()
g_ImplementationHelper.addImplementation(createInstance, implementation_name, implementation_services, )
+18 -7
View File
@@ -12,6 +12,7 @@ import logging
import os
import re
import time
import urllib.parse
import dateutil.parser
@@ -59,9 +60,6 @@ class FT(BaseClient):
:return:
"""
# remove white space
ticker = "".join(ticker.split())
# use cached value for up to 60 seconds
if ticker in self.realtime:
tick = self.realtime[ticker]
@@ -77,7 +75,7 @@ class FT(BaseClient):
asset_class = self.guess_asset_class(ticker)
url = f'https://markets.ft.com/data/{asset_class}/tearsheet/summary?s={ticker}'
url = f'https://markets.ft.com/data/{asset_class}/tearsheet/summary?s={urllib.parse.quote_plus(ticker)}'
try:
text = self.urlopen(url, redirect=True, data=None, headers=None)
@@ -86,7 +84,8 @@ class FT(BaseClient):
return f'FT.getRealtime({ticker}, {datacode}) - urlopen endpoint: {str(e)}'
try:
with open(os.path.join(self.basedir, f'ft-{ticker}.html'), "w", encoding="utf-8") as text_file:
temp = ticker.replace(':', '_') # Windows can't have ':' in file names
with open(os.path.join(self.basedir, f'ft-{temp}.html'), "w", encoding="utf-8") as text_file:
print(f"<!-- '{self.last_url}' -->\r\n\r\n{text}", file=text_file)
except BaseException:
logger.exception("BaseException ticker=%s datacode=%s %s", ticker, datacode)
@@ -236,12 +235,24 @@ class FT(BaseClient):
tick[Datacode.PREV_CLOSE] = self.save_wrapper(
lambda: float(html.unescape(match.group(1)).replace(',', '').strip()))
r = r'<th>\s*Average volume\s*</th><td>\s*([0-9,\.btnm]+)\s*</td>'
r = r'<th>\s*Average volume\s*</th><td>\s*([0-9,\.btnmk]+)\s*</td>'
match = re.compile(r, flags=re.DOTALL).search(text, start)
if match:
tick[Datacode.AVG_DAILY_VOL_3MONTH] = self.save_wrapper(
lambda: handle_abbreviations(html.unescape(match.group(1))))
r = r'<th>\s*Shares outstanding\s*</th><td>\s*([0-9,\.btnmk]+)\s*</td>'
match = re.compile(r, flags=re.DOTALL).search(text, start)
if match:
tick[Datacode.SHARES_OUT] = self.save_wrapper(
lambda: handle_abbreviations(html.unescape(match.group(1))))
r = r'<th>\s*Free float\s*</th><td>\s*([0-9,\.btnmk]+)\s*</td>'
match = re.compile(r, flags=re.DOTALL).search(text, start)
if match:
tick[Datacode.FREE_FLOAT] = self.save_wrapper(
lambda: handle_abbreviations(html.unescape(match.group(1))))
r = r'<th>\s*P/E.*?</th><td>\s*([0-9,\.\-]+)\s*<'
match = re.compile(r, flags=re.DOTALL).search(text, start)
if match:
@@ -252,7 +263,7 @@ class FT(BaseClient):
tick[Datacode.PE_RATIO] = self.save_wrapper(
lambda: float(value))
r = r'<th>\s*Market cap\s*</th><td>\s*([0-9,\.btnm]+)\s*<'
r = r'<th>\s*Market cap\s*</th><td>\s*([0-9,\.btnmk]+)\s*<'
match = re.compile(r, flags=re.DOTALL).search(text, start)
if match:
tick[Datacode.MARKET_CAP] = self.save_wrapper(
+2 -1
View File
@@ -96,7 +96,8 @@ class Google(BaseClient):
return 'Google.getRealtime(\'{}\', {}) - urlopen: {} {}'.format(ticker, datacode, e, url)
try:
with open(os.path.join(self.basedir, 'google-{}.html'.format(ticker)), "w", encoding="utf-8") as text_file:
temp = ticker.replace(':', '_') # Windows can't have ':' in file names
with open(os.path.join(self.basedir, 'google-{}.html'.format(temp)), "w", encoding="utf-8") as text_file:
print(f"<!-- '{url}' -->\r\n\r\n{text}", file=text_file)
except BaseException as e:
logger.exception("BaseException ticker=%s datacode=%s", ticker, datacode)
+23 -12
View File
@@ -50,6 +50,13 @@ def fmt(m, key, default=0.0):
return default
def cookie(name, value):
return cookiejar.Cookie(version=0, name=name, value=value,
port=None, port_specified=False, domain=".yahoo.com", domain_specified=True,
domain_initial_dot=True, path="/", path_specified=True, secure=True, expires=None,
discard=False, comment=None, comment_url=None, rest=dict())
class Yahoo(BaseClient):
def __init__(self, ctx):
super().__init__()
@@ -111,18 +118,15 @@ class Yahoo(BaseClient):
url = 'https://finance.yahoo.com/quote/{}?p={}'.format(ticker, ticker)
cookies = [cookiejar.Cookie(version=0,
name="B",
value="er7g22lg35od5&b=3&s=8p",
port=None, port_specified=False,
domain=".yahoo.com", domain_specified=True, domain_initial_dot=True,
path="/", path_specified=True,
secure=True,
expires=None,
discard=False,
comment=None,
comment_url=None,
rest=dict())
cookies = [cookie("maex","%7B%22v2%22%3A%7B%7D%7D"),
cookie("EuConsent", "CPZ7-cAPZ7-cAAOACBENCRCoAP_AAH_AACiQIlNd_X__bX9n-_7_7ft0cY1f9_r3r-QzjgfNs-8F3L_W_L0X32E7NF36pq4KuR4ku3bBIQFtHMnUTUmxaolVrzHsak2cpyNKI7LkknsZe2dYGH9Pn9lD-YKZ7_5___f53T___9_-39z3_9f___d9_-__-vjfV599n_v9fV_7_9nf_____-_-___4IQQ_AJMNS8gC7EscGTSMIoQQIwrCQqAUAFFAMLRFYAODgp2VgEuoIWACAVARgRAgxBRgwCAAACAJCIgJACwQCIAiAQAAgARAIQAETAILACwMAgAFANCxACgAECQgyICI5TAgIkSiglsrEEoK9jTCAOssAKBRGRUACJAAASAgJCwcxwBICXCyQJMULwAw0AGAAIIlCIAMAAQRKFQAYAAgiUA"),
cookie("GUCS", "AR0nzQVM"),
cookie("GUC", "AQABBwFimlxjZUIcxQRM"),
cookie("PRF","t%3DVFIAX"),
cookie("thamba","1"),
cookie("A1", "d=AQABBNAVmWICEEFBM1xh-RmAmPpJJIsAz3YFEgABBwFcmmJlY_bPb2UB9iMAAAcIzhWZYm7SAIg&S=AQAAAucqV1HMdCsRf6key1gdaFs"),
cookie("A1S", "d=AQABBNAVmWICEEFBM1xh-RmAmPpJJIsAz3YFEgABBwFcmmJlY_bPb2UB9iMAAAcIzhWZYm7SAIg&S=AQAAAucqV1HMdCsRf6key1gdaFs&j=GDPR"),
cookie("A3", "d=AQABBNAVmWICEEFBM1xh-RmAmPpJJIsAz3YFEgABBwFcmmJlY_bPb2UB9iMAAAcIzhWZYm7SAIg&S=AQAAAucqV1HMdCsRf6key1gdaFs")
]
try:
@@ -176,6 +180,7 @@ class Yahoo(BaseClient):
price = results['price']
quoteType = results['quoteType']
summaryDetail = results['summaryDetail']
defaultKeyStatistics = results['defaultKeyStatistics'] if 'defaultKeyStatistics' in results else dict()
if not price:
return 'Could not find price for \'{}\''.format(ticker)
@@ -203,6 +208,8 @@ class Yahoo(BaseClient):
tick[Datacode.DIV_YIELD] = float(raw(summaryDetail, 'dividendYield'))
tick[Datacode.EX_DIV_DATE] = self.save_wrapper(
lambda: dateutil.parser.parse(str(fmt(summaryDetail, 'exDividendDate')), yearfirst=True, dayfirst=False).date())
tick[Datacode.SHARES_OUT] = float(raw(defaultKeyStatistics, 'sharesOutstanding'))
tick[Datacode.FREE_FLOAT] = float(raw(defaultKeyStatistics, 'floatShares'))
tick[Datacode.PAYOUT_RATIO] = float(raw(summaryDetail, 'payoutRatio'))
tick[Datacode.LOW_52_WEEK] = float(raw(summaryDetail, 'fiftyTwoWeekLow'))
@@ -238,6 +245,10 @@ class Yahoo(BaseClient):
if match:
tick[Datacode.CURRENCY] = match.group(1)
# fallback for yield on US mutual funds and ETFs, which is in different field
if not tick[Datacode.DIV_YIELD]:
tick[Datacode.DIV_YIELD] = float(raw(summaryDetail, 'yield'))
name = price['longName'] or price['shortName']
if name:
tick[Datacode.NAME] = html.unescape(str(name))
+1 -7
View File
@@ -14,19 +14,13 @@ import os
cur_dir = os.getcwd()
addin_id = "com.financials.getinfo"
addin_version = "2.6.0"
addin_version = "3.0.7"
addin_displayname = "Financial Market Extension"
addin_publisher_link = "https://github.com/cmallwitz/Financials-Extension"
addin_publisher_name = "The Publisher"
print("Generating extension files for version", addin_version)
################################################################################
# version.py
version_file = open(cur_dir + '/build/version.py', 'w')
version_file.write(f"version = '{addin_version}'")
version_file.close()
################################################################################
# description.xml
+27 -4
View File
@@ -70,6 +70,12 @@ class Test(unittest.TestCase):
s = financials.getRealtime('IBM:NYQ', 'EPS', 'FT')
self.assertEqual(float, type(s), 'test_US_equity EPS {}'.format(s))
s = financials.getRealtime('IBM:NYQ', 'SHARES_OUT', 'FT')
self.assertEqual(float, type(s), 'test_US_equity SHARES_OUT {}'.format(s))
s = financials.getRealtime('IBM:NYQ', 'FREE_FLOAT', 'FT')
self.assertEqual(float, type(s), 'test_US_equity FREE_FLOAT {}'.format(s))
s = financials.getRealtime('IBM:NYQ', 'PE_RATIO', 'FT')
self.assertEqual(float, type(s), 'test_US_equity PE_RATIO {}'.format(s))
@@ -96,7 +102,7 @@ class Test(unittest.TestCase):
s = financials.getRealtime('IBM:NYQ', 'INDUSTRY', 'FT')
self.assertEqual(str, type(s), 'test_US_equity INDUSTRY {}'.format(s))
self.assertEqual(s, 'Software & Computer Services', 'test_US_equity INDUSTRY {}'.format(s))
self.assertEqual(s, 'Software and Computer Services', 'test_US_equity INDUSTRY {}'.format(s))
# may fail (s is None) on weekends when date/time displayed doesn't have time component with TZ
s = financials.getRealtime('IBM:NYQ', 'TIMEZONE', 'FT')
@@ -202,7 +208,7 @@ class Test(unittest.TestCase):
s = financials.getRealtime('SAPX:GER', 'INDUSTRY', 'FT')
self.assertEqual(str, type(s), 'test_DE_equity INDUSTRY {}'.format(s))
self.assertEqual('Software & Computer Services', s, 'test_DE_equity INDUSTRY {}'.format(s))
self.assertEqual('Software and Computer Services', s, 'test_DE_equity INDUSTRY {}'.format(s))
s = financials.getRealtime('SAPX:GER', 'LAST_PRICE_DATE', 'FT')
self.assertEqual(str, type(s), 'test_DE_equity LAST_PRICE_DATE {}'.format(s))
@@ -217,6 +223,12 @@ class Test(unittest.TestCase):
s = financials.getRealtime('SAPX:GER', 'EPS', 'FT')
self.assertEqual(float, type(s), 'test_DE_equity EPS {}'.format(s))
s = financials.getRealtime('SAPX:GER', 'SHARES_OUT', 'FT')
self.assertEqual(float, type(s), 'test_DE_equity SHARES_OUT {}'.format(s))
s = financials.getRealtime('SAPX:GER', 'FREE_FLOAT', 'FT')
self.assertEqual(float, type(s), 'test_DE_equity FREE_FLOAT {}'.format(s))
s = financials.getRealtime('SAPX:GER', 'PE_RATIO', 'FT')
self.assertEqual(float, type(s), 'test_DE_equity PE_RATIO {}'.format(s))
@@ -231,7 +243,7 @@ class Test(unittest.TestCase):
self.assertTrue(testutils.is_date(s), 'test_DE_equity EX_DIV_DATE {}'.format(s))
s = financials.getRealtime('ISHAX:GER', 'NAME', 'FT')
self.assertEqual('Intershop Communications AG', s, 'test_DE_equity NAME {}'.format(s))
self.assertEqual('INTERSHOP Communications AG', s, 'test_DE_equity NAME {}'.format(s))
s = financials.getRealtime('ISHAX:GER', 'BETA', 'FT')
self.assertTrue(testutils.is_positive_float(s), 'test_DE_equity BETA {}'.format(s))
@@ -254,6 +266,17 @@ class Test(unittest.TestCase):
s = financials.getRealtime('ISHAX:GER', 'EX_DIV_DATE', 'FT')
self.assertIsNone(s, 'test_DE_equity EX_DIV_DATE {}'.format(s))
def test_DK_equity(self):
s = financials.getRealtime('NOVO B:CPH', 'name', 'FT')
self.assertEqual('Novo Nordisk A/S', s, 'test_DK_equity NAME {}'.format(s))
s = financials.getRealtime('NOVO B:CPH', 'currency', 'FT')
self.assertEqual('DKK', s, 'test_DK_equity CURRENCY {}'.format(s))
s = financials.getRealtime('NOVO B:CPH', 'industry', 'FT')
self.assertEqual(str, type(s), 'test_DK_equity INDUSTRY {}'.format(s))
self.assertEqual('Pharmaceuticals and Biotechnology', s, 'test_DK_equity INDUSTRY {}'.format(s))
def test_TY_equity(self):
s = financials.getRealtime('6503:TYO', 'OPEN', 'FT')
self.assertEqual(float, type(s), 'test_TY_equity OPEN {}'.format(s))
@@ -317,7 +340,7 @@ class Test(unittest.TestCase):
s = financials.getRealtime('DELT:TLV', 'SECTOR', 'FT')
self.assertEqual(str, type(s), 'test_TlV_equity SECTOR {}'.format(s))
self.assertEqual('Consumer Goods', s, 'test_TY_equity SECTOR {}'.format(s))
self.assertEqual('Consumer Discretionary', s, 'test_TY_equity SECTOR {}'.format(s))
s = financials.getRealtime('DELT:TLV', 'INDUSTRY', 'FT')
self.assertEqual(str, type(s), 'test_TlV_equity INDUSTRY {}'.format(s))
+2 -27
View File
@@ -20,7 +20,8 @@ from datacode import Datacode
financials = financials.createInstance(None)
class Test(unittest.TestCase):
# class Test(unittest.TestCase):
class Test:
def test_currency(self):
s = financials.getRealtime('EURGBP', Datacode.LAST_PRICE.value, 'GOOGLE')
@@ -249,32 +250,6 @@ class Test(unittest.TestCase):
s = financials.getRealtime('NYS:IBM', Datacode.LAST_PRICE.value, cell_range)
self.assertEqual(s, 'Cell range not allowed for source', 'test_errors')
def test_support(self):
cell_range = ((1, 2), ('3', '4'), (5.0, 6.0))
s = financials.getRealtime('SUPPORT')
self.assertTrue(s.startswith("ctx="), 'test_errors SUPPORT {}'.format(s))
s = financials.getRealtime('SUPPORT', 1)
self.assertTrue(s.startswith("ctx="), 'test_errors SUPPORT {}'.format(s))
self.assertTrue("type(datacode)=<class 'int'>" in s, 'test_errors SUPPORT {}'.format(s))
self.assertTrue("str(datacode)=1" in s, 'test_errors SUPPORT {}'.format(s))
s = financials.getRealtime('SUPPORT', 1.0)
self.assertTrue(s.startswith("ctx="), 'test_errors SUPPORT {}'.format(s))
self.assertTrue("type(datacode)=<class 'float'>" in s, 'test_errors SUPPORT {}'.format(s))
self.assertTrue("str(datacode)=1.0" in s, 'test_errors SUPPORT {}'.format(s))
s = financials.getRealtime('SUPPORT', '1')
self.assertTrue(s.startswith("ctx="), 'test_errors SUPPORT {}'.format(s))
self.assertTrue("type(datacode)=<class 'str'>" in s, 'test_errors SUPPORT {}'.format(s))
self.assertTrue("str(datacode)=1" in s, 'test_errors SUPPORT {}'.format(s))
s = financials.getRealtime('SUPPORT', cell_range)
self.assertTrue(s.startswith("ctx="), 'test_errors SUPPORT {}'.format(s))
self.assertTrue("type(datacode)=<class 'tuple'>" in s, 'test_errors SUPPORT {}'.format(s))
self.assertTrue("str(datacode)=((1, 2), ('3', '4'), (5.0, 6.0))" in s, 'test_errors SUPPORT {}'.format(s))
if __name__ == '__main__':
parser = argparse.ArgumentParser()
+57
View File
@@ -0,0 +1,57 @@
# test_google.py
#
# license: GNU LGPL
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
import argparse
import logging
import sys
import unittest
logging.basicConfig(level=logging.ERROR, format="%(asctime)s %(name)s %(levelname)s %(message)s")
import financials
financials = financials.createInstance(None)
class Test(unittest.TestCase):
def test_support(self):
cell_range = ((1, 2), ('3', '4'), (5.0, 6.0))
s = financials.getRealtime('SUPPORT')
self.assertTrue(s.startswith("ctx="), 'test_errors SUPPORT {}'.format(s))
self.assertTrue("version=3.0.0" in s, 'test_errors SUPPORT {}'.format(s))
s = financials.getRealtime('SUPPORT', 1)
self.assertTrue(s.startswith("ctx="), 'test_errors SUPPORT {}'.format(s))
self.assertTrue("type(datacode)=<class 'int'>" in s, 'test_errors SUPPORT {}'.format(s))
self.assertTrue("str(datacode)=1" in s, 'test_errors SUPPORT {}'.format(s))
s = financials.getRealtime('SUPPORT', 1.0)
self.assertTrue(s.startswith("ctx="), 'test_errors SUPPORT {}'.format(s))
self.assertTrue("type(datacode)=<class 'float'>" in s, 'test_errors SUPPORT {}'.format(s))
self.assertTrue("str(datacode)=1.0" in s, 'test_errors SUPPORT {}'.format(s))
s = financials.getRealtime('SUPPORT', '1')
self.assertTrue(s.startswith("ctx="), 'test_errors SUPPORT {}'.format(s))
self.assertTrue("type(datacode)=<class 'str'>" in s, 'test_errors SUPPORT {}'.format(s))
self.assertTrue("str(datacode)=1" in s, 'test_errors SUPPORT {}'.format(s))
s = financials.getRealtime('SUPPORT', cell_range)
self.assertTrue(s.startswith("ctx="), 'test_errors SUPPORT {}'.format(s))
self.assertTrue("type(datacode)=<class 'tuple'>" in s, 'test_errors SUPPORT {}'.format(s))
self.assertTrue("str(datacode)=((1, 2), ('3', '4'), (5.0, 6.0))" in s, 'test_errors SUPPORT {}'.format(s))
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('unittest_args', nargs='*')
args = parser.parse_args()
unit_argv = [sys.argv[0]] + args.unittest_args
unittest.main(argv=unit_argv)
+46 -31
View File
@@ -106,6 +106,12 @@ class Test(unittest.TestCase):
s = financials.getRealtime('IBM', Datacode.PAYOUT_RATIO.value, 'YAHOO')
self.assertTrue(testutils.is_positive_float(s), 'test_realtime_US_equity PAYOUT_RATIO {}'.format(s))
s = financials.getRealtime('IBM', Datacode.SHARES_OUT.value, 'YAHOO')
self.assertTrue(testutils.is_positive_float(s), 'test_realtime_US_equity SHARES_OUT {}'.format(s))
s = financials.getRealtime('IBM', Datacode.FREE_FLOAT.value, 'YAHOO')
self.assertTrue(testutils.is_positive_float(s), 'test_realtime_US_equity FREE_FLOAT {}'.format(s))
s = financials.getRealtime('IBM', Datacode.EXCHANGE.value, 'YAHOO')
self.assertEqual(s, 'NYQ', 'test_realtime_US_equity EXCHANGE')
@@ -123,39 +129,44 @@ class Test(unittest.TestCase):
s = financials.getRealtime('VFIAX', Datacode.LAST_PRICE_TIME.value, 'YAHOO')
self.assertEqual(str, type(s), 'test_realtime_US_mutuals LAST_PRICE_TIME {}'.format(s))
s = financials.getRealtime('VFIAX', Datacode.DIV_YIELD.value, 'YAHOO')
self.assertTrue(testutils.is_positive_float(s), 'test_realtime_US_mutuals DIV_YIELD {}'.format(s))
def test_realtime_US_options(self):
s = financials.getRealtime('IBM210618C00090000', Datacode.PREV_CLOSE.value, 'YAHOO')
# symbol from https://finance.yahoo.com/quote/IBM/options?p=IBM
s = financials.getRealtime('IBM230120C00130000', Datacode.PREV_CLOSE.value, 'YAHOO')
self.assertEqual(float, type(s), 'test_realtime_US_options PREV_CLOSE {}'.format(s))
s = financials.getRealtime('IBM210618C00090000', Datacode.NAME.value, 'YAHOO')
s = financials.getRealtime('IBM230120C00130000', Datacode.NAME.value, 'YAHOO')
self.assertEqual(str, type(s), 'test_realtime_US_options NAME {}'.format(s))
self.assertEqual('IBM Jun 2021 90.000 call', s, 'test_realtime_US_options NAME {}'.format(s))
self.assertEqual('IBM Jan 2023 130.000 call', s, 'test_realtime_US_options NAME {}'.format(s))
s = financials.getRealtime('IBM210618C00090000', Datacode.EXPIRY_DATE.value, 'YAHOO')
s = financials.getRealtime('IBM230120C00130000', Datacode.EXPIRY_DATE.value, 'YAHOO')
self.assertEqual(str, type(s), 'test_realtime_US_options EXPIRY_DATE {}'.format(s))
self.assertTrue(testutils.is_date(s), 'test_realtime_US_options EXPIRY_DATE {}'.format(s))
self.assertEqual("2021-06-18", s, 'test_realtime_US_options EXPIRY_DATE {}'.format(s))
self.assertEqual("2023-01-20", s, 'test_realtime_US_options EXPIRY_DATE {}'.format(s))
s = financials.getRealtime('IBM210618C00090000', Datacode.LAST_PRICE.value, 'YAHOO')
s = financials.getRealtime('IBM230120C00130000', Datacode.LAST_PRICE.value, 'YAHOO')
self.assertEqual(float, type(s), 'test_realtime_US_options LAST_PRICE {}'.format(s))
s = financials.getRealtime('IBM210618C00090000', Datacode.OPEN.value, 'YAHOO')
s = financials.getRealtime('IBM230120C00130000', Datacode.OPEN.value, 'YAHOO')
self.assertEqual(float, type(s), 'test_realtime_US_options OPEN {}'.format(s))
s = financials.getRealtime('IBM210618C00090000', Datacode.VOLUME.value, 'YAHOO')
s = financials.getRealtime('IBM230120C00130000', Datacode.VOLUME.value, 'YAHOO')
self.assertEqual(float, type(s), 'test_realtime_US_options VOLUME {}'.format(s))
s = financials.getRealtime('IBM210618C00090000', Datacode.BID.value, 'YAHOO')
s = financials.getRealtime('IBM230120C00130000', Datacode.BID.value, 'YAHOO')
self.assertEqual(float, type(s), 'test_realtime_US_options BID {}'.format(s))
s = financials.getRealtime('IBM210618C00090000', Datacode.ASK.value, 'YAHOO')
s = financials.getRealtime('IBM230120C00130000', Datacode.ASK.value, 'YAHOO')
self.assertEqual(float, type(s), 'test_realtime_US_options ASK {}'.format(s))
s = financials.getRealtime('IBM210618C00090000', Datacode.BIDSIZE.value, 'YAHOO')
s = financials.getRealtime('IBM230120C00130000', Datacode.BIDSIZE.value, 'YAHOO')
self.assertEqual(float, type(s), 'test_realtime_US_options BIDSIZE {}'.format(s))
s = financials.getRealtime('IBM210618C00090000', Datacode.ASKSIZE.value, 'YAHOO')
s = financials.getRealtime('IBM230120C00130000', Datacode.ASKSIZE.value, 'YAHOO')
self.assertEqual(float, type(s), 'test_realtime_US_options ASKSIZE {}'.format(s))
def test_realtime_UK_ETF(self):
@@ -164,12 +175,11 @@ class Test(unittest.TestCase):
self.assertEqual(float, type(s), 'test_realtime_UK_ETF LAST_PRICE {}'.format(s))
s = financials.getRealtime('VERX.L', Datacode.TIMEZONE.value, 'YAHOO')
self.assertEqual(s, 'Europe/London', 'test_realtime_UK_ETF TIMEZONE {}'.format(s))
self.assertEqual('Europe/London', s, 'test_realtime_UK_ETF TIMEZONE {}'.format(s))
s = financials.getRealtime('CSP1.L', Datacode.NAME.value, 'YAHOO')
self.assertEqual(str, type(s), 'test_realtime_UK_ETF NAME {}'.format(s))
self.assertEqual(s, 'iShares VII Public Limited Company - iShares Core S&P 500 UCITS ETF',
'test_realtime_UK_ETF NAME {}'.format(s))
self.assertEqual('iShares Core S&P 500 UCITS ETF USD (Acc)', s, 'test_realtime_UK_ETF NAME {}'.format(s))
def test_realtime_DE_equity(self):
@@ -177,11 +187,11 @@ class Test(unittest.TestCase):
self.assertEqual(float, type(s), 'test_realtime_DE_equity LAST_PRICE {}'.format(s))
s = financials.getRealtime('SAP.DE', Datacode.TIMEZONE.value, 'YAHOO')
self.assertEqual(s, 'Europe/Berlin', 'test_realtime_DE_equity TIMEZONE {}'.format(s))
self.assertEqual('Europe/Berlin', s, 'test_realtime_DE_equity TIMEZONE {}'.format(s))
s = financials.getRealtime('SAP.DE', Datacode.SECTOR.value, 'YAHOO')
self.assertEqual(str, type(s), 'test_realtime_DE_equity SECTOR {}'.format(s))
self.assertEqual(s, 'Technology', 'test_realtime_DE_equity SECTOR {}'.format(s))
self.assertEqual('Technology', s, 'test_realtime_DE_equity SECTOR {}'.format(s))
s = financials.getRealtime('SAP.DE', Datacode.INDUSTRY.value, 'YAHOO')
self.assertEqual(str, type(s), 'test_realtime_DE_equity INDUSTRY {}'.format(s))
@@ -193,6 +203,20 @@ class Test(unittest.TestCase):
s = financials.getRealtime('C060.DE', Datacode.EXCHANGE.value, 'YAHOO')
self.assertEqual(s, 'GER', 'test_realtime_DE_equity EXCHANGE')
def test_DK_equity(self):
s = financials.getRealtime('NOVO-B.CO', 'last_price', 'YAHOO')
self.assertEqual(float, type(s), 'test_DK_equity LAST_PRICE {}'.format(s))
s = financials.getRealtime('NOVO-B.CO', 'name', 'YAHOO')
self.assertEqual('Novo Nordisk A/S', s, 'test_DK_equity NAME {}'.format(s))
s = financials.getRealtime('NOVO-B.CO', 'currency', 'YAHOO')
self.assertEqual('DKK', s, 'test_DK_equity CURRENCY {}'.format(s))
s = financials.getRealtime('NOVO-B.CO', 'industry', 'YAHOO')
self.assertEqual(str, type(s), 'test_DK_equity INDUSTRY {}'.format(s))
self.assertEqual('Biotechnology', s, 'test_DK_equity INDUSTRY {}'.format(s))
def test_realtime_TY_equity(self):
s = financials.getRealtime('6503.T', Datacode.OPEN.value, 'YAHOO')
self.assertEqual(float, type(s), 'test_TY_equity OPEN {}'.format(s))
@@ -238,12 +262,12 @@ class Test(unittest.TestCase):
self.assertIsNone(s, 'test_historic_US_equity LAST_PRICE {}'.format(s))
s = financials.getHistoric('IBM', Datacode.CLOSE.value, '2017-01-03', 'YAHOO')
self.assertEqual(167.190002, s, 'test_historic_US_equity CLOSE {}'.format(s))
self.assertEqual(159.837479, s, 'test_historic_US_equity CLOSE {}'.format(s))
financials.yahoo.historicdata = {}
s = financials.getHistoric('IBM', Datacode.CLOSE.value, '2017-01-03', 'YAHOO')
self.assertEqual(167.190002, s, 'test_historic_US_equity CLOSE {}'.format(s))
self.assertEqual(159.837479, s, 'test_historic_US_equity CLOSE {}'.format(s))
directory = os.path.join(str(pathlib.Path.home()), '.financials-extension')
ibm = os.path.join(directory, 'yahoo-IBM.csv')
@@ -255,11 +279,10 @@ class Test(unittest.TestCase):
financials.yahoo.historicdata = {}
s = financials.getHistoric('IBM', Datacode.CLOSE.value, '2017-01-03', 'YAHOO')
self.assertEqual(167.190002, s, 'test_historic_US_equity CLOSE {}'.format(s))
self.assertEqual(159.837479, s, 'test_historic_US_equity CLOSE {}'.format(s))
# Note: quarterly dividend and splits will change past adjusted prices - will fail after the next dividend
s = financials.getHistoric('IBM', Datacode.ADJ_CLOSE.value, '2017-01-03', 'YAHOO')
self.assertEqual(137.772202, s, 'test_historic_US_equity ADJ_CLOSE {}'.format(s))
self.assertEqual(float, type(s), 'test_historic_US_equity ADJ_CLOSE {}'.format(s))
def test_historic_UK_ETF(self):
@@ -272,14 +295,6 @@ class Test(unittest.TestCase):
financials.yahoo.historicdata = {}
# Inception Date 2014-09-30
s = financials.getHistoric('VERX.L', Datacode.CLOSE.value, '2018-04-02', 'YAHOO') # Easter Monday
self.assertEqual(s, 'Not a trading day \'2018-04-02\'', 'test_historic_UK_ETF CLOSE {}'.format(s))
# Inception Date 2014-09-30
s = financials.getHistoric('VERX.L', Datacode.CLOSE.value, '2015-01-01', 'YAHOO')
self.assertEqual(s, 'Not a trading day \'2015-01-01\'', 'test_historic_UK_ETF CLOSE {}'.format(s))
s = financials.getHistoric('VERX.L', Datacode.LAST_PRICE.value, '2017-01-01', 'YAHOO')
self.assertEqual(s, 'Not a trading day \'2017-01-01\'', 'test_historic_UK_ETF LAST_PRICE {}'.format(s))
@@ -315,7 +330,7 @@ class Test(unittest.TestCase):
self.assertEqual(s, 82.889999, 'test_historic_DE_equity CLOSE {}'.format(s))
s = financials.getHistoric('C060.DE', Datacode.CLOSE.value, '2017-01-03', 'YAHOO')
self.assertEqual(s, 68.209999, 'test_historic_DE_equity CLOSE {}'.format(s))
self.assertEqual(s, 72.870003, 'test_historic_DE_equity CLOSE {}'.format(s))
def test_realtime_errors(self):
-15
View File
@@ -1,15 +0,0 @@
# version.py
#
# license: GNU LGPL
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
# This file will not be actually used as it is regenerated with the correct build version
# by generate_metainfo.py in the "compile" step
# This is a bit of a hack but I can't find out how to access the version in description.xml once deployed
version = '0.0.0'