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 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
): ):
""" """
Carries out an infinite while loop that polls the Initialises the backtest.
events queue and directs each event to either the """
strategy component of the execution handler. The self.pairs = pairs
loop will then pause for "heartbeat" seconds and self.events = queue.Queue()
continue unti the maximum number of iterations is self.csv_dir = settings.CSV_DATA_DIR
exceeded. self.ticker = data_handler(self.pairs, self.events, self.csv_dir)
""" self.strategy_params = strategy_params
iters = 0 self.strategy = strategy(
while iters < max_iters and ticker.continue_backtest: self.pairs, self.events, **self.strategy_params
try: )
event = events.get(False) self.equity = equity
except queue.Empty: self.heartbeat = heartbeat
ticker.stream_next_tick() self.max_iters = max_iters
else: self.portfolio = portfolio(
if event is not None: self.ticker, self.events, equity=self.equity, backtest=True
if event.type == 'TICK': )
strategy.calculate_signals(event) self.execution = execution()
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()
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__": def _output_performance(self):
heartbeat = 0.0 """
events = queue.Queue() Outputs the strategy performance from the backtest.
equity = settings.EQUITY """
print("Calculating Performance Metrics...")
self.portfolio.output_results()
# Load the historic CSV tick data filesw def simulate_trading(self):
pairs = ["GBPUSD"] """
csv_dir = settings.CSV_DATA_DIR Simulates the backtest and outputs portfolio performance.
if csv_dir is None: """
print("No historic data directory provided - backtest terminating.") self._run_backtest()
sys.exit() self._output_performance()
print("Backtest complete.")
# 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()