Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 23 additions & 35 deletions pyasx/data/securities.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@


import csv
import io
import requests
import requests.exceptions
import tempfile
import pandas
import pyasx
import pyasx.config
import pyasx.data
import urllib


def get_listed_securities():
Expand All @@ -30,42 +33,24 @@ def get_listed_securities():

all_listed_securities = []

# GET CSV file of ASX codes, as a stream
# Create Pandas dataframe directly from URL of .xls file
try:
df = pandas.read_excel(pyasx.config.get(
'asx_securities_tsv'), skiprows=[0, 1, 2, 3])

response = requests.get(pyasx.config.get('asx_securities_tsv'), stream=True)
response.raise_for_status() # throw exception for bad status codes
except urllib.error.HTTPError as ex:
raise pyasx.data.LookupError(
"Failed to lookup listed securities; %s" % str(ex))

except requests.exceptions.HTTPError as ex:
for index, row in df.iterrows():
ticker, name, type, isin = row

raise pyasx.data.LookupError("Failed to lookup listed securities; %s" % str(ex))

# parse the CSV result, piping it to a temp file to make the process more memory efficient
with tempfile.NamedTemporaryFile("w+") as temp_stream:

# pipe the CSV data to a temp file
for block in response.iter_content(1024, True):
temp_stream.write(block.decode('unicode_escape'))

# rewind the temp stream and convert it to an iterator for csv.reader below
temp_stream.seek(0)
temp_iter = iter(temp_stream.readline, '');

# skip the first 5 rows of the CSV as they are header rows
for i in range(0, 5):
next(temp_iter)

# read the stream back in & parse out the company details from each row
for row in csv.reader(temp_iter, dialect="excel-tab"):

ticker, name, type, isin = row

all_listed_securities.append({
'ticker': ticker,
'name': name,
'type': type,
'isin': isin
})
all_listed_securities.append({
'ticker': ticker,
'name': name,
'type': type,
'isin': isin
})

return all_listed_securities

Expand Down Expand Up @@ -133,9 +118,12 @@ def _normalise_security_info(raw):
security_info['indices'] = _normalise_security_indices_info(raw)

# parse dates to datetime objects
security_info['last_trade_date'] = pyasx.data._parse_datetime(security_info['last_trade_date'])
security_info['year_high_date'] = pyasx.data._parse_datetime(security_info['year_high_date'])
security_info['year_low_date'] = pyasx.data._parse_datetime(security_info['year_low_date'])
security_info['last_trade_date'] = pyasx.data._parse_datetime(
security_info['last_trade_date'])
security_info['year_high_date'] = pyasx.data._parse_datetime(
security_info['year_high_date'])
security_info['year_low_date'] = pyasx.data._parse_datetime(
security_info['year_low_date'])

return security_info

Expand Down
67 changes: 23 additions & 44 deletions pyasx/tests/data/securities.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,36 @@
import pyasx.data
import pyasx.data.securities
import json
import pandas


class SecuritiesTest(unittest.TestCase):
"""
Unit tests for pyasx.data.securities module
"""


def __init__(self, *args, **kwargs):
super(SecuritiesTest, self).__init__(*args, **kwargs)

self.get_listed_securities_data = []
self.get_listed_securities_mock = ""
self.get_security_info_mock = []


def setUp(self):

self.setUpGetListedSecurities()
self.setUpGetSecurityInfo()

def setUpGetListedSecurities(self):

def setUpGetListedSecurities(self): # TODO

self.get_listed_securities_data = [
# just the first ones from the file to test with
[ "IJH", "ISHARES MID-CAP ETF", "CHESS DEPOSITARY INTERESTS 1:1 ISHS&P400", "AU000000IJH2" ],
[ "MOQ", "MOQ LIMITED", "ORDINARY FULLY PAID", "AU000000MOQ5" ],
[ "MOQAI", "MOQ LIMITED", "OPTION EXPIRING VARIOUS DATES EX VARIOUS PRICES", "AU0000MOQAI6" ],
]

# build the mock CSV data based on self.get_listed_companies_data

for i in range(0, 5): # header
self.get_listed_securities_mock += "HEADER\tROW\n"

for row in self.get_listed_securities_data:

csv_row = "\t".join(row)
csv_row += "\n"

self.get_listed_securities_mock += csv_row

# just the first ones from the file to test with
self.get_listed_securities_data = pandas.DataFrame([
["IJH", "ISHARES MID-CAP ETF",
"CHESS DEPOSITARY INTERESTS 1:1 ISHS&P400", "AU000000IJH2"],
["MOQ", "MOQ LIMITED", "ORDINARY FULLY PAID", "AU000000MOQ5"],
["MOQAI", "MOQ LIMITED",
"OPTION EXPIRING VARIOUS DATES EX VARIOUS PRICES", "AU0000MOQAI6"],
])

def setUpGetSecurityInfo(self):

Expand Down Expand Up @@ -86,41 +73,35 @@ def setUpGetSecurityInfo(self):
"deprecated_number_of_shares": 25,
"suspended": False,
"status": [
"CD"
"CD"
],
"indices": [
{
"index_code": "XJO",
"name_full": "S&P/ASX 200",
"name_short": "S&P/ASX200",
"name_abrev": "S&P/ASX 200"
"index_code": "XJO",
"name_full": "S&P/ASX 200",
"name_short": "S&P/ASX200",
"name_abrev": "S&P/ASX 200"
}
]
}


def testGetListedSecuritiesMocked(self):
"""
Unit test for pyasx.data.securities.get_listed_securities()
Test pulling mock data + verify
"""

with unittest.mock.patch("requests.get") as mock:

# set up mock iterator for response.iter_content()

bytes_mock = bytes(self.get_listed_securities_mock, "utf-8")

instance = mock.return_value
instance.iter_content.return_value = iter([bytes_mock])
with unittest.mock.patch("pandas.read_excel") as mock:
# set up mock iterator for pandas.read_csv
mock.return_value = self.get_listed_securities_data

# this is the test
securities = pyasx.data.securities.get_listed_securities()

# verify data is all correct
i = 0;
i = 0
for security in securities:
security_data = self.get_listed_securities_data[i]
security_data = self.get_listed_securities_data.iloc[i, :]

self.assertEqual(security["ticker"], security_data[0])
self.assertEqual(security["name"], security_data[1])
Expand All @@ -129,16 +110,15 @@ def testGetListedSecuritiesMocked(self):

i += 1


def testGetListedSecuritiesLive(self):
"""
Unit test for pyasx.data.securities.get_listed_securities()
Simple check of pulling live data
"""

securities = pyasx.data.securities.get_listed_securities()
self.assertTrue(len(securities) > 1000) # there are at least a few thousand listed securities

# there are at least a few thousand listed securities
self.assertTrue(len(securities) > 1000)

def testGetSecurityInfoMocked(self):
"""
Expand Down Expand Up @@ -200,13 +180,12 @@ def testGetSecurityInfoMocked(self):
self.assertEquals(security["is_suspended"], False)
self.assertTrue(len(security["indices"]))


def testGetSecurityInfoLive(self):
"""
Unit test for pyasx.data.securities.get_security_info()
Simple check of pulling live data
"""

security = pyasx.data.securities.get_security_info('CBAPC')
security = pyasx.data.securities.get_security_info('CBAPD')
self.assertTrue("ticker" in security)
self.assertTrue(len(security))
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ PyYAML==3.13
requests==2.18.4
six==1.11.0
urllib3==1.24.1
pandas==1.1.2
xlrd==1.2.0