Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Barevent #19

Merged
merged 2 commits into from
May 16, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Added BarEvent event object to encapsulate OHLCV data at various peri…
…od resolutions. Modified the PriceHandler base class hierarchy such that subsequent PriceHandler objects can now have a TICK_HANDLER or BAR_HANDLER type. Modified remainder of code to work with TickEvent and BarEvent objects.
  • Loading branch information
mhallsmoore committed May 16, 2016
commit 3f321b54515b9643a7d49a6c552ae690e5c9c5b4
16 changes: 14 additions & 2 deletions backtest/backtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,15 @@ def _run_backtest(self):
print("Running Backtest...")
iters = 0
ticks = 0
bars = 0
while iters < self.max_iters and self.price_handler.continue_backtest:
try:
event = self.events_queue.get(False)
except queue.Empty:
self.price_handler.stream_next_tick()
if self.price_handler.type == "TICK_HANDLER":
self.price_handler.stream_next_tick()
else:
self.price_handler.stream_next_bar()
else:
if event is not None:
if event.type == 'TICK':
Expand All @@ -106,6 +110,13 @@ def _run_backtest(self):
self.strategy.calculate_signals(event)
self.portfolio_handler.update_portfolio_value()
ticks += 1
elif event.type == 'BAR':
self.cur_time = event.time
print("Bar %s, at %s" % (bars, self.cur_time))
self._append_equity_state()
self.strategy.calculate_signals(event)
self.portfolio_handler.update_portfolio_value()
bars += 1
elif event.type == 'SIGNAL':
self.portfolio_handler.on_signal(event)
elif event.type == 'ORDER':
Expand All @@ -124,4 +135,5 @@ def simulate_trading(self):
print("Backtest complete.")
statistics = Statistics()
statistics.generate_results()
statistics.plot_results()
statistics.plot_results()

93 changes: 93 additions & 0 deletions event/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,99 @@ def __repr__(self):
return str(self)


class BarEvent(Event):
"""
Handles the event of receiving a new market
open-high-low-close-volume bar, as would be generated
via common data providers such as Yahoo Finance.
"""
def __init__(
self, ticker, time, period,
open_price, high_price, low_price,
close_price, volume, adj_close_price=None
):
"""
Initialises the BarEvent.

Parameters:
ticker - The ticker symbol, e.g. 'GOOG'.
time - The timestamp of the bar
period - The time period covered by the bar in seconds
open_price - The unadjusted opening price of the bar
high_price - The unadjusted high price of the bar
low_price - The unadjusted low price of the bar
close_price - The unadjusted close price of the bar
volume - The volume of trading within the bar
adj_close_price - The vendor adjusted closing price
(e.g. back-adjustment) of the bar

Note: It is not advised to use 'open', 'close' instead
of 'open_price', 'close_price' as 'open' is a reserved
word in Python.
"""
self.type = 'BAR'
self.ticker = ticker
self.time = time
self.period = period
self.open_price = open_price
self.high_price = high_price
self.low_price = low_price
self.close_price = close_price
self.volume = volume
self.adj_close_price = adj_close_price
self.period_readable = self._readable_period()

def _readable_period(self):
"""
Creates a human-readable period from the number
of seconds specified for 'period'.

For instance, converts:
* 1 -> '1sec'
* 5 -> '5secs'
* 60 -> '1min'
* 300 -> '5min'

If no period is found in the lookup table, the human
readable period is simply passed through from period,
in seconds.
"""
lut = {
1: "1sec",
5: "5sec",
10: "10sec",
15: "15sec",
30: "30sec",
60: "1min",
300: "5min",
600: "10min",
900: "15min",
1800: "30min",
3600: "1hr",
86400: "1day",
604800: "1wk"
}
if self.period in lut:
return lut[self.period]
else:
return "%ssec" % str(self.period)

def __str__(self):
format_str = "Type: %s, Ticker: %s, Time: %s, Period: %s, " \
"Open: %s, High: %s, Low: %s, Close: %s, " \
"Adj Close: %s, Volume: %s" % (
str(self.type), str(self.ticker), str(self.time),
str(self.period_readable), str(self.open_price),
str(self.high_price), str(self.low_price),
str(self.close_price), str(self.adj_close_price),
str(self.volume)
)
return format_str

def __repr__(self):
return str(self)


class SignalEvent(Event):
"""
Handles the event of sending a Signal from a Strategy object.
Expand Down
2 changes: 1 addition & 1 deletion examples/test_sp500tr_buy_and_hold_backtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from qstrader.execution_handler.execution_handler import IBSimulatedExecutionHandler
from qstrader.portfolio_handler.portfolio_handler import PortfolioHandler
from qstrader.position_sizer.position_sizer import TestPositionSizer
from qstrader.price_handler.price_handler import YahooDailyBarPriceHandler
from qstrader.price_handler.yahoo_price_handler import YahooDailyBarPriceHandler
from qstrader.risk_manager.risk_manager import TestRiskManager
from qstrader import settings
from qstrader.strategy.strategy import BuyAndHoldStrategy
Expand Down
12 changes: 8 additions & 4 deletions execution_handler/execution_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,15 @@ def execute_order(self, event):
quantity = event.quantity

# Obtain the fill price
bid, ask = self.price_handler.get_best_bid_ask(ticker)
if event.action == "BOT":
fill_price = Decimal(str(ask))
if self.price_handler.type == "TICK_HANDLER":
bid, ask = self.price_handler.get_best_bid_ask(ticker)
if event.action == "BOT":
fill_price = Decimal(str(ask))
else:
fill_price = Decimal(str(bid))
else:
fill_price = Decimal(str(bid))
close_price = self.price_handler.get_last_close(ticker)
fill_price = Decimal(str(close_price))

# Set a dummy exchange and calculate trade commission
exchange = "ARCA"
Expand Down
21 changes: 18 additions & 3 deletions portfolio/portfolio.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ def _update_portfolio(self):
"""
for ticker in self.positions:
pt = self.positions[ticker]
bid, ask = self.price_handler.get_best_bid_ask(ticker)
if self.price_handler.type == "TICK_HANDLER":
bid, ask = self.price_handler.get_best_bid_ask(ticker)
else:
close_price = self.price_handler.get_last_close(ticker)
bid = close_price
ask = close_price
pt.update_market_value(bid, ask)
self.unrealised_pnl += pt.unrealised_pnl
self.realised_pnl += pt.realised_pnl
Expand All @@ -66,7 +71,12 @@ def _add_position(
"""
self._reset_values()
if ticker not in self.positions:
bid, ask = self.price_handler.get_best_bid_ask(ticker)
if self.price_handler.type == "TICK_HANDLER":
bid, ask = self.price_handler.get_best_bid_ask(ticker)
else:
close_price = self.price_handler.get_last_close(ticker)
bid = close_price
ask = close_price
position = Position(
action, ticker, quantity,
price, commission, bid, ask
Expand Down Expand Up @@ -97,7 +107,12 @@ def _modify_position(
self.positions[ticker].transact_shares(
action, quantity, price, commission
)
bid, ask = self.price_handler.get_best_bid_ask(ticker)
if self.price_handler.type == "TICK_HANDLER":
bid, ask = self.price_handler.get_best_bid_ask(ticker)
else:
close_price = self.price_handler.get_last_close(ticker)
bid = close_price
ask = close_price
self.positions[ticker].update_market_value(bid, ask)
self._update_portfolio()
else:
Expand Down
2 changes: 1 addition & 1 deletion portfolio/portfolio_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

class PriceHandlerMock(object):
def __init__(self):
pass
self.type = "TICK_HANDLER"

def get_best_bid_ask(self, ticker):
prices = {
Expand Down
2 changes: 1 addition & 1 deletion portfolio_handler/portfolio_handler_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

class PriceHandlerMock(object):
def __init__(self):
pass
self.type = "TICK_HANDLER"

def get_best_bid_ask(self, ticker):
prices = {
Expand Down
Loading