5 Commits

Author SHA1 Message Date
dependabot[bot] 22bc5bb4a8 Bump requests from 2.7.0 to 2.20.0
Bumps [requests](https://github.com/requests/requests) from 2.7.0 to 2.20.0.
- [Release notes](https://github.com/requests/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/master/HISTORY.md)
- [Commits](https://github.com/requests/requests/compare/v2.7.0...v2.20.0)

Signed-off-by: dependabot[bot] <support@github.com>
2019-10-18 17:03:00 +00:00
Michael Halls-Moore 2c53efb4f3 Merge pull request #28 from mhallsmoore/position_fix
Position fix
2015-07-15 08:24:12 +01:00
Michael Halls-Moore 553edab0db Added basic logging capability to trading.py and related classes. 2015-07-13 19:22:27 +01:00
Michael Halls-Moore 675412c125 Modified the position handling to fix a pricing bug, so that locally handled Portfolio values match those of OANDA (up to slippage). 2015-07-13 16:30:55 +01:00
Michael Halls-Moore be9ef2b54f Merge pull request #24 from mhallsmoore/backtest_class
Backtest class
2015-06-30 09:48:56 +01:00
10 changed files with 157 additions and 68 deletions
+10 -4
View File
@@ -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):
"""
@@ -38,12 +41,13 @@ class StreamingForexPrices(PriceHandler):
def connect_to_stream(self):
pairs_oanda = ["%s_%s" % (p[:3], p[3:]) for p in self.pairs]
pair_list = ",".join(pairs_oanda)
try:
requests.packages.urllib3.disable_warnings()
s = requests.Session()
url = "https://" + self.domain + "/v1/prices"
headers = {'Authorization' : 'Bearer ' + self.access_token}
params = {'instruments' : pairs_oanda, 'accountId' : self.account_id}
params = {'instruments' : pair_list, 'accountId' : self.account_id}
req = requests.Request('GET', url, headers=headers, params=params)
pre = req.prepare()
resp = s.send(pre, stream=True, verify=False)
@@ -62,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"]
+29 -2
View File
@@ -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)
+4 -2
View File
@@ -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)
+28
View File
@@ -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=
+61 -44
View File
@@ -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
@@ -126,51 +128,66 @@ class Portfolio(object):
print(out_line[:-2])
self.backtest_file.write(out_line)
def execute_signal(self, signal_event):
side = signal_event.side
currency_pair = signal_event.instrument
units = int(self.trade_units)
time = signal_event.time
# If there is no position, create one
if currency_pair not in self.positions:
if side == "buy":
position_type = "long"
def execute_signal(self, signal_event):
# Check that the prices ticker contains all necessary
# currency pairs prior to executing an order
execute = True
tp = self.ticker.prices
for pair in tp:
if tp[pair]["ask"] is None or tp[pair]["bid"] is None:
execute = False
# All necessary pricing data is available,
# we can execute
if execute:
side = signal_event.side
currency_pair = signal_event.instrument
units = int(self.trade_units)
time = signal_event.time
# If there is no position, create one
if currency_pair not in self.positions:
if side == "buy":
position_type = "long"
else:
position_type = "short"
self.add_new_position(
position_type, currency_pair,
units, self.ticker
)
# If a position exists add or remove units
else:
position_type = "short"
self.add_new_position(
position_type, currency_pair,
units, self.ticker
)
ps = self.positions[currency_pair]
# If a position exists add or remove units
if side == "buy" and ps.position_type == "long":
add_position_units(currency_pair, units)
elif side == "sell" and ps.position_type == "long":
if units == ps.units:
self.close_position(currency_pair)
# TODO: Allow units to be added/removed
elif units < ps.units:
return
elif units > ps.units:
return
elif side == "buy" and ps.position_type == "short":
if units == ps.units:
self.close_position(currency_pair)
# TODO: Allow units to be added/removed
elif units < ps.units:
return
elif units > ps.units:
return
elif side == "sell" and ps.position_type == "short":
add_position_units(currency_pair, units)
order = OrderEvent(currency_pair, units, "market", side)
self.events.put(order)
self.logger.info("Portfolio Balance: %s" % self.balance)
else:
ps = self.positions[currency_pair]
if side == "buy" and ps.position_type == "long":
add_position_units(currency_pair, units)
elif side == "sell" and ps.position_type == "long":
if units == ps.units:
self.close_position(currency_pair)
# TODO: Allow units to be added/removed
elif units < ps.units:
return
elif units > ps.units:
return
elif side == "buy" and ps.position_type == "short":
if units == ps.units:
self.close_position(currency_pair)
# TODO: Allow units to be added/removed
elif units < ps.units:
return
elif units > ps.units:
return
elif side == "sell" and ps.position_type == "short":
add_position_units(currency_pair, units)
order = OrderEvent(currency_pair, units, "market", side)
self.events.put(order)
self.logger.info("Unable to execute order as price data was insufficient.")
+4 -4
View File
@@ -212,7 +212,7 @@ class TestPortfolio(unittest.TestCase):
)
self.assertTrue(rpu)
self.assertEqual(ps.units, Decimal("7000"))
self.assertEqual(self.port.balance, Decimal("99988.84"))
self.assertEqual(self.port.balance, Decimal("99988.83"))
def test_close_position_long(self):
position_type = "long"
@@ -265,7 +265,7 @@ class TestPortfolio(unittest.TestCase):
cp = self.port.close_position(currency_pair)
self.assertTrue(cp)
self.assertRaises(ps) # Key doesn't exist
self.assertEqual(self.port.balance, Decimal("100026.64"))
self.assertEqual(self.port.balance, Decimal("100026.63"))
def test_close_position_short(self):
position_type = "short"
@@ -312,13 +312,13 @@ class TestPortfolio(unittest.TestCase):
)
self.assertTrue(rpu)
self.assertEqual(ps.units, Decimal("7000"))
self.assertEqual(self.port.balance, Decimal("99988.84"))
self.assertEqual(self.port.balance, Decimal("99988.83"))
# Close the position
cp = self.port.close_position(currency_pair)
self.assertTrue(cp)
self.assertRaises(ps) # Key doesn't exist
self.assertEqual(self.port.balance, Decimal("99962.80"))
self.assertEqual(self.port.balance, Decimal("99962.77"))
if __name__ == "__main__":
+6 -8
View File
@@ -24,7 +24,7 @@ class Position(object):
ticker_cur = self.ticker.prices[self.currency_pair]
if self.position_type == "long":
self.avg_price = Decimal(str(ticker_cur["ask"]))
self.cur_price = Decimal(str(ticker_cur["bid"]))
self.cur_price = Decimal(str(ticker_cur["bid"]))
else:
self.avg_price = Decimal(str(ticker_cur["bid"]))
self.cur_price = Decimal(str(ticker_cur["ask"]))
@@ -83,11 +83,11 @@ class Position(object):
ticker_cp = self.ticker.prices[self.currency_pair]
ticker_qh = self.ticker.prices[self.quote_home_currency_pair]
if self.position_type == "long":
remove_price = ticker_cp["ask"]
qh_close = ticker_qh["bid"]
else:
remove_price = ticker_cp["bid"]
qh_close = ticker_qh["ask"]
else:
remove_price = ticker_cp["ask"]
qh_close = ticker_qh["bid"]
self.units -= dec_units
self.update_position_price()
# Calculate PnL
@@ -99,11 +99,9 @@ class Position(object):
ticker_cp = self.ticker.prices[self.currency_pair]
ticker_qh = self.ticker.prices[self.quote_home_currency_pair]
if self.position_type == "long":
remove_price = ticker_cp["ask"]
qh_close = ticker_qh["bid"]
else:
remove_price = ticker_cp["bid"]
qh_close = ticker_qh["ask"]
else:
qh_close = ticker_qh["bid"]
self.update_position_price()
# Calculate PnL
pnl = self.calculate_pips() * qh_close * self.units
+1 -1
View File
@@ -7,7 +7,7 @@ pandas==0.16.1
pyparsing==2.0.3
python-dateutil==2.4.2
pytz==2014.10
requests==2.7.0
requests==2.20.0
scikit-learn==0.16.1
scipy==0.15.1
seaborn==0.5.1
+1 -1
View File
@@ -20,7 +20,7 @@ class TestStrategy(object):
self.invested = False
def calculate_signals(self, event):
if event.type == 'TICK':
if event.type == 'TICK' and event.instrument == self.pairs[0]:
if self.ticks % 5 == 0:
if self.invested == False:
signal = SignalEvent(self.pairs[0], "market", "buy", event.time)
+13 -2
View File
@@ -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
@@ -47,8 +56,8 @@ if __name__ == "__main__":
events = queue.Queue()
equity = settings.EQUITY
# Trade "Cable"
pairs = ["GBPUSD"]
# Pairs to include in streaming data set
pairs = ["EURUSD", "GBPUSD"]
# Create the OANDA market price streaming class
# making sure to provide authentication commands
@@ -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()