Skip to content

Commit

Permalink
Merge pull request #30 from ilcardella/config-filepath
Browse files Browse the repository at this point in the history
Config filepath
  • Loading branch information
ilcardella committed Feb 16, 2019
2 parents 38cf93c + 4c4f199 commit e53c8c8
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 77 deletions.
6 changes: 2 additions & 4 deletions config/config.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
{
"general": {
"debug_log": false,
"enable_file_log": false,
"log_filepath": "{home}/.TradingMate/log/trading_mate_{timestamp}.log",
"trading_log_path": "/home/alberto/Dropbox/Documents/Trading/trading_log.json",
"credentials_filepath": "../data/.credentials"
"trading_log_path": "{home}/.TradingMate/trading_log.json",
"credentials_filepath": "{home}/.TradingMate/config/.credentials"
},
"alpha_vantage": {
"api_base_uri": "https://www.alphavantage.co/query",
Expand Down
45 changes: 22 additions & 23 deletions src/Model/DatabaseHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,49 +20,46 @@ def __init__(self, config):
Initialise
"""
# By default use the configured filepath
self.db_filepath = config.get_trading_database_path()
filepath = config.get_trading_database_path()
self.db_filepath = filepath.replace('{home}', Utils.get_home_path())
os.makedirs(os.path.dirname(self.db_filepath), exist_ok=True)
# Create an empty list to store trades from database
self.trading_history = []
logging.info('DatabaseHandler initialised')

def read_data(self, filepath=None):
"""
Read the trade history from the json database and return the list of trades
- **filepath**: optional, if not set the configured path will be used
"""
try:
path = filepath if filepath is not None else self.db_filepath
self.db_filepath = path
json_obj = Utils.load_json_file(path)

path = filepath if filepath is not None else self.db_filepath
logging.info('DatabaseHandler - reading data from {}'.format(path))
self.db_filepath = path
json_obj = Utils.load_json_file(path)
self.trading_history.clear()
if json_obj is not None:
# Create a list of all the trades in the json file
self.trading_history.clear()
for item in json_obj['trades']:
trade = Trade.from_dict(item)
# Store the list internally
self.trading_history.append(trade)
except Exception as e:
logging.error(e)
raise RuntimeError('Unable to read data from the database')


def write_data(self, filepath=None):
"""
Write the trade history to the database
"""
try:
path = filepath if filepath is not None else self.db_filepath
# Create a json object and store the trade history into it
json_obj = {
'trades': []
}
for t in self.trading_history:
json_obj['trades'].append(t.to_dict())
# Write to file
return Utils.write_json_file(path, json_obj)
except Exception as e:
logging.error(e)
raise RuntimeError('Unable to write data to the database')
path = filepath if filepath is not None else self.db_filepath
logging.info('DatabaseHandler - writing data to {}'.format(path))
# Create a json object and store the trade history into it
json_obj = {
'trades': []
}
for t in self.trading_history:
json_obj['trades'].append(t.to_dict())
# Write to file
return Utils.write_json_file(path, json_obj)

def get_db_filepath(self):
"""
Expand All @@ -82,6 +79,7 @@ def add_trade(self, trade):
"""
try:
self.trading_history.append(trade)
logging.info('DatabaseHandler - adding trade {}'.format(trade))
except Exception as e:
logging.error(e)
raise RuntimeError('Unable to add trade to the database')
Expand All @@ -92,6 +90,7 @@ def remove_last_trade(self):
"""
try:
del self.trading_history[-1]
logging.info('DatabaseHandler - removed last trade')
except Exception as e:
logging.error(e)
raise RuntimeError('Unable to delete last trade')
6 changes: 6 additions & 0 deletions src/Model/Holding.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
import inspect
import sys
import logging

currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
Expand All @@ -10,8 +11,10 @@ class Holding():

def __init__(self, symbol, quantity, open_price=None):
if quantity is None or quantity < 1:
logging.error('Holding - init: Invalid quantity')
raise ValueError("Invalid quantity")
if open_price is not None and open_price < 0:
logging.error('Holding - init: Invalid open_price')
raise ValueError('Invalid open_price')
self._symbol = symbol
self._quantity = quantity
Expand All @@ -21,17 +24,20 @@ def __init__(self, symbol, quantity, open_price=None):

def set_last_price(self, price):
if price is None or price < 0:
logging.error('Holding - set_last_price: Invalid price')
raise ValueError("Invalid price")
self._lastPrice = price
self._lastPriceValid = True

def set_open_price(self, price):
if price is None or price < 0:
logging.error('Holding - set_open_price: Invalid price')
raise ValueError("Invalid price")
self._openPrice = price

def set_quantity(self, value):
if value is None or value < 1:
logging.error('Holding - set_quantity: Invalid quantity')
raise ValueError("Invalid quantity")
self._quantity = value

Expand Down
15 changes: 12 additions & 3 deletions src/Model/Portfolio.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,20 @@ def __init__(self, name, config):
self.callbacks = {}
# Work thread that fetches stocks live prices
self.price_getter = StockPriceGetter(config, self.on_new_price_data)
logging.info('Portfolio initialised')

def set_callback(self, id, callback):
self.callbacks[id] = callback

def start(self, trades_list):
self.reload(trades_list)
self.price_getter.start()
logging.info('Portfolio started')

def stop(self):
self.price_getter.shutdown()
self.price_getter.join()
logging.info('Portfolio stopped')

# GETTERS

Expand Down Expand Up @@ -162,6 +165,7 @@ def clear(self):
self._cash_available = 0
self._cash_deposited = 0
self._holdings.clear()
logging.info('Portfolio cleared')

def reload(self, trades_list):
"""
Expand Down Expand Up @@ -199,6 +203,7 @@ def reload(self, trades_list):
self._holdings[symbol].set_open_price(self.compute_avg_holding_open_price(symbol, trades_list))
for symbol, price in self.price_getter.get_last_data().items():
self._holdings[symbol].set_last_price(price)
logging.info('Portfolio reloaded successfully')
except Exception as e:
logging.error(e)
raise RuntimeError('Unable to reload the portfolio')
Expand Down Expand Up @@ -230,36 +235,40 @@ def is_trade_valid(self, newTrade):
"""
if newTrade.action == Actions.WITHDRAW:
if newTrade.quantity > self.get_cash_available():
logging.error(Messages.INSUF_FUNDING.value)
logging.warning(Messages.INSUF_FUNDING.value)
raise RuntimeError(Messages.INSUF_FUNDING.value)
elif newTrade.action == Actions.BUY:
cost = (newTrade.price * newTrade.quantity) / 100 # in £
fee = newTrade.fee
tax = (newTrade.sdr * cost) / 100
totalCost = cost + fee + tax
if totalCost > self.get_cash_available():
logging.error(Messages.INSUF_FUNDING.value)
logging.warning(Messages.INSUF_FUNDING.value)
raise RuntimeError(Messages.INSUF_FUNDING.value)
elif newTrade.action == Actions.SELL:
if newTrade.quantity > self.get_holding_quantity(newTrade.symbol):
logging.error(Messages.INSUF_HOLDINGS.value)
logging.warning(Messages.INSUF_HOLDINGS.value)
raise RuntimeError(Messages.INSUF_HOLDINGS.value)
logging.info('Portfolio - trade validated')
return True

# PRICE GETTER WORK THREAD

def on_new_price_data(self):
logging.info('Portfolio - new live price available')
priceDict = self.price_getter.get_last_data()
for symbol, price in priceDict.items():
if symbol in self._holdings:
self._holdings[symbol].set_last_price(price)
self.callbacks[Callbacks.UPDATE_LIVE_PRICES]()

def on_manual_refresh_live_data(self):
logging.info('Portfolio - manual refresh live price')
if self.price_getter.is_enabled():
self.price_getter.cancel_timeout()
else:
self.price_getter.force_single_run()

def set_auto_refresh(self, enabled):
logging.info('Portfolio - live price auto refresh: {}'.format(enabled))
self.price_getter.enable(enabled)
13 changes: 12 additions & 1 deletion src/Model/StockPriceGetter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import inspect
import requests
import json
import logging

currentdir = os.path.dirname(os.path.abspath(
inspect.getfile(inspect.currentframe())))
Expand All @@ -22,6 +23,7 @@ def __init__(self, config, onNewPriceDataCallback):
self.onNewPriceDataCallback = onNewPriceDataCallback
self.lastData = {}
self.symbolList = []
logging.info('StockPriceGetter initialised')

def _read_configuration(self, config):
self.alphaVantageAPIKey = config.get_alpha_vantage_api_key()
Expand All @@ -34,7 +36,7 @@ def task(self):
for symbol in self.symbolList:
if not self._finished.isSet():
value = self._fetch_price_data(symbol)
# Wait 1 sec as suggested by AlphaVantage support
# Wait as suggested by AlphaVantage support
self._timeout.wait(2)
if value is not None:
priceDict[symbol] = value
Expand All @@ -46,14 +48,23 @@ def _fetch_price_data(self, symbol):
try:
url = self._build_url("TIME_SERIES_DAILY",
symbol, "5min", self.alphaVantageAPIKey)
except Exception:
logging.error(
'StockPriceGetter - Unable to build url for {}'.format(symbol))
return None
try:
response = requests.get(url)
if response.status_code != 200:
logging.error('StockPriceGetter - Request for {} returned code {}'.format(
url.split('apikey')[0], response.status_code))
return None
data = json.loads(response.text)
timeSerie = data["Time Series (Daily)"]
last = next(iter(timeSerie.values()))
value = float(last["4. close"])
except Exception:
logging.error(
'StockPriceGetter - Unable to fetch data from {}'.format(url.split('apikey')[0]))
value = None
return value

Expand Down

0 comments on commit e53c8c8

Please sign in to comment.