Dalam dua artikel sebelumnya dari seri ini kami membahas apa sistem backtesting event-driven dan hierarki kelas untuk objek Event.
Salah satu tujuan kami dengan sistem perdagangan yang didorong oleh acara adalah untuk meminimalkan duplikasi kode antara elemen backtesting dan elemen eksekusi langsung. Idealnya, akan optimal untuk menggunakan metodologi generasi sinyal dan komponen manajemen portofolio yang sama untuk pengujian historis dan perdagangan langsung. Untuk ini bekerja objek Strategi yang menghasilkan Sinyal, dan objek Portofolio yang menyediakan Order berdasarkan mereka, harus menggunakan antarmuka yang identik dengan umpan pasar untuk berjalan historis dan langsung.
Hal ini memotivasi konsep hierarki kelas berdasarkan objek DataHandler, yang memberikan semua subkelas antarmuka untuk menyediakan data pasar ke komponen yang tersisa dalam sistem. Dengan cara ini setiap subkelas data handler dapat
Subkelas contoh spesifik dapat mencakup HistoricCSVDataHandler, QuandlDataHandler, SecuritiesMasterDataHandler, InteractiveBrokersMarketFeedDataHandler dll. Dalam tutorial ini kita hanya akan mempertimbangkan pembuatan pengendali data CSV historis, yang akan memuat data CSV intraday untuk ekuitas dalam set bar Open-Low-High-Close-Volume-OpenInterest. Ini kemudian dapat digunakan untuk
Tugas pertama adalah untuk mengimpor perpustakaan yang diperlukan. khususnya kita akan mengimpor panda dan abstrak alat kelas dasar. karena DataHandler menghasilkan MarketEvents kita juga perlu mengimporevent.pyseperti yang dijelaskan dalam tutorial sebelumnya:
# data.py
import datetime
import os, os.path
import pandas as pd
dari abc import ABCMeta, metode abstrak
dari impor acara MarketEvent DataHandler adalah kelas dasar abstrak (ABC), yang berarti bahwa tidak mungkin untuk menginstansikan sebuah instance secara langsung. Hanya subclass yang dapat diinstansikan. Alasan untuk ini adalah bahwa ABC menyediakan antarmuka yang harus diikuti oleh semua subclass DataHandler berikutnya sehingga memastikan kompatibilitas dengan kelas lain yang berkomunikasi dengan mereka.
Kami memanfaatkanMetakelasproperti untuk membiarkan Python tahu bahwa ini adalah ABC. Selain itu kita menggunakan dekorator @abstractmethod untuk membiarkan Python tahu bahwa metode akan ditimpa dalam subkelas (ini identik dengan metode virtual murni di C ++).
Kedua metode yang menarik adalah get_latest_bars dan update_bars. Yang pertama mengembalikan N bar terakhir dari timestamp detak jantung saat ini, yang berguna untuk perhitungan bergulir yang diperlukan dalam kelas Strategi. Metode terakhir menyediakan mekanisme
# data.py
class DataHandler(object):
"""
DataHandler is an abstract base class providing an interface for
all subsequent (inherited) data handlers (both live and historic).
The goal of a (derived) DataHandler object is to output a generated
set of bars (OLHCVI) for each symbol requested.
This will replicate how a live strategy would function as current
market data would be sent "down the pipe". Thus a historic and live
system will be treated identically by the rest of the backtesting suite.
"""
__metaclass__ = ABCMeta
@abstractmethod
def get_latest_bars(self, symbol, N=1):
"""
Returns the last N bars from the latest_symbol list,
or fewer if less bars are available.
"""
raise NotImplementedError("Should implement get_latest_bars()")
@abstractmethod
def update_bars(self):
"""
Pushes the latest bar to the latest symbol structure
for all symbols in the symbol list.
"""
raise NotImplementedError("Should implement update_bars()")
Dengan DataHandler ABC yang ditentukan langkah selanjutnya adalah membuat pengendali untuk file CSV historis. Secara khusus HistoricCSVDataHandler akan mengambil beberapa file CSV, satu untuk setiap simbol, dan mengubahnya menjadi kamus panda DataFrames.
Data handler membutuhkan beberapa parameter, yaitu Event Queue untuk mendorong informasi MarketEvent, jalur absolut file CSV dan daftar simbol.
# data.py
class HistoricCSVDataHandler(DataHandler):
"""
HistoricCSVDataHandler is designed to read CSV files for
each requested symbol from disk and provide an interface
to obtain the "latest" bar in a manner identical to a live
trading interface.
"""
def __init__(self, events, csv_dir, symbol_list):
"""
Initialises the historic data handler by requesting
the location of the CSV files and a list of symbols.
It will be assumed that all files are of the form
'symbol.csv', where symbol is a string in the list.
Parameters:
events - The Event Queue.
csv_dir - Absolute directory path to the CSV files.
symbol_list - A list of symbol strings.
"""
self.events = events
self.csv_dir = csv_dir
self.symbol_list = symbol_list
self.symbol_data = {}
self.latest_symbol_data = {}
self.continue_backtest = True
self._open_convert_csv_files()
Ini secara implisit akan mencoba membuka file dengan format
Salah satu manfaat menggunakan panda sebagai data store secara internal dalam HistoricCSVDataHandler adalah bahwa indeks semua simbol yang dilacak dapat digabungkan bersama. Hal ini memungkinkan titik data yang hilang untuk dipadatkan ke depan, ke belakang atau diinterpolasi dalam kesenjangan ini sehingga ticker dapat dibandingkan pada basis bar-to-bar. Ini diperlukan untuk strategi mean-reversing, misalnya. Perhatikan penggunaan metode union dan reindex saat menggabungkan indeks untuk semua simbol:
# data.py
def _open_convert_csv_files(self):
"""
Opens the CSV files from the data directory, converting
them into pandas DataFrames within a symbol dictionary.
For this handler it will be assumed that the data is
taken from DTN IQFeed. Thus its format will be respected.
"""
comb_index = None
for s in self.symbol_list:
# Load the CSV file with no header information, indexed on date
self.symbol_data[s] = pd.io.parsers.read_csv(
os.path.join(self.csv_dir, '%s.csv' % s),
header=0, index_col=0,
names=['datetime','open','low','high','close','volume','oi']
)
# Combine the index to pad forward values
if comb_index is None:
comb_index = self.symbol_data[s].index
else:
comb_index.union(self.symbol_data[s].index)
# Set the latest symbol_data to None
self.latest_symbol_data[s] = []
# Reindex the dataframes
for s in self.symbol_list:
self.symbol_data[s] = self.symbol_data[s].reindex(index=comb_index, method='pad').iterrows()
Metode _get_new_bar membuat generator untuk menyediakan versi berformat dari data bar. Ini berarti bahwa panggilan berikutnya ke metode akan menghasilkan bar baru sampai data simbol mencapai akhir:
# data.py
def _get_new_bar(self, symbol):
"""
Returns the latest bar from the data feed as a tuple of
(sybmbol, datetime, open, low, high, close, volume).
"""
for b in self.symbol_data[symbol]:
yield tuple([symbol, datetime.datetime.strptime(b[0], '%Y-%m-%d %H:%M:%S'),
b[1][0], b[1][1], b[1][2], b[1][3], b[1][4]])
Metode abstrak pertama dari DataHandler yang akan diimplementasikan adalah get_latest_bars. Metode ini hanya menyediakan daftar N bar terakhir dari struktur data latest_symbol_data. Pengaturan N=1 memungkinkan pengambilan bar saat ini (dibungkus dalam daftar):
# data.py
def get_latest_bars(self, symbol, N=1):
"""
Returns the last N bars from the latest_symbol list,
or N-k if less available.
"""
try:
bars_list = self.latest_symbol_data[symbol]
except KeyError:
print "That symbol is not available in the historical data set."
else:
return bars_list[-N:]
Metode terakhir, update_bars adalah metode abstrak kedua dari DataHandler. Ini hanya menghasilkan MarketEvent yang ditambahkan ke antrian saat menambahkan bar terbaru ke data_simbol_terbaru:
# data.py
def update_bars(self):
"""
Pushes the latest bar to the latest_symbol_data structure
for all symbols in the symbol list.
"""
for s in self.symbol_list:
try:
bar = self._get_new_bar(s).next()
except StopIteration:
self.continue_backtest = False
else:
if bar is not None:
self.latest_symbol_data[s].append(bar)
self.events.put(MarketEvent())
Dengan demikian kita memiliki DataHandler-derived object, yang digunakan oleh komponen yang tersisa untuk melacak data pasar.
Dalam artikel berikutnya kita akan membahas hierarki kelas Strategi dan menjelaskan bagaimana strategi dapat dirancang untuk menangani beberapa simbol, sehingga menghasilkan beberapa SignalEvents untuk objek Portfolio.