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:
+68
-66
@@ -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.")
|
||||
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user