From 13040851c6ebec9b6fd6dba024afeb961fc01f42 Mon Sep 17 00:00:00 2001 From: Luca Sbardella Date: Sun, 4 Aug 2019 11:02:47 +0100 Subject: [PATCH 1/2] test for black formatting --- .circleci/config.yml | 3 + .vscode/settings.json | 3 + Makefile | 4 + README.rst | 3 +- ccy/__init__.py | 91 +++-- ccy/core/country.py | 40 +- ccy/core/currency.py | 129 +++--- ccy/core/data.py | 692 ++++++++++++++++++++++++++------- ccy/core/daycounter.py | 27 +- ccy/dates/__init__.py | 3 - ccy/dates/converters.py | 50 +-- ccy/dates/futures.py | 26 +- ccy/dates/period.py | 59 ++- ccy/tradingcentres/__init__.py | 4 +- ccy/tradingcentres/centres.py | 42 +- ccy/tradingcentres/holiday.py | 6 +- dev/requirements-dev.txt | 6 + setup.cfg | 5 +- tests/test_ccy.py | 84 ++-- tests/test_dates.py | 80 ++-- tests/test_tcs.py | 4 +- 21 files changed, 919 insertions(+), 442 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.circleci/config.yml b/.circleci/config.yml index 1663116..cc8c960 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -33,6 +33,9 @@ jobs: - run: name: flake8 command: flake8 + - run: + name: black + command: make black - run: name: test command: pytest --cov diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..b48db30 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.pythonPath": "venv/bin/python" +} diff --git a/Makefile b/Makefile index 7ab9283..ac85c64 100644 --- a/Makefile +++ b/Makefile @@ -13,3 +13,7 @@ clean: ## remove python cache files version: ## dipsplay software version @python3 -c "import ccy; print(ccy.__version__)" + + +black: ## check black formatting + black --check ccy tests diff --git a/README.rst b/README.rst index 725655f..ecaf745 100644 --- a/README.rst +++ b/README.rst @@ -5,8 +5,7 @@ join and add more. :Package: |license| |pyversions| |status| |downloads| :CI: |master-build| |coverage-master| -:Documentation: http://pythonhosted.org/ccy/ -:Dowloads: http://pypi.python.org/pypi/ccy +:Dowloads: https://pypi.org/project/ccy/ :Source: https://github.com/lsbardel/ccy .. |pyversions| image:: https://img.shields.io/pypi/pyversions/ccy.svg diff --git a/ccy/__init__.py b/ccy/__init__.py index 2c489db..3c22a19 100644 --- a/ccy/__init__.py +++ b/ccy/__init__.py @@ -1,41 +1,74 @@ """Python currencies""" -__version__ = '1.0.0' +__version__ = "1.1.0" -from .core.currency import ( - currency, currencydb, ccypair, currency_pair, - dump_currency_table -) from .core.country import ( - country, countryccy, set_new_country, - countries, set_country_map, country_map, - CountryError, eurozone, print_eurozone + CountryError, + countries, + country, + country_map, + countryccy, + eurozone, + print_eurozone, + set_country_map, + set_new_country, ) -from .core.daycounter import getdc, ActActYears, alldc +from .core.currency import ( + ccypair, + currency, + currency_pair, + currencydb, + dump_currency_table, +) +from .core.daycounter import ActActYears, alldc, getdc from .dates.converters import ( - todate, date2timestamp, timestamp2date, yyyymmdd2date, - date2yyyymmdd, juldate2date, date2juldate, date_from_string, - jstimestamp + date2juldate, + date2timestamp, + date2yyyymmdd, + date_from_string, + jstimestamp, + juldate2date, + timestamp2date, + todate, + yyyymmdd2date, ) from .dates.futures import future_date_to_code, future_month_dict -from .dates.period import period, Period - +from .dates.period import Period, period __all__ = [ - 'currency', 'currencydb', 'ccypair', 'currency_pair', - 'dump_currency_table', + "currency", + "currencydb", + "ccypair", + "currency_pair", + "dump_currency_table", # - 'getdc', 'ActActYears', 'alldc', + "getdc", + "ActActYears", + "alldc", # - 'country', 'countryccy', 'set_new_country', - 'countries', 'set_country_map', 'country_map', - 'CountryError', 'eurozone', 'print_eurozone', - 'future_date_to_code', 'future_month_dict', - 'period', 'Period', - 'todate', 'date2timestamp', 'timestamp2date', - 'yyyymmdd2date', 'date2yyyymmdd', 'juldate2date', - 'date2juldate', 'date_from_string', 'jstimestamp' + "country", + "countryccy", + "set_new_country", + "countries", + "set_country_map", + "country_map", + "CountryError", + "eurozone", + "print_eurozone", + "future_date_to_code", + "future_month_dict", + "period", + "Period", + "todate", + "date2timestamp", + "timestamp2date", + "yyyymmdd2date", + "date2yyyymmdd", + "juldate2date", + "date2juldate", + "date_from_string", + "jstimestamp", ] @@ -45,7 +78,7 @@ def cross(code): def crossover(code): - return currency(code).as_cross('/') + return currency(code).as_cross("/") def all(): @@ -53,14 +86,14 @@ def all(): def g7(): - return ['EUR', 'GBP', 'USD', 'CAD'] + return ["EUR", "GBP", "USD", "CAD"] def g10(): - return g7() + ['CHF', 'SEK', 'JPY'] + return g7() + ["CHF", "SEK", "JPY"] def g10m(): """modified g10 = G10 + AUD, NZD, NOK """ - return g10() + ['AUD', 'NZD', 'NOK'] + return g10() + ["AUD", "NZD", "NOK"] diff --git a/ccy/core/country.py b/ccy/core/country.py index 0f8859c..fb36750 100644 --- a/ccy/core/country.py +++ b/ccy/core/country.py @@ -8,8 +8,9 @@ # using ISO 3166-1 alpha-2 country codes # see http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 # -eurozone = tuple(('AT BE CY DE EE ES FI FR GR IE IT LU LV LT MT ' - 'NL PT SI SK').split(' ')) +eurozone = tuple( + ("AT BE CY DE EE ES FI FR GR IE IT LU LV LT MT " "NL PT SI SK").split(" ") +) def print_eurozone(): @@ -29,7 +30,7 @@ class CountryError(Exception): def country(code): cdb = countries() code = country_map(code) - return cdb.get(code, '') + return cdb.get(code, "") def countryccy(code): @@ -39,15 +40,16 @@ def countryccy(code): def countries(): - ''' + """ get country dictionar from pytz and add some extra. - ''' + """ global _countries if not _countries: v = {} _countries = v try: from pytz import country_names + for k, n in country_names.items(): v[k.upper()] = n except Exception: @@ -56,17 +58,17 @@ def countries(): def countryccys(): - ''' + """ Create a dictionary with keys given by countries ISO codes and values given by their currencies - ''' + """ global _country_ccys if not _country_ccys: v = {} _country_ccys = v ccys = currencydb() for c in eurozone: - v[c] = 'EUR' + v[c] = "EUR" for c in ccys.values(): if c.default_country: v[c.default_country] = c.code @@ -74,9 +76,9 @@ def countryccys(): def set_country_map(cfrom, cto, name=None, replace=True): - ''' + """ Set a mapping between a country code to another code - ''' + """ global _country_maps cdb = countries() cfrom = str(cfrom).upper() @@ -86,7 +88,7 @@ def set_country_map(cfrom, cto, name=None, replace=True): c = name cto = str(cto).upper() if cto in cdb: - raise CountryError('Country %s already in database' % cto) + raise CountryError("Country %s already in database" % cto) cdb[cto] = c _country_maps[cfrom] = cto ccys = currencydb() @@ -101,34 +103,34 @@ def set_country_map(cfrom, cto, name=None, replace=True): cdb.pop(cfrom) cccys.pop(cfrom) else: - raise CountryError('Country %s not in database' % c) + raise CountryError("Country %s not in database" % c) def set_new_country(code, ccy, name): - ''' + """ Add new country code to database - ''' + """ code = str(code).upper() cdb = countries() if code in cdb: - raise CountryError('Country %s already in database' % code) + raise CountryError("Country %s already in database" % code) ccys = currencydb() ccy = str(ccy).upper() if ccy not in ccys: - raise CountryError('Currency %s not in database' % ccy) + raise CountryError("Currency %s not in database" % ccy) cdb[code] = str(name) cccys = countryccys() cccys[code] = ccy def country_map(code): - ''' + """ Country mapping - ''' + """ code = str(code).upper() global _country_maps return _country_maps.get(code, code) # Add eurozone to list of Countries -set_new_country('EU', 'EUR', 'Eurozone') +set_new_country("EU", "EUR", "Eurozone") diff --git a/ccy/core/currency.py b/ccy/core/currency.py index 39e9328..e1f1b7b 100644 --- a/ccy/core/currency.py +++ b/ccy/core/currency.py @@ -2,15 +2,14 @@ from .data import make_ccys - usd_order = 5 def to_string(v): if isinstance(v, bytes): - return v.decode('utf-8') + return v.decode("utf-8") else: - return '%s' % v + return "%s" % v def overusdfun(v1): @@ -18,23 +17,31 @@ def overusdfun(v1): def overusdfuni(v1): - return 1./v1 + return 1.0 / v1 class ccy(object): - ''' + """ Currency object - ''' - def __init__(self, code, isonumber, twoletterscode, order, name, - roundoff=4, - default_country=None, - fixeddc=None, - floatdc=None, - fixedfreq=None, - floatfreq=None, - future=None, - symbol=r'\00a4', - html=''): + """ + + def __init__( + self, + code, + isonumber, + twoletterscode, + order, + name, + roundoff=4, + default_country=None, + fixeddc=None, + floatdc=None, + fixedfreq=None, + floatfreq=None, + future=None, + symbol=r"\00a4", + html="", + ): self.code = to_string(code) self.id = self.code self.isonumber = isonumber @@ -44,19 +51,19 @@ def __init__(self, code, isonumber, twoletterscode, order, name, self.rounding = roundoff self.default_country = default_country self.symbol_raw = symbol - self.symbol = symbol.encode('utf-8').decode('unicode_escape') + self.symbol = symbol.encode("utf-8").decode("unicode_escape") self.html = html or self.symbol self.fixeddc = fixeddc self.floatdc = floatdc - self.future = '' + self.future = "" if future: self.future = str(future) def __getstate__(self): - return {'code': self.code} + return {"code": self.code} def __setstate__(self, dict): - c = currency(dict['code']) + c = currency(dict["code"]) self.__dict__.update(c.__dict__) def __eq__(self, other): @@ -66,41 +73,43 @@ def __eq__(self, other): def description(self): if self.order > usd_order: - v = 'USD / %s' % self.code + v = "USD / %s" % self.code else: - v = '%s / USD' % self.code + v = "%s / USD" % self.code if self.order != usd_order: - return '%s Spot Exchange Rate' % v + return "%s Spot Exchange Rate" % v else: - return 'Dollar' + return "Dollar" def info(self): - return {'code': self.code, - 'isonumber': self.isonumber, - 'twoletterscode': self.twoletterscode, - 'symbol': self.symbol, - 'order': self.order, - 'name': self.name, - 'rounding': self.rounding, - 'default_country': self.default_country, - 'unicode symbol': self.symbol_raw} + return { + "code": self.code, + "isonumber": self.isonumber, + "twoletterscode": self.twoletterscode, + "symbol": self.symbol, + "order": self.order, + "name": self.name, + "rounding": self.rounding, + "default_country": self.default_country, + "unicode symbol": self.symbol_raw, + } def printinfo(self, stream=None): info = self.info() stream = stream or sys.stdout for k, v in info.items(): - stream.write(to_string('%s: %s\n' % (k, v))) + stream.write(to_string("%s: %s\n" % (k, v))) def __repr__(self): - return '%s: %s' % (self.__class__.__name__, self.code) + return "%s: %s" % (self.__class__.__name__, self.code) def __str__(self): return self.code def swap(self, c2): - ''' + """ put the order of currencies as market standard - ''' + """ inv = False c1 = self if c1.order > c2.order: @@ -122,40 +131,41 @@ def usdoverfunc(self): else: return overusdfuni - def as_cross(self, delimiter=''): - ''' + def as_cross(self, delimiter=""): + """ Return a cross rate representation with respect USD. @param delimiter: could be '' or '/' normally - ''' + """ if self.order > usd_order: - return 'USD%s%s' % (delimiter, self.code) + return "USD%s%s" % (delimiter, self.code) else: - return '%s%sUSD' % (self.code, delimiter) + return "%s%sUSD" % (self.code, delimiter) def spot(self, c2, v1, v2): if self.order > c2.order: vt = v1 v1 = v2 v2 = vt - return v1/v2 + return v1 / v2 class ccy_pair(object): - ''' + """ Currency pair such as EURUSD, USDCHF XXXYYY - XXX is the foreign currency, while YYY is the base currency XXXYYY means 1 unit of of XXX cost XXXYYY units of YYY - ''' + """ + def __init__(self, c1, c2): self.ccy1 = c1 self.ccy2 = c2 - self.code = '%s%s' % (c1, c2) + self.code = "%s%s" % (c1, c2) self.id = self.code def __repr__(self): - return '%s: %s' % (self.__class__.__name__, self.code) + return "%s: %s" % (self.__class__.__name__, self.code) def __str__(self): return self.code @@ -166,9 +176,9 @@ def mkt(self): else: return self - def over(self, name='usd'): - '''Returns a new currency pair with the *over* currency as - second part of the pair (Foreign currency).''' + def over(self, name="usd"): + """Returns a new currency pair with the *over* currency as + second part of the pair (Foreign currency).""" name = name.upper() if self.ccy1.code == name.upper(): return ccy_pair(self.ccy2, self.ccy1) @@ -177,7 +187,6 @@ def over(self, name='usd'): class ccydb(dict): - def insert(self, *args, **kwargs): c = ccy(*args, **kwargs) self[c.code] = c @@ -209,7 +218,7 @@ def ccypair(code): def currency_pair(code): - '''Construct a :class:`ccy_pair` from a six letter string.''' + """Construct a :class:`ccy_pair` from a six letter string.""" c = str(code) c1 = currency(c[:3]) c2 = currency(c[3:]) @@ -231,13 +240,15 @@ def make_ccypairs(): def dump_currency_table(): - headers = ['code', - 'name', - ('isonumber', 'iso'), - ('html', 'symbol'), - ('default_country', 'country'), - 'order', - 'rounding'] + headers = [ + "code", + "name", + ("isonumber", "iso"), + ("html", "symbol"), + ("default_country", "country"), + "order", + "rounding", + ] all = [] data = [] all.append(data) diff --git a/ccy/core/data.py b/ccy/core/data.py index 08e7e80..eaccbd3 100644 --- a/ccy/core/data.py +++ b/ccy/core/data.py @@ -1,172 +1,570 @@ - def make_ccys(db): - ''' + """ Create the currency dictionary - ''' + """ dfr = 4 - dollar = r'\u0024' - peso = r'\u20b1' - kr = r'kr' + dollar = r"\u0024" + peso = r"\u20b1" + kr = r"kr" insert = db.insert # G10 & SCANDI - insert('EUR', '978', 'EU', 1, - 'Euro', dfr, 'EU', '30/360', 'ACT/360', - future='FE', symbol=r'\u20ac', html='€') - insert('GBP', '826', 'BP', 2, - 'British Pound', dfr, 'GB', 'ACT/365', 'ACT/365', - symbol=r'\u00a3', html='£') - insert('AUD', '036', 'AD', 3, - 'Australian Dollar', dfr, 'AU', 'ACT/365', 'ACT/365', - symbol=dollar, html='$') - insert('NZD', '554', 'ND', 4, - 'New-Zealand Dollar', dfr, 'NZ', 'ACT/365', 'ACT/365', - symbol=dollar, html='$') - insert('USD', '840', 'UD', 5, - 'US Dollar', 0, 'US', '30/360', 'ACT/360', - future='ED', symbol=dollar, html='$') - insert('CAD', '124', 'CD', 6, - 'Canadian Dollar', dfr, 'CA', 'ACT/365', 'ACT/365', - symbol=dollar, html='$') - insert('CHF', '756', 'SF', 7, - 'Swiss Franc', dfr, 'CH', '30/360', 'ACT/360', - symbol=r'Fr', html='₣') - insert('NOK', '578', 'NK', 8, - 'Norwegian Krona', dfr, 'NO', '30/360', 'ACT/360', - symbol=kr, html=kr) - insert('SEK', '752', 'SK', 9, - 'Swedish Krona', dfr, 'SE', '30/360', 'ACT/360', - symbol=kr, html=kr) - insert('DKK', '208', 'DK', 10, - 'Danish Krona', dfr, 'DK', '30/360', 'ACT/360', - symbol=kr, html=kr) - insert('JPY', '392', 'JY', 10000, - 'Japanese Yen', 2, 'JP', 'ACT/365', 'ACT/360', - symbol=r'\u00a5', html='¥') + insert( + "EUR", + "978", + "EU", + 1, + "Euro", + dfr, + "EU", + "30/360", + "ACT/360", + future="FE", + symbol=r"\u20ac", + html="€", + ) + insert( + "GBP", + "826", + "BP", + 2, + "British Pound", + dfr, + "GB", + "ACT/365", + "ACT/365", + symbol=r"\u00a3", + html="£", + ) + insert( + "AUD", + "036", + "AD", + 3, + "Australian Dollar", + dfr, + "AU", + "ACT/365", + "ACT/365", + symbol=dollar, + html="$", + ) + insert( + "NZD", + "554", + "ND", + 4, + "New-Zealand Dollar", + dfr, + "NZ", + "ACT/365", + "ACT/365", + symbol=dollar, + html="$", + ) + insert( + "USD", + "840", + "UD", + 5, + "US Dollar", + 0, + "US", + "30/360", + "ACT/360", + future="ED", + symbol=dollar, + html="$", + ) + insert( + "CAD", + "124", + "CD", + 6, + "Canadian Dollar", + dfr, + "CA", + "ACT/365", + "ACT/365", + symbol=dollar, + html="$", + ) + insert( + "CHF", + "756", + "SF", + 7, + "Swiss Franc", + dfr, + "CH", + "30/360", + "ACT/360", + symbol=r"Fr", + html="₣", + ) + insert( + "NOK", + "578", + "NK", + 8, + "Norwegian Krona", + dfr, + "NO", + "30/360", + "ACT/360", + symbol=kr, + html=kr, + ) + insert( + "SEK", + "752", + "SK", + 9, + "Swedish Krona", + dfr, + "SE", + "30/360", + "ACT/360", + symbol=kr, + html=kr, + ) + insert( + "DKK", + "208", + "DK", + 10, + "Danish Krona", + dfr, + "DK", + "30/360", + "ACT/360", + symbol=kr, + html=kr, + ) + insert( + "JPY", + "392", + "JY", + 10000, + "Japanese Yen", + 2, + "JP", + "ACT/365", + "ACT/360", + symbol=r"\u00a5", + html="¥", + ) # ASIA - insert('CNY', '156', 'CY', 680, - 'Chinese Renminbi', dfr, 'CN', 'ACT/365', 'ACT/365', - symbol=r'\u00a5', html='¥') - insert('KRW', '410', 'KW', 110000, - 'South Korean won', 2, 'KR', 'ACT/365', 'ACT/365', - symbol=r'\u20a9', html='₩') - insert('SGD', '702', 'SD', 15, - 'Singapore Dollar', dfr, 'SG', 'ACT/365', 'ACT/365', - symbol=dollar, html='$') - insert('IDR', '360', 'IH', 970000, - 'Indonesian Rupiah', 0, 'ID', 'ACT/360', 'ACT/360', - symbol=r'Rp', html='Rp') - insert('THB', '764', 'TB', 3300, - 'Thai Baht', 2, 'TH', 'ACT/365', 'ACT/365', - symbol=r'\u0e3f', html='฿') - insert('TWD', '901', 'TD', 18, - 'Taiwan Dollar', dfr, 'TW', 'ACT/365', 'ACT/365', - symbol=dollar, html='$') - insert('HKD', '344', 'HD', 19, - 'Hong Kong Dollar', dfr, 'HK', 'ACT/365', 'ACT/365', - symbol=r'\u5713', html='HK$') - insert('PHP', '608', 'PP', 4770, - 'Philippines Peso', dfr, 'PH', 'ACT/360', 'ACT/360', - symbol=peso, html='₱') - insert('INR', '356', 'IR', 4500, - 'Indian Rupee', dfr, 'IN', 'ACT/365', 'ACT/365', - symbol=r'\u20a8', html='₨') - insert('MYR', '458', 'MR', 345, - 'Malaysian Ringgit', dfr, 'MY', 'ACT/365', 'ACT/365') - insert('VND', '704', 'VD', 1700000, - 'Vietnamese Dong', 0, 'VN', 'ACT/365', 'ACT/365', - symbol=r'\u20ab', html='₫') + insert( + "CNY", + "156", + "CY", + 680, + "Chinese Renminbi", + dfr, + "CN", + "ACT/365", + "ACT/365", + symbol=r"\u00a5", + html="¥", + ) + insert( + "KRW", + "410", + "KW", + 110000, + "South Korean won", + 2, + "KR", + "ACT/365", + "ACT/365", + symbol=r"\u20a9", + html="₩", + ) + insert( + "SGD", + "702", + "SD", + 15, + "Singapore Dollar", + dfr, + "SG", + "ACT/365", + "ACT/365", + symbol=dollar, + html="$", + ) + insert( + "IDR", + "360", + "IH", + 970000, + "Indonesian Rupiah", + 0, + "ID", + "ACT/360", + "ACT/360", + symbol=r"Rp", + html="Rp", + ) + insert( + "THB", + "764", + "TB", + 3300, + "Thai Baht", + 2, + "TH", + "ACT/365", + "ACT/365", + symbol=r"\u0e3f", + html="฿", + ) + insert( + "TWD", + "901", + "TD", + 18, + "Taiwan Dollar", + dfr, + "TW", + "ACT/365", + "ACT/365", + symbol=dollar, + html="$", + ) + insert( + "HKD", + "344", + "HD", + 19, + "Hong Kong Dollar", + dfr, + "HK", + "ACT/365", + "ACT/365", + symbol=r"\u5713", + html="HK$", + ) + insert( + "PHP", + "608", + "PP", + 4770, + "Philippines Peso", + dfr, + "PH", + "ACT/360", + "ACT/360", + symbol=peso, + html="₱", + ) + insert( + "INR", + "356", + "IR", + 4500, + "Indian Rupee", + dfr, + "IN", + "ACT/365", + "ACT/365", + symbol=r"\u20a8", + html="₨", + ) + insert( + "MYR", "458", "MR", 345, "Malaysian Ringgit", dfr, "MY", "ACT/365", "ACT/365" + ) + insert( + "VND", + "704", + "VD", + 1700000, + "Vietnamese Dong", + 0, + "VN", + "ACT/365", + "ACT/365", + symbol=r"\u20ab", + html="₫", + ) # LATIN AMERICA - insert('BRL', '986', 'BC', 200, - 'Brazilian Real', dfr, 'BR', 'BUS/252', 'BUS/252', - symbol=r'R$') - insert('PEN', '604', 'PS', 220, - 'Peruvian New Sol', dfr, 'PE', 'ACT/360', 'ACT/360', - symbol=r'S/.') - insert('ARS', '032', 'AP', 301, - 'Argentine Peso', dfr, 'AR', '30/360', 'ACT/360', - symbol=dollar, html='$') - insert('MXN', '484', 'MP', 1330, - 'Mexican Peso', dfr, 'MX', 'ACT/360', 'ACT/360', - symbol=dollar, html='$') - insert('CLP', '152', 'CH', 54500, - 'Chilean Peso', 2, 'CL', 'ACT/360', 'ACT/360', - symbol=dollar, html='$') - insert('COP', '170', 'CL', 190000, - 'Colombian Peso', 2, 'CO', 'ACT/360', 'ACT/360', - symbol=dollar, html='$') + insert( + "BRL", + "986", + "BC", + 200, + "Brazilian Real", + dfr, + "BR", + "BUS/252", + "BUS/252", + symbol=r"R$", + ) + insert( + "PEN", + "604", + "PS", + 220, + "Peruvian New Sol", + dfr, + "PE", + "ACT/360", + "ACT/360", + symbol=r"S/.", + ) + insert( + "ARS", + "032", + "AP", + 301, + "Argentine Peso", + dfr, + "AR", + "30/360", + "ACT/360", + symbol=dollar, + html="$", + ) + insert( + "MXN", + "484", + "MP", + 1330, + "Mexican Peso", + dfr, + "MX", + "ACT/360", + "ACT/360", + symbol=dollar, + html="$", + ) + insert( + "CLP", + "152", + "CH", + 54500, + "Chilean Peso", + 2, + "CL", + "ACT/360", + "ACT/360", + symbol=dollar, + html="$", + ) + insert( + "COP", + "170", + "CL", + 190000, + "Colombian Peso", + 2, + "CO", + "ACT/360", + "ACT/360", + symbol=dollar, + html="$", + ) # TODO: Check towletters code and position - insert('JMD', '388', 'JD', 410, - 'Jamaican Dollar', dfr, 'JM', 'ACT/360', 'ACT/360', - symbol=dollar, html='$') + insert( + "JMD", + "388", + "JD", + 410, + "Jamaican Dollar", + dfr, + "JM", + "ACT/360", + "ACT/360", + symbol=dollar, + html="$", + ) # TODO: Check towletters code and position - insert('TTD', '780', 'TT', 410, - 'Trinidad and Tobago Dollar', dfr, 'TT', 'ACT/360', 'ACT/360', - symbol=dollar, html='$') + insert( + "TTD", + "780", + "TT", + 410, + "Trinidad and Tobago Dollar", + dfr, + "TT", + "ACT/360", + "ACT/360", + symbol=dollar, + html="$", + ) # TODO: Check towletters code and position - insert('BMD', '060', 'BD', 410, - 'Bermudian Dollar', dfr, 'BM', - symbol=dollar, html='$') + insert( + "BMD", + "060", + "BD", + 410, + "Bermudian Dollar", + dfr, + "BM", + symbol=dollar, + html="$", + ) # EASTERN EUROPE - insert('CZK', '203', 'CK', 28, - 'Czech Koruna', dfr, 'CZ', 'ACT/360', 'ACT/360', - symbol=r'\u004b\u010d') - insert('PLN', '985', 'PZ', 29, - 'Polish Zloty', dfr, 'PL', 'ACT/ACT', 'ACT/365', - symbol=r'\u0050\u0142') - insert('TRY', '949', 'TY', 30, - 'Turkish Lira', dfr, 'TR', 'ACT/360', 'ACT/360', - symbol=r'\u0054\u004c') - insert('HUF', '348', 'HF', 32, - 'Hungarian Forint', dfr, 'HU', 'ACT/365', 'ACT/360', - symbol=r'Ft', html='Ft') - insert('RON', '946', 'RN', 34, - 'Romanian Leu', dfr, 'RO', 'ACT/360', 'ACT/360') - insert('RUB', '643', 'RR', 36, - 'Russian Ruble', dfr, 'RU', 'ACT/ACT', 'ACT/ACT', - symbol=r'\u0440\u0443\u0431') + insert( + "CZK", + "203", + "CK", + 28, + "Czech Koruna", + dfr, + "CZ", + "ACT/360", + "ACT/360", + symbol=r"\u004b\u010d", + ) + insert( + "PLN", + "985", + "PZ", + 29, + "Polish Zloty", + dfr, + "PL", + "ACT/ACT", + "ACT/365", + symbol=r"\u0050\u0142", + ) + insert( + "TRY", + "949", + "TY", + 30, + "Turkish Lira", + dfr, + "TR", + "ACT/360", + "ACT/360", + symbol=r"\u0054\u004c", + ) + insert( + "HUF", + "348", + "HF", + 32, + "Hungarian Forint", + dfr, + "HU", + "ACT/365", + "ACT/360", + symbol=r"Ft", + html="Ft", + ) + insert("RON", "946", "RN", 34, "Romanian Leu", dfr, "RO", "ACT/360", "ACT/360") + insert( + "RUB", + "643", + "RR", + 36, + "Russian Ruble", + dfr, + "RU", + "ACT/ACT", + "ACT/ACT", + symbol=r"\u0440\u0443\u0431", + ) # TODO: Check towletters code and position - insert('HRK', '191', 'HK', 410, - 'Croatian kuna', dfr, 'HR', - symbol=r'kn') + insert("HRK", "191", "HK", 410, "Croatian kuna", dfr, "HR", symbol=r"kn") # TODO: Check towletters code and position - insert('KZT', '398', 'KT', 410, - 'Tenge', dfr, 'KZ', - symbol=r'\u20b8', html='₸') + insert( + "KZT", "398", "KT", 410, "Tenge", dfr, "KZ", symbol=r"\u20b8", html="₸" + ) # TODO: Check towletters code and position - insert('BGN', '975', 'BN', 410, - 'Bulgarian Lev', dfr, 'BG', - symbol=r'\u043b\u0432.', html='лв') + insert( + "BGN", + "975", + "BN", + 410, + "Bulgarian Lev", + dfr, + "BG", + symbol=r"\u043b\u0432.", + html="лв", + ) # MIDDLE EAST & AFRICA - insert('ILS', '376', 'IS', 410, - 'Israeli Shekel', dfr, 'IL', 'ACT/365', 'ACT/365', - symbol=r'\u20aa', html='₪') + insert( + "ILS", + "376", + "IS", + 410, + "Israeli Shekel", + dfr, + "IL", + "ACT/365", + "ACT/365", + symbol=r"\u20aa", + html="₪", + ) # TODO: Check towletters code and position - insert('AED', '784', 'AE', 410, - 'United Arab Emirates Dirham', dfr, 'AE') + insert("AED", "784", "AE", 410, "United Arab Emirates Dirham", dfr, "AE") # TODO: Check towletters code and position - insert('QAR', '634', 'QA', 410, - 'Qatari Riyal', dfr, 'QA', - symbol=r'\ufdfc', html='﷼') + insert( + "QAR", + "634", + "QA", + 410, + "Qatari Riyal", + dfr, + "QA", + symbol=r"\ufdfc", + html="﷼", + ) # TODO: Check towletters code and position - insert('SAR', '682', 'SR', 410, - 'Saudi Riyal', dfr, 'SA', - symbol=r'\ufdfc', html='﷼') - insert('EGP', '818', 'EP', 550, - 'Egyptian Pound', dfr, 'EG', - symbol=r'\u00a3', html='£') - insert('NGN', '566', 'NG', 650, - 'Nigerian Naira', dfr, 'NG', - symbol=r'\u20a6', html='₦') - insert('ZAR', '710', 'SA', 750, - 'South African Rand', dfr, 'ZA', 'ACT/365', 'ACT/365', - symbol=r'R', html='R') + insert( + "SAR", + "682", + "SR", + 410, + "Saudi Riyal", + dfr, + "SA", + symbol=r"\ufdfc", + html="﷼", + ) + insert( + "EGP", + "818", + "EP", + 550, + "Egyptian Pound", + dfr, + "EG", + symbol=r"\u00a3", + html="£", + ) + insert( + "NGN", + "566", + "NG", + 650, + "Nigerian Naira", + dfr, + "NG", + symbol=r"\u20a6", + html="₦", + ) + insert( + "ZAR", + "710", + "SA", + 750, + "South African Rand", + dfr, + "ZA", + "ACT/365", + "ACT/365", + symbol=r"R", + html="R", + ) # BITCOIN - insert('XBT', '000', 'BT', -1, - 'Bitcoin', 8, 'WW', - symbol=r'\u0e3f', html='฿') + insert("XBT", "000", "BT", -1, "Bitcoin", 8, "WW", symbol=r"\u0e3f", html="฿") diff --git a/ccy/core/daycounter.py b/ccy/core/daycounter.py index c1b433b..f4e6633 100644 --- a/ccy/core/daycounter.py +++ b/ccy/core/daycounter.py @@ -1,16 +1,15 @@ -'''Day Counter for Counting time between 2 dates. +"""Day Counter for Counting time between 2 dates. Implemented:: * Actual 360 * Actual 365 * 30 / 360 * Actual Actual -''' +""" from copy import copy from datetime import date - -__all__ = ['getdc', 'ActActYears', 'alldc'] +__all__ = ["getdc", "ActActYears", "alldc"] def getdc(name): @@ -33,11 +32,10 @@ def ActActYears(dt): if r > 0: a = 1.0 dd = (dt - date(y, 1, 1)).days - return y + dd/(365.0 + a) + return y + dd / (365.0 + a) class DayCounterMeta(type): - def __new__(cls, name, bases, attrs): new_class = super(DayCounterMeta, cls).__new__(cls, name, bases, attrs) if new_class.name: @@ -53,35 +51,34 @@ class DayCounter(object): __metaclass__ = DayCounterMeta def count(self, start, end): - return (end-start).days + return (end - start).days def dcf(self, start, end): - return self.count(start, end)/360.0 + return self.count(start, end) / 360.0 class act360(DayCounter): - name = 'ACT/360' + name = "ACT/360" class act365(DayCounter): - name = 'ACT/365' + name = "ACT/365" def dcf(self, start, end): - return self.count(start, end)/365.0 + return self.count(start, end) / 365.0 class thirty360(DayCounter): - name = '30/360' + name = "30/360" def count(self, start, end): d1 = min(start.day, 30) d2 = min(end.day, 30) - return 360*(end.year - start.year) + 30*(end.month - - start.month) + d2 - d1 + return 360 * (end.year - start.year) + 30 * (end.month - start.month) + d2 - d1 class actact(DayCounter): - name = 'ACT/ACT' + name = "ACT/ACT" def dcf(self, start, end): return ActActYears(end) - ActActYears(start) diff --git a/ccy/dates/__init__.py b/ccy/dates/__init__.py index 00ae1d8..e69de29 100644 --- a/ccy/dates/__init__.py +++ b/ccy/dates/__init__.py @@ -1,3 +0,0 @@ -from .period import * # noqa -from .converters import * # noqa -from .futures import * # noqa diff --git a/ccy/dates/converters.py b/ccy/dates/converters.py index 81c51ef..9c5b097 100644 --- a/ccy/dates/converters.py +++ b/ccy/dates/converters.py @@ -1,19 +1,19 @@ import time -from datetime import datetime, date +from datetime import date, datetime try: from dateutil.parser import parse as date_from_string -except ImportError: # noqa +except ImportError: # noqa def date_from_string(dte): raise NotImplementedError def todate(val): - '''Convert val to a datetime.date instance by trying several + """Convert val to a datetime.date instance by trying several conversion algorithm. If it fails it raise a ValueError exception. - ''' + """ if not val: raise ValueError("Value not provided") if isinstance(val, datetime): @@ -43,17 +43,17 @@ def date2timestamp(dte): def jstimestamp(dte): - '''Convert a date to a javascript timestamp. + """Convert a date to a javascript timestamp. A Javascript timestamp is the number of milliseconds since - January 1, 1970 00:00:00 UTC.''' - return 1000*date2timestamp(dte) + January 1, 1970 00:00:00 UTC.""" + return 1000 * date2timestamp(dte) def timestamp2date(tstamp): "Converts a unix timestamp to a Python datetime object" dt = datetime.fromtimestamp(tstamp) - if not dt.hour+dt.minute+dt.second+dt.microsecond: + if not dt.hour + dt.minute + dt.second + dt.microsecond: return dt.date() else: return dt @@ -67,19 +67,19 @@ def yyyymmdd2date(dte): d = md % 100 return date(y, m, d) except Exception: - raise ValueError('Could not convert %s to date' % dte) + raise ValueError("Could not convert %s to date" % dte) def date2yyyymmdd(dte): - return dte.day + 100*(dte.month + 100*dte.year) + return dte.day + 100 * (dte.month + 100 * dte.year) def juldate2date(val): - '''Convert from a Julian date/datetime to python date or datetime''' + """Convert from a Julian date/datetime to python date or datetime""" ival = int(val) dec = val - ival try: - val4 = 4*ival + val4 = 4 * ival yd = val4 % 1461 st = 1899 if yd >= 4: @@ -97,27 +97,33 @@ def juldate2date(val): m = qq % 12 + 1 d = qr // 5 + 1 except Exception: - raise ValueError('Could not convert %s to date' % val) + raise ValueError("Could not convert %s to date" % val) if dec: - dec24 = 24*dec + dec24 = 24 * dec hours = int(dec24) - minutes = int(60*(dec24 - hours)) - tot_seconds = 60*(60*(dec24 - hours) - minutes) + minutes = int(60 * (dec24 - hours)) + tot_seconds = 60 * (60 * (dec24 - hours) - minutes) seconds = int(tot_seconds) - microseconds = int(1000000*(tot_seconds-seconds)) + microseconds = int(1000000 * (tot_seconds - seconds)) return datetime(y, m, d, hours, minutes, seconds, microseconds) else: return date(y, m, d) def date2juldate(val): - '''Convert from a python date/datetime to a Julian date & time''' - f = 12*val.year + val.month - 22803 + """Convert from a python date/datetime to a Julian date & time""" + f = 12 * val.year + val.month - 22803 fq = f // 12 fr = f % 12 - dt = (fr*153 + 302)//5 + val.day + fq*1461//4 + dt = (fr * 153 + 302) // 5 + val.day + fq * 1461 // 4 if isinstance(val, datetime): - return dt + (val.hour + (val.minute + ( - val.second + 0.000001*val.microsecond)/60.)/60.)/24. + return ( + dt + + ( + val.hour + + (val.minute + (val.second + 0.000001 * val.microsecond) / 60.0) / 60.0 + ) + / 24.0 + ) else: return dt diff --git a/ccy/dates/futures.py b/ccy/dates/futures.py index aef4a19..b06f12b 100644 --- a/ccy/dates/futures.py +++ b/ccy/dates/futures.py @@ -1,8 +1,19 @@ -future_month_list = ['F', 'G', 'H', 'J', 'K', 'M', - 'N', 'Q', 'U', 'V', 'X', 'Z'] +future_month_list = ["F", "G", "H", "J", "K", "M", "N", "Q", "U", "V", "X", "Z"] -short_month = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] +short_month = [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", +] def future_date_to_code(dte): @@ -10,8 +21,9 @@ def future_date_to_code(dte): For example december 2010 will result in Z10. """ - return '%s%s' % (future_month_list[dte.month-1], str(dte.year)[2:]) + return "%s%s" % (future_month_list[dte.month - 1], str(dte.year)[2:]) -future_month_dict = dict((future_month_list[i], - (i+1, short_month[i])) for i in range(0, 12)) +future_month_dict = dict( + (future_month_list[i], (i + 1, short_month[i])) for i in range(0, 12) +) diff --git a/ccy/dates/period.py b/ccy/dates/period.py index 901f3de..efe5cd7 100644 --- a/ccy/dates/period.py +++ b/ccy/dates/period.py @@ -1,5 +1,5 @@ -def period(pstr=''): - '''Create a period object from a period string''' +def period(pstr=""): + """Create a period object from a period string""" return Period.make(pstr) @@ -22,13 +22,12 @@ def safemod(x, d): class Period: - def __init__(self, months=0, days=0): self._months = months self._days = days @classmethod - def make(cls, pstr=''): + def make(cls, pstr=""): if isinstance(pstr, cls): return pstr else: @@ -41,13 +40,13 @@ def add_days(self, days): self._days += days def add_weeks(self, weeks): - self._days += int(7*weeks) + self._days += int(7 * weeks) def add_months(self, months): self._months += months def add_years(self, years): - self._months += int(12*years) + self._months += int(12 * years) @property def years(self): @@ -67,43 +66,43 @@ def days(self): @property def totaldays(self): - return 30*self._months + self._days + return 30 * self._months + self._days def __repr__(self): - '''The period string''' + """The period string""" return self.components() def __str__(self): return self.__repr__() def components(self): - '''The period string''' - p = '' + """The period string""" + p = "" neg = self.totaldays < 0 y = self.years m = self.months w = self.weeks d = self.days if y: - p = '%sY' % abs(y) + p = "%sY" % abs(y) if m: - p = '%s%sM' % (p, abs(m)) + p = "%s%sM" % (p, abs(m)) if w: - p = '%s%sW' % (p, abs(w)) + p = "%s%sW" % (p, abs(w)) if d: - p = '%s%sD' % (p, abs(d)) - return '-'+p if neg else p + p = "%s%sD" % (p, abs(d)) + return "-" + p if neg else p def simple(self): - '''A string representation with only one period delimiter.''' + """A string representation with only one period delimiter.""" if self._days: - return '%sD' % self.totaldays + return "%sD" % self.totaldays elif self.months: - return '%sM' % self._months + return "%sM" % self._months elif self.years: - return '%sY' % self.years + return "%sY" % self.years else: - return '' + return "" def add_tenure(self, pstr): if isinstance(pstr, self.__class__): @@ -117,36 +116,34 @@ def add_tenure(self, pstr): if not st: done = True else: - ip = find_first_of(st, 'DWMY') + ip = find_first_of(st, "DWMY") if ip == -1: raise ValueError("Unknown period %s" % pstr) p = st[ip] v = int(st[:ip]) sign = sign if v > 0 else -sign - v = sign*abs(v) - if p == 'D': + v = sign * abs(v) + if p == "D": self.add_days(v) - elif p == 'W': + elif p == "W": self.add_weeks(v) - elif p == 'M': + elif p == "M": self.add_months(v) - elif p == 'Y': + elif p == "Y": self.add_years(v) - st = st[ip+1:] + st = st[ip + 1 :] return self def __add__(self, other): other = self.make(other) - return self.__class__(self._months+other._months, - self._days+other._days) + return self.__class__(self._months + other._months, self._days + other._days) def __radd__(self, other): return self + other def __sub__(self, other): other = self.make(other) - return self.__class__(self._months-other._months, - self._days-other._days) + return self.__class__(self._months - other._months, self._days - other._days) def __rsub__(self, other): return self.make(other) - self diff --git a/ccy/tradingcentres/__init__.py b/ccy/tradingcentres/__init__.py index 7684bbb..4718ff1 100644 --- a/ccy/tradingcentres/__init__.py +++ b/ccy/tradingcentres/__init__.py @@ -1,6 +1,6 @@ -from .centres import centres, TradingCentre +from .centres import TradingCentre, centres -__all__ = ['centres', 'TradingCentre', 'prevbizday', 'nextbizday'] +__all__ = ["centres", "TradingCentre", "prevbizday", "nextbizday"] def prevbizday(dte=None, nd=1, tcs=None): diff --git a/ccy/tradingcentres/centres.py b/ccy/tradingcentres/centres.py index 43f7e18..63627ae 100644 --- a/ccy/tradingcentres/centres.py +++ b/ccy/tradingcentres/centres.py @@ -1,8 +1,9 @@ import datetime -# from dateutil import rrule from .holiday import BaseHoliday, PartialDate +# from dateutil import rrule + # weekdays = (rrule.MO,rrule.TU,rrule.WE,rrule.TH,rrule.FR) # weekend = (rrule.SA,rrule.SU) @@ -15,7 +16,7 @@ def centres(codes=None): tcs = TradingCentres() if codes: - lcs = codes.upper().replace(' ', '').split(',') + lcs = codes.upper().replace(" ", "").split(",") for code in lcs: tc = _tcs.get(code) if tc: @@ -25,37 +26,37 @@ def centres(codes=None): def get_declared_holidays(bases, attrs): _attrs = attrs.copy() - holidays = [(field_name, attrs.pop(field_name)) for field_name, obj in - _attrs.items() if isinstance(obj, BaseHoliday)] + holidays = [ + (field_name, attrs.pop(field_name)) + for field_name, obj in _attrs.items() + if isinstance(obj, BaseHoliday) + ] for base in bases[::-1]: - if hasattr(base, 'holidays'): + if hasattr(base, "holidays"): holidays = base.holidays.items() + holidays return dict(holidays) class TradingCentreMeta(type): - def __new__(cls, name, bases, attrs): global _tcs - abstract = attrs.pop('abstract', False) + abstract = attrs.pop("abstract", False) if abstract: - return super(TradingCentreMeta, cls).__new__(cls, name, - bases, attrs) + return super(TradingCentreMeta, cls).__new__(cls, name, bases, attrs) else: - attrs['holidays'] = get_declared_holidays(bases, attrs) - new_class = super(TradingCentreMeta, cls).__new__(cls, name, - bases, attrs) - if not getattr(new_class, 'abstract', False): + attrs["holidays"] = get_declared_holidays(bases, attrs) + new_class = super(TradingCentreMeta, cls).__new__(cls, name, bases, attrs) + if not getattr(new_class, "abstract", False): cl = new_class() _tcs[cl.code] = cl return new_class -TradingCentreBase = TradingCentreMeta('TradingCentreBase', - (object, ), - {'abstract': True}) +TradingCentreBase = TradingCentreMeta( + "TradingCentreBase", (object,), {"abstract": True} +) class TradingCentre(TradingCentreBase): @@ -71,6 +72,7 @@ def __new__(cls): def __get_code(self): return self.__class__.__name__ + code = property(__get_code) def isbizday(self, dte): @@ -93,7 +95,7 @@ def _isholiday(self, dte): else: start = year end = year - for year in range(start, end+1): + for year in range(start, end + 1): self.build_dates(year) return self._cache.get(dte, False) @@ -156,7 +158,8 @@ def prevbizday(self, dte, nd=1): # # TRADING CENTRES class TGT(TradingCentre): - '''Target''' + """Target""" + new_years_day = PartialDate(1, 1) labor_day = PartialDate(5, 1) christmas_day = PartialDate(12, 25) @@ -164,5 +167,6 @@ class TGT(TradingCentre): class LON(TradingCentre): - '''London''' + """London""" + christmas_day = PartialDate(12, 25) diff --git a/ccy/tradingcentres/holiday.py b/ccy/tradingcentres/holiday.py index 6dae7a7..09b3b33 100644 --- a/ccy/tradingcentres/holiday.py +++ b/ccy/tradingcentres/holiday.py @@ -2,8 +2,7 @@ class BaseHoliday(object): - - def __init__(self, description=''): + def __init__(self, description=""): self.description = description def extended(self): @@ -15,13 +14,12 @@ def allholidays(self, year): def __repr__(self): d = self.extended() if d: - return '%s: %s' % (self.__class__.__name__, d) + return "%s: %s" % (self.__class__.__name__, d) else: return self.__class__.__name__ class PartialDate(BaseHoliday): - def __init__(self, month, day): self.month = month self.day = day diff --git a/dev/requirements-dev.txt b/dev/requirements-dev.txt index 0326358..2be5658 100644 --- a/dev/requirements-dev.txt +++ b/dev/requirements-dev.txt @@ -4,3 +4,9 @@ pytest pytest-cov twine agile-toolkit +mypy +black +isort[requirements] +flake8-blind-except +# flake8-builtins +flake8-commas diff --git a/setup.cfg b/setup.cfg index bde5e71..3814d02 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,9 +3,8 @@ release = clean sdist bdist_wheel upload [flake8] exclude = __pycache__,.eggs,docs,build,dist,venv - -[pep8] -exclude = __pycache__,.eggs,docs,build,dist,venv +max-line-length = 88 +ignore = C815,C812,W503 [bdist_wheel] universal = 1 diff --git a/tests/test_ccy.py b/tests/test_ccy.py index 8c1d7ce..cd08e58 100644 --- a/tests/test_ccy.py +++ b/tests/test_ccy.py @@ -4,15 +4,23 @@ import pytest -from ccy import (currencydb, countryccy, set_new_country, CountryError, - ccypair, currency, currency_pair, dump_currency_table) +from ccy import ( + currencydb, + countryccy, + set_new_country, + CountryError, + ccypair, + currency, + currency_pair, + dump_currency_table, +) from ccy.core.country import eurozone def test_defaultcountry(): ccys = currencydb() for ccy in ccys.values(): - if ccy.code != 'XBT': + if ccy.code != "XBT": assert ccy.code[:2] == ccy.default_country @@ -34,46 +42,46 @@ def test_2letters(): def test_new_country(): with pytest.raises(CountryError): - set_new_country('EU', 'EUR', 'Eurozone') + set_new_country("EU", "EUR", "Eurozone") def test_eurozone(): assert len(eurozone) == 19 for c in eurozone: - assert countryccy(c) == 'EUR' + assert countryccy(c) == "EUR" def test_countryccy(): - assert 'AUD' == countryccy('au') - assert 'EUR' == countryccy('eu') + assert "AUD" == countryccy("au") + assert "EUR" == countryccy("eu") def test_ccy_pair(): - p = ccypair('usdchf') - assert str(p) == 'USDCHF' + p = ccypair("usdchf") + assert str(p) == "USDCHF" p = p.over() - assert str(p) == 'CHFUSD' - p = ccypair('EURUSD') + assert str(p) == "CHFUSD" + p = ccypair("EURUSD") assert p == p.over() def test_pickle(): - c = currency('eur') + c = currency("eur") cd = pickle.dumps(c) c2 = pickle.loads(cd) assert c == c2 - assert c != 'EUR' + assert c != "EUR" def test_json(): - c = currency('eur') + c = currency("eur") info = c.info() json.dumps(info) def test_swap(): - c1 = currency('eur') - c2 = currency('chf') + c1 = currency("eur") + c2 = currency("chf") inv, a1, a2 = c1.swap(c2) assert not inv assert c1 == a1 @@ -85,17 +93,17 @@ def test_swap(): def test_as_cross(): - c1 = currency('eur') - c2 = currency('chf') - assert c1.as_cross() == 'EURUSD' - assert c2.as_cross() == 'USDCHF' - assert c1.as_cross('/') == 'EUR/USD' - assert c2.as_cross('/') == 'USD/CHF' + c1 = currency("eur") + c2 = currency("chf") + assert c1.as_cross() == "EURUSD" + assert c2.as_cross() == "USDCHF" + assert c1.as_cross("/") == "EUR/USD" + assert c2.as_cross("/") == "USD/CHF" def test_print(): stream = StreamIO() - c2 = currency('chf') + c2 = currency("chf") c2.printinfo(stream) value = stream.getvalue() assert value @@ -104,29 +112,29 @@ def test_print(): def test_dump_currency_table(): db = currencydb() table = list(dump_currency_table()) - assert len(table) == len(db)+1 + assert len(table) == len(db) + 1 def test_description(): - c = currency('eur') - assert c.description() == 'EUR / USD Spot Exchange Rate' - c = currency('chf') - assert c.description() == 'USD / CHF Spot Exchange Rate' - c = currency('usd') - assert c.description() == 'Dollar' + c = currency("eur") + assert c.description() == "EUR / USD Spot Exchange Rate" + c = currency("chf") + assert c.description() == "USD / CHF Spot Exchange Rate" + c = currency("usd") + assert c.description() == "Dollar" def test_spot_price(): - c1 = currency('eur') - c2 = currency('gbp') - assert c1.spot(c2, 1.3, 1.6) == 1.3/1.6 - assert c2.spot(c1, 1.6, 1.3) == 1.3/1.6 + c1 = currency("eur") + c2 = currency("gbp") + assert c1.spot(c2, 1.3, 1.6) == 1.3 / 1.6 + assert c2.spot(c1, 1.6, 1.3) == 1.3 / 1.6 def test_currency_pair(): - p = currency_pair('eurgbp') - assert p.ccy1.code == 'EUR' - assert p.ccy2.code == 'GBP' + p = currency_pair("eurgbp") + assert p.ccy1.code == "EUR" + assert p.ccy2.code == "GBP" assert p.mkt() == p - p = currency_pair('gbpeur') + p = currency_pair("gbpeur") assert p.mkt() != p diff --git a/tests/test_dates.py b/tests/test_dates.py index bfd6123..a5f9b83 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -2,8 +2,7 @@ import pytest -from ccy import period, date2juldate, juldate2date, todate -from ccy import date2yyyymmdd, yyyymmdd2date +from ccy import date2juldate, date2yyyymmdd, juldate2date, period, todate, yyyymmdd2date @pytest.fixture() @@ -13,58 +12,58 @@ def dates(): (date(2009, 4, 2), 39905, 20090402, 1238626800), (date(1996, 2, 29), 35124, 19960229, 825552000), (date(1970, 1, 1), 25569, 19700101, 0), - (date(1900, 1, 1), 1, 19000101, None) + (date(1900, 1, 1), 1, 19000101, None), ] def test_period(): - a = period('5Y') + a = period("5Y") assert a.years == 5 - b = period('1y3m') + b = period("1y3m") assert b.years == 1 assert b.months == 3 - c = period('-3m') + c = period("-3m") assert c.years == 0 assert c.months == -3 def test_add_period(): - a = period('4Y') - b = period('1Y3M') + a = period("4Y") + b = period("1Y3M") c = a + b assert c.years == 5 assert c.months == 3 def test_add_string(): - a = period('4y') - assert a+'3m' == period('4y3m') - assert '3m'+a == period('4y3m') + a = period("4y") + assert a + "3m" == period("4y3m") + assert "3m" + a == period("4y3m") def test_subtract_period(): - a = period('4Y') - b = period('1Y') + a = period("4Y") + b = period("1Y") c = a - b assert c.years == 3 assert c.months == 0 - c = period('3Y') - period('1Y3M') + c = period("3Y") - period("1Y3M") assert c.years == 1 assert c.months == 9 - assert str(c) == '1Y9M' + assert str(c) == "1Y9M" def test_subtract_string(): - a = period('4y') - assert a-'3m' == period('3y9m') - assert '5y'-a == period('1y') - assert '3m'-a == period('-3y9m') + a = period("4y") + assert a - "3m" == period("3y9m") + assert "5y" - a == period("1y") + assert "3m" - a == period("-3y9m") def test_compare(): - a = period('4Y') - b = period('4Y') - c = period('1Y2M') + a = period("4Y") + b = period("4Y") + c = period("1Y2M") assert a == b assert a >= b assert a <= b @@ -72,47 +71,47 @@ def test_compare(): assert c < a assert (c == a) is False assert (c >= b) is False - assert c > a-b + assert c > a - b def test_week(): - p = period('7d') + p = period("7d") assert p.weeks == 1 - assert str(p) == '1W' + assert str(p) == "1W" p.add_weeks(3) assert p.weeks == 4 - assert str(p) == '4W' + assert str(p) == "4W" assert not p.isempty() - p = period('3w2d') + p = period("3w2d") assert not p.isempty() assert p.weeks == 3 - assert str(p) == '3W2D' + assert str(p) == "3W2D" def test_empty(): - assert not period('3y').isempty() - assert not period('1m').isempty() - assert not period('3d').isempty() + assert not period("3y").isempty() + assert not period("1m").isempty() + assert not period("3d").isempty() assert period().isempty() def test_addperiod(): - p = period('3m') - a = period('6m') + p = period("3m") + a = period("6m") assert a.add_tenure(p) == a - assert str(a) == '9M' + assert str(a) == "9M" def test_error(): with pytest.raises(ValueError): - period('5y6g') + period("5y6g") def test_simple(): - assert period('3m2y').simple() == '27M' - assert period('-3m2y').simple() == '-27M' - assert period('3d2m').simple() == '63D' - assert period('2y').simple() == '2Y' + assert period("3m2y").simple() == "27M" + assert period("-3m2y").simple() == "-27M" + assert period("3d2m").simple() == "63D" + assert period("2y").simple() == "2Y" def test_date2JulDate(dates): @@ -148,7 +147,8 @@ def test_Juldate2datetime(): def test_string(): target = date(2014, 1, 5) - assert todate('2014 Jan 05') == target + assert todate("2014 Jan 05") == target + # def testDate2Timestamp(): # for d,jd,y,ts in .dates: diff --git a/tests/test_tcs.py b/tests/test_tcs.py index ec42c28..da24da2 100644 --- a/tests/test_tcs.py +++ b/tests/test_tcs.py @@ -2,7 +2,7 @@ import pytest -from ccy.tradingcentres import nextbizday, prevbizday, centres +from ccy.tradingcentres import centres, nextbizday, prevbizday @pytest.fixture() @@ -34,6 +34,6 @@ def test_prevBizDay(dates): def test_TGT(): - tcs = centres('TGT') + tcs = centres("TGT") assert not tcs.isbizday(datetime.date(2009, 12, 25)) assert not tcs.isbizday(datetime.date(2010, 1, 1)) From f0931b674c53022ed156656dcd776bad4586cb89 Mon Sep 17 00:00:00 2001 From: Luca Sbardella Date: Sun, 4 Aug 2019 11:09:13 +0100 Subject: [PATCH 2/2] Drop python 3.5 support --- .circleci/config.yml | 13 ----------- README.rst | 2 +- ccy/dates/period.py | 3 ++- setup.py | 54 ++++++++++++++++++++++---------------------- 4 files changed, 30 insertions(+), 42 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cc8c960..38475a6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,16 +1,5 @@ version: 2 jobs: - test35: - docker: - - image: circleci/python:3.5 - steps: - - checkout - - run: - name: install - command: sudo ./dev/install.sh - - run: - name: test - command: pytest test36: docker: - image: circleci/python:3.6 @@ -68,12 +57,10 @@ workflows: build-deploy: jobs: - tests - - test35 - test36 - deploy-release: requires: - tests - - test35 - test36 filters: branches: diff --git a/README.rst b/README.rst index ecaf745..6c8feaa 100644 --- a/README.rst +++ b/README.rst @@ -6,7 +6,7 @@ join and add more. :Package: |license| |pyversions| |status| |downloads| :CI: |master-build| |coverage-master| :Dowloads: https://pypi.org/project/ccy/ -:Source: https://github.com/lsbardel/ccy +:Source: https://github.com/quantmind/ccy .. |pyversions| image:: https://img.shields.io/pypi/pyversions/ccy.svg :target: https://pypi.org/project/ccy/ diff --git a/ccy/dates/period.py b/ccy/dates/period.py index efe5cd7..02eabad 100644 --- a/ccy/dates/period.py +++ b/ccy/dates/period.py @@ -131,7 +131,8 @@ def add_tenure(self, pstr): self.add_months(v) elif p == "Y": self.add_years(v) - st = st[ip + 1 :] + ip += 1 + st = st[ip:] return self def __add__(self, other): diff --git a/setup.py b/setup.py index f440f11..4c17c61 100644 --- a/setup.py +++ b/setup.py @@ -1,12 +1,13 @@ import os -from setuptools import setup, find_packages + +from setuptools import find_packages, setup import ccy def read(name): filename = os.path.join(os.path.dirname(__file__), name) - with open(filename, encoding='utf8') as fp: + with open(filename, encoding="utf8") as fp: return fp.read() @@ -14,13 +15,13 @@ def requirements(name): install_requires = [] dependency_links = [] - for line in read(name).split('\n'): - if line.startswith('-e '): + for line in read(name).split("\n"): + if line.startswith("-e "): link = line[3:].strip() - if link == '.': + if link == ".": continue dependency_links.append(link) - line = link.split('=')[1] + line = link.split("=")[1] line = line.strip() if line: install_requires.append(line) @@ -28,40 +29,39 @@ def requirements(name): return install_requires, dependency_links -install_requires = requirements('dev/requirements.txt')[0] -tests_require = requirements('dev/requirements-dev.txt')[0] +install_requires = requirements("dev/requirements.txt")[0] +tests_require = requirements("dev/requirements-dev.txt")[0] meta = dict( - name='ccy', + name="ccy", version=ccy.__version__, description=ccy.__doc__, - author='Luca Sbardella', + author="Luca Sbardella", author_email="luca@quantmind.com", maintainer_email="luca@quantmind.com", - url="https://github.com/lsbardel/ccy", + url="https://github.com/quantmind/ccy", license="BSD", - long_description=read('README.rst'), - packages=find_packages(include=['ccy', 'ccy.*']), + long_description=read("README.rst"), + packages=find_packages(include=["ccy", "ccy.*"]), install_requires=install_requires, tests_require=tests_require, zip_safe=False, classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Plugins', - 'Intended Audience :: Developers', - 'Intended Audience :: Financial and Insurance Industry', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Topic :: Office/Business :: Financial', - 'Topic :: Utilities' - ] + "Development Status :: 5 - Production/Stable", + "Environment :: Plugins", + "Intended Audience :: Developers", + "Intended Audience :: Financial and Insurance Industry", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Office/Business :: Financial", + "Topic :: Utilities", + ], ) -if __name__ == '__main__': +if __name__ == "__main__": setup(**meta)