Added a Backtest class, which replaces the script in backtest.py. Also added an examples directory, to make strategy testing straightforward.

This commit is contained in:
Michael Halls-Moore
2015-06-23 11:52:44 +01:00
parent d191aad641
commit 784cfd2508
3 changed files with 97 additions and 66 deletions
+68 -66
View File
@@ -1,81 +1,83 @@
from __future__ import print_function
import copy
try:
import Queue as queue
except ImportError:
import queue
import threading
import time
from decimal import Decimal, getcontext
from qsforex.execution.execution import SimulatedExecution
from qsforex.portfolio.portfolio import Portfolio
from qsforex import settings
from qsforex.strategy.strategy import TestStrategy, MovingAverageCrossStrategy
from qsforex.data.price import HistoricCSVPriceHandler
def backtest(
events, ticker, strategy, portfolio,
execution, heartbeat, max_iters=5000000
class Backtest(object):
"""
Enscapsulates the settings and components for carrying out
an event-driven backtest on the foreign exchange markets.
"""
def __init__(
self, pairs, data_handler, strategy,
strategy_params, portfolio, execution,
equity=100000.0, heartbeat=0.0,
max_iters=100000000
):
"""
Carries out an infinite while loop that polls the
events queue and directs each event to either the
strategy component of the execution handler. The
loop will then pause for "heartbeat" seconds and
continue unti the maximum number of iterations is
exceeded.
"""
iters = 0
while iters < max_iters and ticker.continue_backtest:
try:
event = events.get(False)
except queue.Empty:
ticker.stream_next_tick()
else:
if event is not None:
if event.type == 'TICK':
strategy.calculate_signals(event)
portfolio.update_portfolio(event)
elif event.type == 'SIGNAL':
portfolio.execute_signal(event)
elif event.type == 'ORDER':
execution.execute_order(event)
time.sleep(heartbeat)
iters += 1
portfolio.output_results()
"""
Initialises the backtest.
"""
self.pairs = pairs
self.events = queue.Queue()
self.csv_dir = settings.CSV_DATA_DIR
self.ticker = data_handler(self.pairs, self.events, self.csv_dir)
self.strategy_params = strategy_params
self.strategy = strategy(
self.pairs, self.events, **self.strategy_params
)
self.equity = equity
self.heartbeat = heartbeat
self.max_iters = max_iters
self.portfolio = portfolio(
self.ticker, self.events, equity=self.equity, backtest=True
)
self.execution = execution()
def _run_backtest(self):
"""
Carries out an infinite while loop that polls the
events queue and directs each event to either the
strategy component of the execution handler. The
loop will then pause for "heartbeat" seconds and
continue unti the maximum number of iterations is
exceeded.
"""
print("Running Backtest...")
iters = 0
while iters < self.max_iters and self.ticker.continue_backtest:
try:
event = self.events.get(False)
except queue.Empty:
self.ticker.stream_next_tick()
else:
if event is not None:
if event.type == 'TICK':
self.strategy.calculate_signals(event)
self.portfolio.update_portfolio(event)
elif event.type == 'SIGNAL':
self.portfolio.execute_signal(event)
elif event.type == 'ORDER':
self.execution.execute_order(event)
time.sleep(self.heartbeat)
iters += 1
if __name__ == "__main__":
heartbeat = 0.0
events = queue.Queue()
equity = settings.EQUITY
def _output_performance(self):
"""
Outputs the strategy performance from the backtest.
"""
print("Calculating Performance Metrics...")
self.portfolio.output_results()
# Load the historic CSV tick data filesw
pairs = ["GBPUSD"]
csv_dir = settings.CSV_DATA_DIR
if csv_dir is None:
print("No historic data directory provided - backtest terminating.")
sys.exit()
# Create the historic tick data streaming class
ticker = HistoricCSVPriceHandler(pairs, events, csv_dir)
# Create the strategy/signal generator, passing the
# instrument and the events queue
strategy = MovingAverageCrossStrategy(
pairs, events, 500, 2000
)
# Create the portfolio object to track trades
portfolio = Portfolio(
ticker, events, equity=equity, backtest=True
)
# Create the simulated execution handler
execution = SimulatedExecution()
# Carry out the backtest loop
backtest(events, ticker, strategy, portfolio, execution, heartbeat)
def simulate_trading(self):
"""
Simulates the backtest and outputs portfolio performance.
"""
self._run_backtest()
self._output_performance()
print("Backtest complete.")