Code Monkey home page Code Monkey logo

xil's Introduction

PyPI Python Versions

Tests pre-commit Poetry Ruff Code style: black Imports: isort linting: pylint Checked with mypy

XIL

Gather and compare foreign currency exchange buy and sell rates offered by Israeli banks.

Banks data

The XIL project supports the following banks:

Bank and data source XIL module Tests Bank name (Hebrew)
Bank Leumi Le-Israel leumi בנק לאומי לישראל
Bank Hapoalim poalim בנק הפועלים
Mizrahi Tefahot Bank mizrahi_tefahot בנק מזרחי טפחות
Israel Discount Bank discount בנק דיסקונט לישראל
First International Bank of Israel fibi הבנק הבינלאומי הראשון לישראל
Bank of Jerusalem jerusalem בנק ירושלים
Mercantile Discount Bank mercantile בנק מרכנתיל דיסקונט
Bank Massad massad בנק מסד
One Zero Digital Bank onezero וואן זירו הבנק הדיגיטלי
Bank of Israel boi בנק ישראל

For the data sources (websites and URLs) for each bank, see the docstring of the corresponding XIL module.

Banks that are not supported yet:

Installation

The project requires Python 3.12 or above. To install the project, run:

pip install xil

Contributing to the XIL project

Please read the Contribution Guide.

Similar projects

xil's People

Contributors

dependabot[bot] avatar jond01 avatar

Watchers

 avatar

xil's Issues

Add unit tests for each module

Test by fetching the information from the web (no mocks). This method compromises on using external resources in the tests.
Later to be separated into 1) mocks and 2) live tests.

Add Bank of Israel (BOI) support

They have plain data on:

And XML API is explained on:

For example:
https://www.boi.org.il/currency.xml?rdate=20221125

yields the following XML:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<CURRENCIES>
  <LAST_UPDATE>2022-11-25</LAST_UPDATE>
  <CURRENCY>
    <NAME>Dollar</NAME>
    <UNIT>1</UNIT>
    <CURRENCYCODE>USD</CURRENCYCODE>
    <COUNTRY>USA</COUNTRY>
    <RATE>3.419</RATE>
    <CHANGE>-0.029</CHANGE>
  </CURRENCY>
  <CURRENCY>
    <NAME>Pound</NAME>
    <UNIT>1</UNIT>
    <CURRENCYCODE>GBP</CURRENCYCODE>
    <COUNTRY>Great Britain</COUNTRY>
    <RATE>4.1313</RATE>
    <CHANGE>-0.323</CHANGE>
  </CURRENCY>
  <CURRENCY>
    <NAME>Yen</NAME>
    <UNIT>100</UNIT>
    <CURRENCYCODE>JPY</CURRENCYCODE>
    <COUNTRY>Japan</COUNTRY>
    <RATE>2.4524</RATE>
    <CHANGE>-0.781</CHANGE>
  </CURRENCY>
  <CURRENCY>
    <NAME>Euro</NAME>
    <UNIT>1</UNIT>
    <CURRENCYCODE>EUR</CURRENCYCODE>
    <COUNTRY>EMU</COUNTRY>
    <RATE>3.5558</RATE>
    <CHANGE>-0.056</CHANGE>
  </CURRENCY>
  <CURRENCY>
    <NAME>Dollar</NAME>
    <UNIT>1</UNIT>
    <CURRENCYCODE>AUD</CURRENCYCODE>
    <COUNTRY>Australia</COUNTRY>
    <RATE>2.3033</RATE>
    <CHANGE>-0.152</CHANGE>
  </CURRENCY>
  <CURRENCY>
    <NAME>Dollar</NAME>
    <UNIT>1</UNIT>
    <CURRENCYCODE>CAD</CURRENCYCODE>
    <COUNTRY>Canada</COUNTRY>
    <RATE>2.5587</RATE>
    <CHANGE>-0.105</CHANGE>
  </CURRENCY>
  <CURRENCY>
    <NAME>krone</NAME>
    <UNIT>1</UNIT>
    <CURRENCYCODE>DKK</CURRENCYCODE>
    <COUNTRY>Denmark</COUNTRY>
    <RATE>0.4781</RATE>
    <CHANGE>-0.042</CHANGE>
  </CURRENCY>
  <CURRENCY>
    <NAME>Krone</NAME>
    <UNIT>1</UNIT>
    <CURRENCYCODE>NOK</CURRENCYCODE>
    <COUNTRY>Norway</COUNTRY>
    <RATE>0.3448</RATE>
    <CHANGE>0.378</CHANGE>
  </CURRENCY>
  <CURRENCY>
    <NAME>Rand</NAME>
    <UNIT>1</UNIT>
    <CURRENCYCODE>ZAR</CURRENCYCODE>
    <COUNTRY>South Africa</COUNTRY>
    <RATE>0.1994</RATE>
    <CHANGE>-0.747</CHANGE>
  </CURRENCY>
  <CURRENCY>
    <NAME>Krona</NAME>
    <UNIT>1</UNIT>
    <CURRENCYCODE>SEK</CURRENCYCODE>
    <COUNTRY>Sweden</COUNTRY>
    <RATE>0.3281</RATE>
    <CHANGE>0.336</CHANGE>
  </CURRENCY>
  <CURRENCY>
    <NAME>Franc</NAME>
    <UNIT>1</UNIT>
    <CURRENCYCODE>CHF</CURRENCYCODE>
    <COUNTRY>Switzerland</COUNTRY>
    <RATE>3.6188</RATE>
    <CHANGE>-0.229</CHANGE>
  </CURRENCY>
  <CURRENCY>
    <NAME>Dinar</NAME>
    <UNIT>1</UNIT>
    <CURRENCYCODE>JOD</CURRENCYCODE>
    <COUNTRY>Jordan</COUNTRY>
    <RATE>4.8228</RATE>
    <CHANGE>0.108</CHANGE>
  </CURRENCY>
  <CURRENCY>
    <NAME>Pound</NAME>
    <UNIT>10</UNIT>
    <CURRENCYCODE>LBP</CURRENCYCODE>
    <COUNTRY>Lebanon</COUNTRY>
    <RATE>0.0226</RATE>
    <CHANGE>0</CHANGE>
  </CURRENCY>
  <CURRENCY>
    <NAME>Pound</NAME>
    <UNIT>1</UNIT>
    <CURRENCYCODE>EGP</CURRENCYCODE>
    <COUNTRY>Egypt</COUNTRY>
    <RATE>0.1391</RATE>
    <CHANGE>0</CHANGE>
  </CURRENCY>
</CURRENCIES>

It's a relatively low-priority task as most banks list the official rates (שערים יציגים) themselves, which are the BOI rates.

Share code between the modules and use classes (OOD)

PR #12, which solved issue #5, introduced many code duplications between the different modules.
Instead of rewriting the same lines repeatedly, design classes, implement proper inheritance structure, and use it in the bank scrapers.

Test the banks list against BOI data

One Zero bank test failed

https://github.com/jond01/xil/actions/runs/6708383613

After a quick examination - there are probably two separate issues.

Need to understand if the following assertions are correct:

assert (
df[("transfer", "buy")] < df[("currency", "official rate")]
).all(), "The buy rate is not lower than the official rate"
assert (
df[("currency", "official rate")] < df[("transfer", "sell")]
).all(), "The sell rate is not higher than the official rate"

The "official rate" is probably updated once a day, so the sell/buy rates may not respect the assumed relations at all times.

The raw JSON from a simple scraping is different than the one I see on the web browser.
The scraped one, which is used in XIL, can be obtained with:

import urllib.request

with urllib.request.urlopen("https://dv16ymfyh91nr.cloudfront.net/MarketingRatesReport/MarketingSiteFCYRatesCurrentReport.json") as f:
    print(f.read().decode())

The difference is:

{
    "generatingReportDateTime": "2023-10-31",
    "marketingRecords": [
        {
             "fromCurrency": "EUR",
             "toCurrency": "ILS",
             "representativeExchangeRate": 0.2329,
-            "buyRate": 0.2326,
-            "sellRate": 0.2349
+            "buyRate": 0.2318,
+            "sellRate": 0.2341
         },
         {
             "fromCurrency": "ILS",
             "toCurrency": "EUR",
             "representativeExchangeRate": 4.2934,
-            "buyRate": 4.2573,
-            "sellRate": 4.3001
+            "buyRate": 4.2711,
+            "sellRate": 4.3141
         },
         {
             "fromCurrency": "USD",
             "toCurrency": "ILS",
             "representativeExchangeRate": 0.2466,
-            "buyRate": 0.2467,
-            "sellRate": 0.2492
+            "buyRate": 0.2457,
+            "sellRate": 0.2482
         },
         {
             "fromCurrency": "ILS",
             "toCurrency": "USD",
             "representativeExchangeRate": 4.0550,
-            "buyRate": 4.0135,
-            "sellRate": 4.0538
+            "buyRate": 4.0288,
+            "sellRate": 4.0693
         }
     ]
 }

Need to understand where does this difference data come from.

Cache pre-commit hooks' environment in the tests workflow

Following #35 (which solved #29), the pre-commit job added to the total time of the "tests" workflow.
The step that added the most time is:

- name: Run pre-commit
run: poetry run pre-commit run --all-files

Which installs the hooks - initializes the environments, and runs them.
For instance: https://github.com/jond01/xil/actions/runs/4091900465/jobs/7056277242#step:6:12

To save time and resources, cache the environment of the hooks:

The cache location (on ubuntu-latest) is: /home/runner/.cache/pre-commit/.

Add a `__version__` attribute to the package (PEP 396)

xil/__init__.py should contain a __version__ attribute.
It has to match the tool.poetry.version key in pyproject.toml (and ideally with the recent Git tag):

version = "0.1.0"

https://peps.python.org/pep-0396/

There are several ways to solve the single source of truth issues with Poetry.
Some options are:

In some other build systems, this problem is solved:

Fix BOI banks list

The BOI updated their website, and the link:

BOI_XML_URL = "\
https://www.boi.org.il/en/BankingSupervision/BanksAndBranchLocations/Lists/BoiBankBranchesDocs/banking_corporations_en.xml"

No longer works.

The relevant pages are:

The relevant data as was downloaded from the currently down URL on Nov 25, 22:
banking_corporations_en.xls

Category Bank Code Name
COMMERCIAL BANKS 12 Bank Hapoalim B.M
COMMERCIAL BANKS 10 Bank Leumi Le-Israel B.M
COMMERCIAL BANKS 46 Bank Massad Ltd
COMMERCIAL BANKS 4 Bank Yahav for Government Employees Ltd
COMMERCIAL BANKS 54 Bank of Jerusalem Ltd
COMMERCIAL BANKS 11 Israel Discount Bank Ltd
COMMERCIAL BANKS 17 Mercantile Discount Bank ltd
COMMERCIAL BANKS 20 Mizrahi Tefahot Bank Ltd
COMMERCIAL BANKS 18 One Zero Digital Bank LTD
COMMERCIAL BANKS 31 The First International Bank of Israel Ltd
COMMERCIAL BANKS 13 Union Bank of Israel Ltd
JOINT SERVICE COMPANIES 50 Bank Clearing Center Ltd
FOREIGN BANKS 27 Barclays Bank PLC
FOREIGN BANKS 22 Citibank N.A
FOREIGN BANKS 23 HSBC BANK plc
FOREIGN BANKS 39 State Bank Of India

(Only the relevant columns are shown above.)

Consider adding a skip or removing the test.

Address Geoblocking

A few of the APIs are blocked outside of Israel.
It should be documented and accompanied by an informative error message.

Bank Hapoalim API requires a functional cookie

After a few requests from the same IP, the following URL is blocked:
https://www.bankhapoalim.co.il/he/coin-rates
With the following uninformative error:

urllib.error.HTTPError: HTTP Error 302: The HTTP server returned a redirect error that would lead to an infinite loop.
The last 30x error message was:
Found

Try with:

import pandas as pd

pd.read_json("https://www.bankhapoalim.co.il/he/coin-rates")

Run it a few times.

After a quick research, I found the following cookies are required ("functional" cookies): incap_ses_***_*******.
One can get them from a web-browser.
For example, the following currently works for me:

pd.read_json(
    "https://www.bankhapoalim.co.il/he/coin-rates",
    storage_options={
        "Cookie": " ".join(
            """\
incap_ses_1255_2405596=a/GzCf3fvnceYM2cLahqEUXj6mUAAAAAtfv8l97VCsPJsew65Qa8XA==;
incap_ses_1255_2405249=DKE/HuRYLSgUMtWcLahqEf/t6mUAAAAArYjjw97q1DiiXLULf6cJJw==;
incap_ses_1255_2405640=wc+ZOPCR9VzEeB+dLahqEfF762UAAAAAk7Vee7dBohFS/K9LTiy3wg==
            """.splitlines()
        )
    },
)

Only the last one is necessary.

We need to find a way to overcome it or allow users to insert their cookie conveniently.

Save caches in the CI (mypy, black, ruff)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.