Skip to content

Commit

Permalink
Add static typing check with mypy (#158)
Browse files Browse the repository at this point in the history
* Added mypy dependency

* Corrected mypy errors and changed absolute imports to relative

* Fixed import order

* Corrected paths

* Fixed import order
  • Loading branch information
ilcardella committed Aug 2, 2020
1 parent 0b58a92 commit 99bb698
Show file tree
Hide file tree
Showing 45 changed files with 779 additions and 555 deletions.
2 changes: 2 additions & 0 deletions .mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[mypy]
ignore_missing_imports = True
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added Makefile to perform development and deployment actions
- Added formatting with black and isort
- Linting with flake8
- Static types checking with mypy

## Removed
- Removed `setup.py` with full usage of `pyproject.toml`
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ endif

INSTALL_DIR = ${HOME}/.TradingMate
DATA_DIR = $(INSTALL_DIR)/data
GTK_ASSETS_DIR = $(DATA_DIR)/assets/gtk
GTK_ASSETS_DIR = $(DATA_DIR)/assets
CONFIG_DIR = $(INSTALL_DIR)/config
LOG_DIR = $(INSTALL_DIR)/log

Expand Down Expand Up @@ -34,7 +34,7 @@ install-system: clean
> mkdir -p $(LOG_DIR)
> cp config/config.json $(CONFIG_DIR)
> cp data/trading_log.json $(DATA_DIR)
> cp dtradingmate/UI/assets/gtk/*.glade $(GTK_ASSETS_DIR)
> cp -r tradingmate/ui/assets/gtk $(GTK_ASSETS_DIR)

build: clean
> poetry build
Expand All @@ -53,7 +53,7 @@ black:

format: isort black

lint: flake8 #mypy
lint: flake8 mypy

check: format lint test

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ AlphaVantage is great collection of API that provide several feature. It require

- Visit AlphaVantage website: `https://www.alphavantage.co`
- Request a free api key
- Insert these info in a file called `.credentials` in `${HOME}/.TradingMate/data`
- Insert these info in a file called `.credentials` in `${HOME}/.TradingMate/config`
```
touch ${HOME}/.TradingMate/data/.credentials
touch ${HOME}/.TradingMate/config/.credentials
```

This must be in json format and contain:
Expand All @@ -55,7 +55,7 @@ AlphaVantage is great collection of API that provide several feature. It require
- Revoke permissions to read the file by others

```
sudo chmod 600 ${HOME}/.TradingMate/data/.credentials
sudo chmod 600 ${HOME}/.TradingMate/config/.credentials
```

### YFinance
Expand Down
4 changes: 2 additions & 2 deletions config/config.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"trading_logs": [
"/opt/TradingMate/data/trading_log.json"
"{home}/.TradingMate/data/trading_log.json"
],
"general": {
"credentials_filepath": "/opt/TradingMate/data/.credentials",
"credentials_filepath": "{home}/.TradingMate/config/.credentials",
"polling_period_sec": 30,
"stocks_interface": {
"active": "yfinance",
Expand Down
47 changes: 45 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ pytest = "^6.0.0"
black = {version = "^19.10b0", allow-prereleases = true}
isort = {version = "^5.2.1", allow-prereleases = true}
flake8 = "^3.8.3"
mypy = "^0.782"

[tool.poetry.scripts]
trading_bot = 'tradingmate:main'
trading_mate = 'tradingmate:main'

[tool.black]
line-length = 88
Expand Down
14 changes: 8 additions & 6 deletions test/test_configuration_manager.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
from pathlib import Path

import pytest

from tradingmate.model import ConfigurationManager


@pytest.fixture
def cm():
return ConfigurationManager("test/test_data/config.json")
return ConfigurationManager(Path("test/test_data/config.json"))


def test_config_values(cm):
config = cm.get_trading_database_path()
assert isinstance(config, list)
assert len(config) == 3
assert config[0] == "test/test_data/trading_log.json"
assert config[1] == "test/test_data/trading_log.json"
assert config[2] == "test/test_data/trading_log.json"
assert config[0] == Path("test/test_data/trading_log.json")
assert config[1] == Path("test/test_data/trading_log.json")
assert config[2] == Path("test/test_data/trading_log.json")

config = cm.get_credentials_path()
assert isinstance(config, str)
assert config == "test/test_data/.credentials"
assert isinstance(config, Path)
assert config == Path("test/test_data/.credentials")

config = cm.get_polling_period()
assert isinstance(config, float)
Expand Down
21 changes: 11 additions & 10 deletions test/test_db_handler.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
from pathlib import Path

import pytest

Expand All @@ -7,12 +8,12 @@

@pytest.fixture
def configuration():
return ConfigurationManager("test/test_data/config.json")
return ConfigurationManager(Path("test/test_data/config.json"))


@pytest.fixture
def dbh(configuration):
return DatabaseHandler(configuration, "test/test_data/trading_log.json")
return DatabaseHandler(configuration, Path("test/test_data/trading_log.json"))


def test_read_data(dbh):
Expand All @@ -22,31 +23,31 @@ def test_read_data(dbh):
assert len(dbh.trading_history) > 0
dbh.trading_history = []
assert len(dbh.trading_history) == 0
dbh.read_data("test/test_data/trading_log.json")
dbh.read_data(Path("test/test_data/trading_log.json"))
assert len(dbh.trading_history) > 0


def test_write_data(dbh):
"""
Test write data into json file
"""
mock_path = "/tmp/test.json"
if os.path.exists(mock_path):
mock_path = Path("/tmp/test.json")
if mock_path.exists():
os.remove(mock_path)
assert not os.path.isfile(mock_path)
assert not mock_path.exists()
assert dbh.write_data(mock_path)
assert os.path.isfile(mock_path)
assert mock_path.is_file()


def test_get_db_filepath(dbh):
"""
Test it returns the correct filepath
"""
assert dbh.get_db_filepath() == "test/test_data/trading_log.json"
assert str(dbh.get_db_filepath()) == "test/test_data/trading_log.json"

mock_path = "/tmp/test.json"
mock_path = Path("/tmp/test.json")
assert dbh.write_data(mock_path)
assert os.path.isfile(mock_path)
assert mock_path.is_file()
dbh.read_data(mock_path)
assert dbh.get_db_filepath() == mock_path

Expand Down
20 changes: 11 additions & 9 deletions test/test_portfolio.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import json
import os
import time
from pathlib import Path

import pytest

from tradingmate.model import ConfigurationManager, Portfolio, Trade

# These variables are based on the content of the test trading log
PF_CASH_AVAILABLE = 2465.0343736000013
PF_CASH_AVAILABLE = 2461.314373600001
PF_CASH_DEPOSITED = 7700
PF_MOCK13_QUANTITY = 1192
PF_MOCK4_QUANTITY = 438
PF_MOCK13_LAST_PRICE = 1245.02
PF_MOCK4_LAST_PRICE = 1245.02
PF_MOCK13_OPEN_PRICE = 166.984
PF_MOCK4_OPEN_PRICE = 582.9117
PF_TOTAL_VALUE = 22758.8603736
PF_TOTAL_VALUE = 22755.140373600003
PF_HOLDINGS_VALUE = 20293.826
PF_PL = 15058.8603736
PF_PL_PERC = 195.56961524155844
PF_PL = 15055.140373600003
PF_PL_PERC = 195.52130355324678
PF_POSITIONS_PL = 15750.223473999999
PF_POSITIONS_PL_PERC = 346.64615542121044

Expand Down Expand Up @@ -50,7 +51,7 @@ def portfolio(requests_mock):
requests_mock.get(URL_13, status_code=200, json=data_13)
requests_mock.get(URL_4, status_code=200, json=data_4)
# Use test configuration file
config = ConfigurationManager("test/test_data/config.json")
config = ConfigurationManager(Path("test/test_data/config.json"))
return Portfolio(config, config.get_trading_database_path()[0])


Expand Down Expand Up @@ -164,12 +165,13 @@ def test_get_trade_history(portfolio):


def test_save_portfolio(portfolio):
filepath = "/tmp/TradingMate_test_save_portfolio.json"
if os.path.exists(filepath):
filepath = Path("/tmp/TradingMate_test_save_portfolio.json")
if filepath.exists():
os.remove(filepath)
assert not filepath.exists()
portfolio.save_portfolio(filepath)
assert os.path.exists(filepath)
config = ConfigurationManager("test/test_data/config.json")
assert filepath.exists()
config = ConfigurationManager(Path("test/test_data/config.json"))
new_pf = Portfolio(config, filepath)
test_get_total_value(new_pf)

Expand Down
17 changes: 9 additions & 8 deletions test/test_trading_mate.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json
import os
import re
from pathlib import Path

import pytest

Expand Down Expand Up @@ -34,7 +34,7 @@ def trading_mate(requests_mock):
with open("test/test_data/mock_yf_quote.html", "r") as f:
requests_mock.get(URL_YF_HTML_MOCK, status_code=200, body=f)
# Create the TradingMate instance using the test config file
return TradingMate("test/test_data/config.json", "/tmp/trading_mate_test_log.log")
return TradingMate(Path("test/test_data/config.json"))


def test_get_portfolios(trading_mate):
Expand Down Expand Up @@ -117,17 +117,17 @@ def test_delete_trade_event(trading_mate):
def test_open_portfolio_event(trading_mate):
assert len(trading_mate.get_portfolios()) == 3
with pytest.raises(Exception):
trading_mate.open_portfolio_event("/tmp/non_existing_file.json")
trading_mate.open_portfolio_event("test/test_data/trading_log.json")
trading_mate.open_portfolio_event(Path("/tmp/non_existing_file.json"))
trading_mate.open_portfolio_event(Path("test/test_data/trading_log.json"))
assert len(trading_mate.get_portfolios()) == 4


def test_save_portfolio_event(trading_mate):
for pf in trading_mate.get_portfolios():
temp_file = f"/tmp/new_trading_log{pf.get_id()}.json"
assert os.path.exists(temp_file) is False
temp_file = Path(f"/tmp/new_trading_log{pf.get_id()}.json")
assert not temp_file.exists()
trading_mate.save_portfolio_event(pf.get_id(), temp_file)
assert os.path.exists(temp_file)
assert temp_file.exists()


# def test_get_settings_event(trading_mate):
Expand All @@ -140,7 +140,8 @@ def test_save_portfolio_event(trading_mate):


def test_get_app_log_filepath(trading_mate):
assert trading_mate.get_app_log_filepath() == "/tmp/trading_mate_test_log.log"
# The log path is generated based on a timestamp and the install directory
assert "/.TradingMate/log/trading_mate_" in str(trading_mate.get_app_log_filepath())


# This is commented out because this function depends on pip being installed
Expand Down
6 changes: 3 additions & 3 deletions tradingmate/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from tradingmate.TradingMate import TradingMate
from .trading_mate import TradingMate


def main():
def main() -> None:
# Initialise the business logic
tm = TradingMate()
from tradingmate.ui.gtk.UIHandler import UIHandler
from .ui.gtk import UIHandler

UIHandler(tm).start()

0 comments on commit 99bb698

Please sign in to comment.