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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Few pointers for contributions:

- Create your PR against the `develop` branch, not `master`.
- New features need to contain unit tests and must be PEP8 conformant (max-line-length = 100).
- Creating a feature, must be done on a branch with prefix `feature_`.
- Making a hotfix, must be done on a branch with prefix `hotfix_`.

If you are unsure, discuss the feature on our [Slack](https://join.slack.com/t/investingbots/shared_invite/enQtODgwNTg3MzA2MjYyLTdiZjczZDRlNWJjNDdmYThiMGE0MzFhOTg4Y2E0NzQ2OTgxYjA1NzU3ZWJiY2JhOTE1ZGJlZGFiNDU3OTAzMDg)
or in a [issue](https://github.com/investingbots/value-investing-bot/issues) before a PR.
Expand Down
62 changes: 54 additions & 8 deletions bot/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

from typing import Any, Dict

from bot.data import remove_ticker, get_tickers, add_ticker, get_company_profile
from bot import __version__, OperationalException
from bot.data.data_provider_manager import DataProviderManager
from bot.data.data_providers import DataProviderException

logger = logging.getLogger(__name__)

Expand All @@ -14,9 +14,7 @@ class Bot:
def __init__(self, config: Dict[str, Any]) -> None:
logger.info('Starting bot version %s', __version__)

# Init objects

# Make variable private, only bot should change them
# Make variables private, only bot should change them
self.__config = config
self.__data_provider_manager = DataProviderManager(self.config)

Expand All @@ -28,11 +26,59 @@ def set_config(self, config: Dict[str, Any]):
self.__config = config

def add_ticker(self, ticker: str) -> None:
logger.info("Adding ticker ...")

if not get_company_profile(ticker, self.config):

if self.__data_provider_manager.evaluate_ticker(ticker):

profile = self.__data_provider_manager.get_profile(ticker)

if not profile:
raise OperationalException("Could not evaluate {} with the data providers".format(ticker))

company_name = profile.get('profile', {}).get('companyName', None)
category = profile.get('profile', {}).get('industry', None)

if not company_name:
raise OperationalException("Could not evaluate company name for ticker {} with the data providers")

if not company_name:
raise OperationalException("Could not evaluate category for ticker {} with the data providers")

try:
add_ticker(
ticker,
company_name=company_name,
category=category,
config=self.config
)
except Exception:
raise OperationalException(
"Something went wrong with adding ticker {} to the registry".format(ticker)
)
else:
raise OperationalException("Could not evaluate ticker {} with the data providers".format(ticker))
else:
raise OperationalException(
"Ticker {} is already present in registry".format(ticker)
)

def remove_ticker(self, ticker: str) -> None:
logger.info("Removing ticker ...")

if get_company_profile(ticker, self.config):

try:
remove_ticker(ticker, self.config)
except Exception:
raise OperationalException("Something went wrong while deleting the ticker from the registry")
else:
raise OperationalException("Provided ticker {} does not exist".format(ticker))

try:
self.__data_provider_manager.add_ticker(ticker)
except DataProviderException as e:
raise OperationalException(str(e))
def list_tickers(self):
logger.info("Listing tickers ...")
return get_tickers(self.config)



65 changes: 44 additions & 21 deletions bot/data/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging
import sqlite3
from typing import Dict, Any, List
from typing import Dict, Any, List, Tuple

from bot import setup

Expand Down Expand Up @@ -31,7 +31,7 @@ def create_tables(config: Dict[str, Any]) -> None:
con.close()

if TRADES_TABLE_NAME not in table_names:
logger.info("Creating database table {}...".format(TRADES_TABLE_NAME))
logger.info("Creating database table {} ...".format(TRADES_TABLE_NAME))
con = create_connection(config)

# Create open trades table
Expand All @@ -45,42 +45,65 @@ def create_tables(config: Dict[str, Any]) -> None:


def add_ticker(ticker: str, company_name: str, category: str, config: Dict[str, Any]) -> None:

logger.info("Adding ticker {} to registry...".format(ticker))

con = create_connection(config)
cursor = con.cursor()

logger.info("Adding ticker {} ...".format(ticker))
# Add ticker if not exists
insert_statement = """
INSERT INTO TICKERS (ticker, company_name, category)
VALUES (?, ?, ?);
"""

# Get ticker if exists
select_statement = """SELECT ticker_id from TICKERS where ticker = ?"""
cursor.execute(select_statement, (ticker,))
data_tuple = (ticker, company_name, category)
cursor.execute(insert_statement, data_tuple)

result = cursor.fetchall()
con.commit()
con.close()

if result:
logger.info("Ticker already in database")
else:
# Add ticker if not exists
insert_statement = """
INSERT INTO TICKERS (ticker, company_name, category)
VALUES (?, ?, ?);
"""

data_tuple = (ticker, company_name, category)
cursor.execute(insert_statement, data_tuple)
def remove_ticker(ticker: str, config: Dict[str, Any]) -> None:

logger.info("Removing ticker {} from registry ...".format(ticker))
con = create_connection(config)
cursor = con.cursor()

delete_statement = '''
DELETE from TICKERS where ticker = ?
'''

cursor.execute(delete_statement, (ticker,))
con.commit()
con.close()


def get_company_info(ticker: str, config: Dict[str, any]) -> List[str]:
def get_company_profile(ticker: str, config: Dict[str, any]) -> Tuple[str]:
con = create_connection(config)
cursor = con.cursor()

logger.info("Getting {} company info ...".format(ticker))
logger.info("Getting {} company info from registry ...".format(ticker))

cursor.execute(" SELECT * FROM TICKERS WHERE ticker=?", (ticker, ))
result = cursor.fetchall()

con.close()
return result


def get_tickers(config: Dict[str, any]) -> List[str]:
con = create_connection(config)
cursor = con.cursor()

logger.info("Getting all tickers from registry ...")

# Get ticker if exists
select_statement = """SELECT ticker_id from TICKERS where ticker = ?"""
cursor.execute(select_statement, (ticker,))
select_statement = '''
SELECT (ticker) from TICKERS
'''

cursor.execute(select_statement)
result = cursor.fetchall()
con.close()
return result
Expand Down
31 changes: 15 additions & 16 deletions bot/data/data_provider_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import List, Dict, Any

from bot.data.data_providers import DataProvider, DataProviderException
from . import add_ticker, get_all_table_names, create_tables, get_company_info
from . import get_all_table_names, create_tables

logger = logging.getLogger(__name__)

Expand All @@ -29,24 +29,23 @@ def __init__(self, config: Dict[str, Any]) -> None:
from bot.data.data_providers.fmp_data_provider import FMPDataProvider
self.registered_modules.append(FMPDataProvider(self.__config))

def add_ticker(self, ticker: str) -> None:
def evaluate_ticker(self, ticker: str) -> bool:

if get_company_info(ticker, self.__config):
raise DataProviderException("Ticker {} already exists".format(ticker))
else:
for data_provider in self.registered_modules:
for data_provider in self.registered_modules:

if data_provider.evaluate_ticker(ticker):
logger.info("Ticker exists")
profile = data_provider.get_profile(ticker)
if data_provider.evaluate_ticker(ticker):
logger.info("Ticker exists")
return True

company_name = profile.get('profile', {}).get('companyName', {})
industry = profile.get('profile', {}).get('industry', {})
return False

if company_name and industry:
add_ticker(ticker, company_name, industry, self.__config)
logger.info("Ticker {} has been added ...".format(ticker))
return
def get_profile(self, ticker: str) -> Dict:

raise DataProviderException("Could not evaluate ticker {}".format(ticker))
for data_provider in self.registered_modules:

profile = data_provider.get_profile(ticker)

if profile:
return profile

raise DataProviderException("Could not profile for {}".format(ticker))
13 changes: 7 additions & 6 deletions bot/data/data_providers/fmp_data_provider.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import logging
import json
from enum import Enum
from typing import Dict, Any

from urllib.request import urlopen
from urllib import error

from bot.data.data_providers.data_provider import DataProvider, DataProviderException

PROFILE_ENDPOINT = 'https://financialmodelingprep.com/api/v3/company/profile/{}'

logger = logging.getLogger(__name__)


class FMPDataProvider(DataProvider):

Expand All @@ -24,15 +27,15 @@ def get_data(self, ticker: str = None):
pass

def evaluate_ticker(self, ticker: str) -> bool:

try:
url = PROFILE_ENDPOINT.format(ticker)

response = urlopen(url)
data = response.read().decode("utf-8")
data = json.loads(data)

return data['symbol'] == ticker
except error.HTTPError:
logger.info('Hello')
return False
except Exception:
return False

Expand All @@ -44,5 +47,3 @@ def get_profile(self, ticker: str) -> Dict:





3 changes: 3 additions & 0 deletions bot/services/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,6 @@ def cleanup(self) -> None:

def _add_ticker(self, ticker: str) -> None:
self._bot.add_ticker(ticker)

def _remove_ticker(self, ticker: str) -> None:
self._bot.remove_ticker(ticker)
Loading