diff --git a/.bumpversion.cfg b/.bumpversion.cfg index f1e38f02..dcb5f756 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,9 +1,11 @@ [bumpversion] -current_version = 0.8.0 -commit = True -tag = True +current_version = 0.8.1 +commit = False +tag = False tag_name = {new_version} [bumpversion:file:setup.cfg] [bumpversion:file:docs/source/conf.py] + +[bumpversion:file:obfuscate.sh] diff --git a/.circleci/config.yml b/.circleci/config.yml index 3076cbe6..a80dc918 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,10 +4,11 @@ workflows: version: 2 main: jobs: - - run_tests + - test-code + - test-docs jobs: - run_tests: + test-code: docker: - image: circleci/python:3.8 steps: @@ -39,3 +40,27 @@ jobs: - store_artifacts: path: test-reports + test-docs: + docker: + - image: circleci/python:3.8 + steps: + - checkout + - run: + name: Install requirements + command: | + python3 -m venv venv + . venv/bin/activate + pip install -r requirements.txt + pip install -r docs/source/requirements.txt + + - run: + name: Build documentation + command: | + . venv/bin/activate + make -C docs html + + - run: + name: Run doctests + command: | + . venv/bin/activate + make -C docs doctest diff --git a/README.md b/README.md index eea95f0e..8e6c32f8 100644 --- a/README.md +++ b/README.md @@ -14,37 +14,44 @@ ArbitrageLab is a python library that enables traders who want to exploit mean-reverting portfolios by providing a complete set of algorithms from the best academic journals. -## Documentation, Example Notebooks and Lecture Videos -For every technique present in the library we not only provide extensive documentation, with both theoretical explanations -and detailed descriptions of available functions, but also supplement the modules with ever-growing array of lecture videos and slides -on the implemented methods. - -We want you to be able to use the tools right away. To achieve that, every module comes with a number of example notebooks -which include detailed examples of the usage of the algorithms. Our goal is to show you the whole pipeline, starting from -importing the libraries and ending with strategy performance metrics so you can get the added value from the get-go. -## Licensing options -This project is licensed under an all rights reserved [licence](https://github.com/hudson-and-thames/mlfinlab/blob/master/LICENSE.txt). +## Development -* Business -* Enterprise +### Creating a release -## Community -With the purchase of the library, our clients get access to the Hudson & Thames Slack community, where our engineers and other quants -are always ready to answer your questions. +- Create `release/` branch +- Bump versions throughout source files (we use `bump2version` to do automate this process, TODO: Add instructions) +- Update customer install instructions in documentation source files +- Update release information in changelog in documentation source files +- Open PR from `release` branch into `develop` +- Merge PR once approved +- Obfuscate `develop` using PyArmor (instructions are located elsewhere in this README) +- Test you can install the wheel from a fresh environment +- Merge `develop` into `master` +- Upload the obfuscated wheel to the Hudson & Thames Clients organization +- Tag the commit with the version number +- Write a blog post announcing the release +- Send a newsletter email +- Post on social media -Alternatively, you can email us at: research@hudsonthames.org. +### Bumping version numbers using `bump2version` -
- - - -
+We use `bump2version` to automatically bump versions throughout source files. + +Configuration lives in the `.bumpversion.cfg` file. To run `bump2version`, first install it via `pip`: + +``` sh +pip install --upgrade bump2version +``` + +And then bump the version: + +``` sh +bump2version +``` -## Who is Hudson & Thames? -Hudson and Thames Quantitative Research is a company with the goal of bridging the gap between the advanced research developed in -quantitative finance and its practical application. We have created three premium python libraries so you can effortlessly access the -latest techniques and focus on what matters most: **creating your own winning strategy**. +where `` tells you which version to be bumped. The acceptable +values are `major`, `minor` or `patch`, conforming to the semantic versioning +pattern: `major.minor.patch`. For example, `3.2.7` has a major version of 3, a +minor version of 2 and a patch version of 7. -### What was only possible with the help of huge R&D teams is now at your disposal, anywhere, anytime. diff --git a/arbitragelab/util/segment.py b/arbitragelab/util/segment.py index b09ceb6a..c39a8cbe 100644 --- a/arbitragelab/util/segment.py +++ b/arbitragelab/util/segment.py @@ -82,10 +82,13 @@ def get_mac(): """ Identify the device by MAC Address """ - mac = getmac.get_mac_address() + mac = getmac.get_mac_address(os.environ.get("ARBLAB_MAC_INTERFACE")) if mac is None: - raise Exception(" No MAC Address is found on this device, must have a MAC Address.") + raise ValueError("No MAC Address is found on this device, must have a MAC Address." + " If using a VPN, you might be able to resolve this by specifying" + " the correct interface manually using the PORTLAB_MAC_INTERFACE environment" + " variable.") return mac diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 5ff10742..7a187f71 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -9,8 +9,9 @@ Changelog * :feature:`50` Add a distutils command for marbles * :bug:`58` Fixed test failure on OSX -.. - For Help: https://releases.readthedocs.io/en/latest/index.html +* :release:`0.8.1 <2022-07-25>` +* :support:`91` Allow user to specify network interface for MAC address + * :release:`0.8.0 <2022-06-28>` * :support:`78` Bump dependencies. diff --git a/docs/source/conf.py b/docs/source/conf.py index 456664bb..62bb2cf2 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'Hudson & Thames Quantitative Research' # The full version, including alpha/beta/rc tags -release = '0.8.0' +release = '0.8.1' # -- General configuration --------------------------------------------------- @@ -34,8 +34,8 @@ 'sphinx.ext.autodoc', 'sphinx.ext.coverage', 'sphinx.ext.intersphinx', -# 'sphinx.ext.viewcode', - 'releases' + 'sphinx.ext.doctest', + 'releases', ] diff --git a/docs/source/getting_started/installation.rst b/docs/source/getting_started/installation.rst index b877786b..f38f26fe 100644 --- a/docs/source/getting_started/installation.rst +++ b/docs/source/getting_started/installation.rst @@ -4,223 +4,192 @@ Installation ============ -Recommended Versions -#################### -* Anaconda -* Python 3.8. +API Keys +######## +In order to use ``arbitragelab``, you will require an API key. This is provided to +you when you purchase ArbitrageLab, and can be found on the client portal. If you are unable to find your API key, please reach out to sales@hudsonthames.org. -Installation -############ - -Ubuntu Linux -************ +Recommended Setup on Windows +############################ -0. Set up Git (if you haven't already, the following `link `__ provides a nice guide.) -1. Make sure you install the latest version of the Anaconda distribution. To do this you can follow the install and update instructions found on this `link `_ -2. Launch a terminal -3. Create a New Conda Environment. From terminal. +#. Download and install the latest version of `Anaconda 3 `__ +#. Launch Anaconda Navigator +#. Click Environments, choose an environment name, select Python 3.8, and click Create +#. Click Home, browse to your new environment, and click Install under Jupyter Notebook +#. Launch the Anaconda Prompt and activate the environment: .. code-block:: - conda create -n python=3.8 + conda activate - Accept all the requests to install. - -4. Now activate the environment with: +#. Install ArbitrageLab using ``pip``: .. code-block:: - source activate + pip install https://1fed2947109cfffdd6aaf615ea84a82be897c4b9@raw.githubusercontent.com/hudson-and-thames-clients/arbitragelab/master/arbitragelab-0.8.1-py38-none-any.whl + -5. Contact Hudson & Thames sales team and purchase ArbitrageLab. You will be provided with an API key. +#. Make sure your API key is available in your environment under the ``ARBLAB_API_KEY`` environment variable by opening the Command Prompt as an administrator, and running the following command: .. code-block:: - Example: "26303adb02cb759b2" + setx ARBLAB_API_KEY "`__ is installed, and that you have installed ``cmake``: - 7.2 The Easy Way: - - If you don't want the key to persist on your local machine, you can always declare it each time, before you import ArbitrageLab. + .. code-block:: - * In your python script or notebook, add the following line before you import ArbitrageLab: + brew install cmake - .. code:: - import os - os.environ['ARBLAB_API_KEY'] = "26303adb02cb759b2" - import arbitragelab as al - .. tip:: +#. Install some variant of ``conda`` environment manager (we recommend Anaconda or Miniconda) for your platform. +#. Launch a new terminal and create a new ``conda`` environment using your environment manager: - If you are running Ubuntu on a virtual machine, you may find it easiest to use the ``os.environ`` method. + .. code-block:: -.. tip:: + conda create -n python=3.8 - * If you are having problems with the installation, please ping us on Slack and we will be able to assist. +#. Make sure the environment is activated: + .. code-block:: -Mac OS X -******** + conda activate -0. Set up Git (if you haven't already, the following `link `__ provides a nice guide.) -1. Make sure you install the latest version of the Anaconda distribution. To do this you can follow the install and update instructions found on this `link `_ -2. Launch a terminal -3. Create a New Conda Environment. From terminal. +#. Install ArbitrageLab using ``pip``: .. code-block:: - conda create -n python=3.8 + pip install https://1fed2947109cfffdd6aaf615ea84a82be897c4b9@raw.githubusercontent.com/hudson-and-thames-clients/arbitragelab/master/arbitragelab-0.8.1-py38-none-any.whl - Accept all the requests to install. +#. Make sure your API key is available in your environment under the ``ARBLAB_API_KEY`` environment variable. -4. Now activate the environment with: + If you're running on **Linux or MacOS**, you can add the following to your + ``~/.zshrc``, ``~/.bashrc`` or ``~/.profile`` file in order to make the API + key available: .. code-block:: - source activate + ARBLAB_API_KEY="" -5. Contact Hudson & Thames sales team and purchase ArbitrageLab. You will be provided with an API key. + where you replace ```_. - You can use this `installation guide `_. +.. code-block:: python -0. Set up Git (if you haven't already, the following `link `__ provides a nice guide.) -1. Download and install the latest version of `Anaconda 3 `__ -2. Launch Anaconda Prompt -3. Create new environment (replace with a name, for example ``arbitragelab``): + # Insert this at the start of your script or notebook + import os - .. code-block:: + os.environ["ARBLAB_API_KEY"] = "" - conda create -n python=3.8 -4. Activate the new environment: +Alternative ways of adding the API Key +###################################### - .. code-block:: - - conda activate +.. warning:: -5. Contact Hudson & Thames sales team and purchase ArbitrageLab. You will be provided with an API key. + The following is not recommended for security reasons, since you run the risk + of accidentally leaking your API key in your source code by accidentally + committing it to your version control system. As a result, we highly + recommended using environment variables instead. - .. code-block:: +You can also use the ``os`` module in your Python script or Jupyter notebook +to add the ``ARBLAB_API_KEY`` environment variable before importing +``arbitragelab``: - Example: "26303adb02cb759b2d484233" +.. code-block:: python -6. Install ArbitrageLab into your python environment via the terminal. + # Insert this at the start of your script or notebook + import os - Please make sure to use this exact statement: - - .. code-block:: + os.environ["ARBLAB_API_KEY"] = "" - pip install https://1fed2947109cfffdd6aaf615ea84a82be897c4b9@raw.githubusercontent.com/hudson-and-thames-clients/arbitragelab/master/arbitragelab-0.8.0-py38-none-any.whl -7. Add API key as an environment variable: +Fixing "No MAC Address Found" Errors +#################################### - 7.1 The Best Way: +Depending on your setup, especially when using a VPN service or application, +importing ``arbitragelab`` may cause a "No MAC Address Found" error to be +raised. This occurs when attempting to verify your API key, and occurs when we +are unable to access the MAC address of your computer's network device. This is +usually the result of a virtual network device, associated with your VPN, that +doesn't report a physical MAC address. - By adding the API key as an environment variable, you won't need to constantly add the key every time you import the library. +The workaround is to manually specify your physical hardware network interface +name using another environment variable, ``ARBLAB_MAC_INTERFACE``: - * Open command prompt as an administrator. - * Create the variable: ``setx ARBLAB_API_KEY "26303adb02cb759b2"`` - * Note that you must add your own API key and not the one given in this example. - * Close and open a new command prompt - * Validate that your variable has been added: ``echo %ARBLAB_API_KEY%`` +For Windows: - 7.2 The Easy Way: +.. code-block:: - If you don't want the key to persist on your local machine, you can always declare it each time, before you import ArbitrageLab. + setx ARBLAB_MAC_INTERFACE "" - * In your python script or notebook, add the following line before you import ArbitrageLab: - .. code:: +For Linux / MacOS: - import os - os.environ['ARBLAB_API_KEY'] = "26303adb02cb759b2" - import arbitragelab as al +.. code-block:: -.. tip:: + # In ~/.bashrc, ~/.zshrc, or ~/.profile, depending on platform + ARBLAB_MAC_INTERFACE="" - * If you are having problems with the installation, please ping us on Slack and we will be able to assist. -Important Notes -############### -* You may need to `install Cython `__ if your distribution hasn't already. -* ArbitrageLab requires an internet connection when you import the library. This checks that your API key is valid. -* We have added analytics to the library, please see the analytics tab for more details. +With this environment variable set (remember to ``source`` your rc files if +you're on Linux or MacOS), you should now be able to import the package normally. diff --git a/docs/source/requirements.txt b/docs/source/requirements.txt index 57a91900..47e9ecdd 100644 --- a/docs/source/requirements.txt +++ b/docs/source/requirements.txt @@ -27,15 +27,16 @@ tensorflow-macos==2.12.0; sys_platform == 'darwin' and platform_machine == 'arm6 tensorflow==2.12.0; sys_platform != 'darwin' or platform_machine != 'arm64' werkzeug==2.2.3 yahoo-fin==0.8.9.1 -yfinance==0.2.20 +yfinance==0.2.24 # Develop -coverage==5.4 +coverage==7.2.7 docutils==0.16 # Docs hudsonthames-sphinx-theme==0.1.5 # Docs jinja2<3.1 # Docs pyarmor==8.2.5 # Encryption pylint==2.6.0 +pytest==7.3.1 releases==1.6.3 # Docs sphinx-rtd-theme==0.5.2 # Docs sphinx==3.4.3 # Docs diff --git a/obfuscate.sh b/obfuscate.sh new file mode 100755 index 00000000..56e7c7ef --- /dev/null +++ b/obfuscate.sh @@ -0,0 +1,40 @@ +#! +# This script builds, unpacks, obfuscates, and then rebuilds the python wheel +# package + +# Set the version +# TODO: Do this automatically in the future by reading from setup.cfg (or +# similar) +VERSION=0.8.1 + +# Start clean +rm -rf dist/ + +# Build the wheel +python setup.py bdist_wheel + +# Unpack the wheel +python -m wheel unpack dist/arbitragelab-$VERSION-py3-none-any.whl --dest dist/ + +# Generate a PyArmor license +pyarmor-7 licenses c1_version # --expired 2024-02-01 c1_version <-- use this to make license expire + +# Obfuscate using PyArmor +pyarmor-7 obfuscate \ + --with-license licenses/c1_version/license.lic \ + --platform windows.x86_64 \ + --platform linux.x86_64 \ + --platform darwin.x86_64 \ + --platform darwin.aarch64 \ + --platform linux.x86 \ + --obf-code=0 \ + --recursive \ + --output dist/arbitragelab-$VERSION/arbitragelab arbitragelab/__init__.py + +# Repack wheel +python -m wheel pack dist/arbitragelab-$VERSION --dest dist + +# Clean-up +rm -rf dist/arbitragelab-$VERSION +rm -rf build +rm -rf licenses diff --git a/requirements.txt b/requirements.txt index 3e764870..47e9ecdd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -27,7 +27,7 @@ tensorflow-macos==2.12.0; sys_platform == 'darwin' and platform_machine == 'arm6 tensorflow==2.12.0; sys_platform != 'darwin' or platform_machine != 'arm64' werkzeug==2.2.3 yahoo-fin==0.8.9.1 -yfinance==0.2.20 +yfinance==0.2.24 # Develop coverage==7.2.7 diff --git a/setup.cfg b/setup.cfg index 0e87bfb4..274fb090 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = arbitragelab -version = 0.8.0 +version = 0.8.1 author = Hudson and Thames Quantitative Research author_email = research@hudsonthames.org licence = All Rights Reserved @@ -50,7 +50,7 @@ install_requires = cvxpy==1.3.1 cython==0.29.28 dash==2.10.2 - getmac>=0.8.0, <1.0.0 + getmac>=0.8.1, <1.0.0 jupyter-dash>=0.2.0, <1.0.0 keras==2.12.0 lxml>=4.9.1 diff --git a/setup.py b/setup.py index 1b71025d..6fcb91d3 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -# Copyright 2019, Hudson and Thames Quantitative Research +# Copyright 2023, Hudson and Thames Quantitative Research # All rights reserved # Read more: https://github.com/hudson-and-thames/arbitragtelab/blob/master/LICENSE.txt diff --git a/tests/test_segment.py b/tests/test_segment.py new file mode 100644 index 00000000..4bdcc561 --- /dev/null +++ b/tests/test_segment.py @@ -0,0 +1,42 @@ +"Tests for the `segment` module" + +# pylint: disable=redefined-outer-name + +from unittest import mock + +import pytest + +from arbitragelab.util import segment + +@pytest.fixture +def mock_getmac(): + """Mock for the getmac dependency""" + with mock.patch("arbitragelab.util.segment.getmac") as mock_getmac: + yield mock_getmac + + +def test_get_mac_default_interface(mock_getmac): + """Test if we can get the MAC address for the default interface""" + mock_getmac.get_mac_address.return_value = "MAC_ADDRESS" + + actual_result = segment.get_mac() + expected_result = "MAC_ADDRESS" + assert actual_result == expected_result + mock_getmac.get_mac_address.assert_called_with(None) + +def test_get_mac_custom_interface(monkeypatch, mock_getmac): + """Test if we can get the MAC address for a custom interface""" + monkeypatch.setenv("ARBLAB_MAC_INTERFACE", "CUSTOM_INTERFACE") + mock_getmac.get_mac_address.return_value = "MAC_ADDRESS" + + actual_result = segment.get_mac() + expected_result = "MAC_ADDRESS" + assert actual_result == expected_result + mock_getmac.get_mac_address.assert_called_with("CUSTOM_INTERFACE") + +def test_get_mac_raises_error_if_no_mac_address_found(mock_getmac): # pylint: disable=invalid-name + """Test if get_mac raises an error if a MAC address cannot be found""" + mock_getmac.get_mac_address.return_value = None + + with pytest.raises(ValueError): + segment.get_mac()