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
|
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)
|
|
||||||
|
|||||||
@@ -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