From 553edab0db0a1daa71d555259f17e5b657f347fa Mon Sep 17 00:00:00 2001 From: Michael Halls-Moore Date: Mon, 13 Jul 2015 19:22:27 +0100 Subject: [PATCH] Added basic logging capability to trading.py and related classes. --- data/streaming.py | 11 ++++++++--- event/event.py | 31 +++++++++++++++++++++++++++++-- execution/execution.py | 6 ++++-- logging.conf | 28 ++++++++++++++++++++++++++++ portfolio/portfolio.py | 7 +++++-- trading/trading.py | 11 +++++++++++ 6 files changed, 85 insertions(+), 9 deletions(-) create mode 100644 logging.conf diff --git a/data/streaming.py b/data/streaming.py index 1ec77e5..3abe7dd 100644 --- a/data/streaming.py +++ b/data/streaming.py @@ -1,9 +1,11 @@ from __future__ import print_function from decimal import Decimal, getcontext, ROUND_HALF_DOWN -import requests +import logging import json +import requests + from qsforex.event.event import TickEvent from qsforex.data.price import PriceHandler @@ -19,6 +21,7 @@ class StreamingForexPrices(PriceHandler): self.events_queue = events_queue self.pairs = pairs self.prices = self._set_up_prices_dict() + self.logger = logging.getLogger(__name__) def invert_prices(self, pair, bid, ask): """ @@ -63,10 +66,12 @@ class StreamingForexPrices(PriceHandler): dline = line.decode('utf-8') msg = json.loads(dline) except Exception as e: - print("Caught exception when converting message into json\n" + str(e)) + self.logger.error( + "Caught exception when converting message into json: %s" % str(e) + ) return if "instrument" in msg or "tick" in msg: - print(msg) + self.logger.debug(msg) getcontext().rounding = ROUND_HALF_DOWN instrument = msg["tick"]["instrument"].replace("_", "") time = msg["tick"]["time"] diff --git a/event/event.py b/event/event.py index 0552a1c..0a7c118 100644 --- a/event/event.py +++ b/event/event.py @@ -10,15 +10,33 @@ class TickEvent(Event): self.bid = bid self.ask = ask + def __str__(self): + return "Type: %s, Instrument: %s, Time: %s, Bid: %s, Ask: %s" % ( + str(self.type), str(self.instrument), + str(self.time), str(self.bid), str(self.ask) + ) + + def __repr__(self): + return str(self) + class SignalEvent(Event): def __init__(self, instrument, order_type, side, time): self.type = 'SIGNAL' self.instrument = instrument self.order_type = order_type - self.side = side + self.side = side self.time = time # Time of the last tick that generated the signal + def __str__(self): + return "Type: %s, Instrument: %s, Order Type: %s, Side: %s" % ( + str(self.type), str(self.instrument), + str(self.order_type), str(self.side) + ) + + def __repr__(self): + return str(self) + class OrderEvent(Event): def __init__(self, instrument, units, order_type, side): @@ -26,4 +44,13 @@ class OrderEvent(Event): self.instrument = instrument self.units = units self.order_type = order_type - self.side = side + self.side = side + + def __str__(self): + return "Type: %s, Instrument: %s, Units: %s, Order Type: %s, Side: %s" % ( + str(self.type), str(self.instrument), str(self.units), + str(self.order_type), str(self.side) + ) + + def __repr__(self): + return str(self) \ No newline at end of file diff --git a/execution/execution.py b/execution/execution.py index 1f4ff40..3197dc7 100644 --- a/execution/execution.py +++ b/execution/execution.py @@ -5,6 +5,7 @@ try: import httplib except ImportError: import http.client as httplib +import logging try: from urllib import urlencode except ImportError: @@ -47,6 +48,7 @@ class OANDAExecutionHandler(ExecutionHandler): self.access_token = access_token self.account_id = account_id self.conn = self.obtain_connection() + self.logger = logging.getLogger(__name__) def obtain_connection(self): return httplib.HTTPSConnection(self.domain) @@ -68,6 +70,6 @@ class OANDAExecutionHandler(ExecutionHandler): "/v1/accounts/%s/orders" % str(self.account_id), params, headers ) - response = self.conn.getresponse().read() - print(response) + response = self.conn.getresponse().read().decode("utf-8").replace("\n","").replace("\t","") + self.logger.debug(response) \ No newline at end of file diff --git a/logging.conf b/logging.conf new file mode 100644 index 0000000..ee8ec95 --- /dev/null +++ b/logging.conf @@ -0,0 +1,28 @@ +[loggers] +keys=root,qsforex.trading.trading + +[handlers] +keys=consoleHandler + +[formatters] +keys=simpleFormatter + +[logger_root] +level=DEBUG +handlers=consoleHandler + +[logger_qsforex.trading.trading] +level=DEBUG +handlers=consoleHandler +qualname=qsforex.trading.trading +propagate=0 + +[handler_consoleHandler] +class=StreamHandler +level=DEBUG +formatter=simpleFormatter +args=(sys.stdout,) + +[formatter_simpleFormatter] +format=%(asctime)s - %(name)s - %(levelname)s - %(message)s +datefmt= diff --git a/portfolio/portfolio.py b/portfolio/portfolio.py index 9c7dd5e..c86aafb 100644 --- a/portfolio/portfolio.py +++ b/portfolio/portfolio.py @@ -2,6 +2,7 @@ from __future__ import print_function from copy import deepcopy from decimal import Decimal, getcontext, ROUND_HALF_DOWN +import logging import os import pandas as pd @@ -30,6 +31,7 @@ class Portfolio(object): self.positions = {} if self.backtest: self.backtest_file = self.create_equity_file() + self.logger = logging.getLogger(__name__) def calc_risk_position_size(self): return self.equity * self.risk_per_trade @@ -184,7 +186,8 @@ class Portfolio(object): order = OrderEvent(currency_pair, units, "market", side) self.events.put(order) - print("Balance: ", self.balance) + + self.logger.info("Portfolio Balance: %s" % self.balance) else: - print("Unable to execute order as price data was insufficient.") + self.logger.info("Unable to execute order as price data was insufficient.") \ No newline at end of file diff --git a/trading/trading.py b/trading/trading.py index 359542d..e418896 100644 --- a/trading/trading.py +++ b/trading/trading.py @@ -1,5 +1,7 @@ import copy from decimal import Decimal, getcontext +import logging +import logging.config try: import Queue as queue except ImportError: @@ -30,16 +32,23 @@ def trade(events, strategy, portfolio, execution, heartbeat): else: if event is not None: if event.type == 'TICK': + logger.info("Received new tick event: %s", event) strategy.calculate_signals(event) portfolio.update_portfolio(event) elif event.type == 'SIGNAL': + logger.info("Received new signal event: %s", event) portfolio.execute_signal(event) elif event.type == 'ORDER': + logger.info("Received new order event: %s", event) execution.execute_order(event) time.sleep(heartbeat) if __name__ == "__main__": + # Set up logging + logging.config.fileConfig('../logging.conf') + logger = logging.getLogger('qsforex.trading.trading') + # Set the number of decimal places to 2 getcontext().prec = 2 @@ -86,5 +95,7 @@ if __name__ == "__main__": price_thread = threading.Thread(target=prices.stream_to_queue, args=[]) # Start both threads + logger.info("Starting trading thread") trade_thread.start() + logger.info("Starting price streaming thread") price_thread.start()