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
+52 -50
View File
@@ -1,25 +1,45 @@
from __future__ import print_function from __future__ import print_function
import copy
try: try:
import Queue as queue import Queue as queue
except ImportError: except ImportError:
import queue import queue
import threading
import time 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 import settings
from qsforex.strategy.strategy import TestStrategy, MovingAverageCrossStrategy
from qsforex.data.price import HistoricCSVPriceHandler
def backtest( class Backtest(object):
events, ticker, strategy, portfolio, """
execution, heartbeat, max_iters=5000000 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
): ):
"""
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 Carries out an infinite while loop that polls the
events queue and directs each event to either the events queue and directs each event to either the
@@ -28,54 +48,36 @@ def backtest(
continue unti the maximum number of iterations is continue unti the maximum number of iterations is
exceeded. exceeded.
""" """
print("Running Backtest...")
iters = 0 iters = 0
while iters < max_iters and ticker.continue_backtest: while iters < self.max_iters and self.ticker.continue_backtest:
try: try:
event = events.get(False) event = self.events.get(False)
except queue.Empty: except queue.Empty:
ticker.stream_next_tick() self.ticker.stream_next_tick()
else: else:
if event is not None: if event is not None:
if event.type == 'TICK': if event.type == 'TICK':
strategy.calculate_signals(event) self.strategy.calculate_signals(event)
portfolio.update_portfolio(event) self.portfolio.update_portfolio(event)
elif event.type == 'SIGNAL': elif event.type == 'SIGNAL':
portfolio.execute_signal(event) self.portfolio.execute_signal(event)
elif event.type == 'ORDER': elif event.type == 'ORDER':
execution.execute_order(event) self.execution.execute_order(event)
time.sleep(heartbeat) time.sleep(self.heartbeat)
iters += 1 iters += 1
portfolio.output_results()
def _output_performance(self):
"""
Outputs the strategy performance from the backtest.
"""
print("Calculating Performance Metrics...")
self.portfolio.output_results()
if __name__ == "__main__": def simulate_trading(self):
heartbeat = 0.0 """
events = queue.Queue() Simulates the backtest and outputs portfolio performance.
equity = settings.EQUITY """
self._run_backtest()
# Load the historic CSV tick data filesw self._output_performance()
pairs = ["GBPUSD"] print("Backtest complete.")
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)
View File
+29
View File
@@ -0,0 +1,29 @@
from __future__ import print_function
from qsforex.backtest.backtest import Backtest
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
if __name__ == "__main__":
# Trade on the "Cable" - GBP/USD
pairs = ["GBPUSD"]
# Create the strategy parameters for the
# MovingAverageCrossStrategy
strategy_params = {
"short_window": 500,
"long_window": 2000
}
# Create and execute the backtest
backtest = Backtest(
pairs, HistoricCSVPriceHandler,
MovingAverageCrossStrategy, strategy_params,
Portfolio, SimulatedExecution,
equity=settings.EQUITY
)
backtest.simulate_trading()