# # Copyright 2014 Quantopian, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import sys import os import argparse from copy import copy from six import print_ from six.moves import configparser import pandas as pd try: from pygments import highlight from pygments.lexers import PythonLexer from pygments.formatters import TerminalFormatter PYGMENTS = True except: PYGMENTS = False import zipline from zipline.errors import NoSourceError, PipelineDateError DEFAULTS = { 'data_frequency': 'daily', 'capital_base': '10e6', 'source': 'yahoo', 'symbols': 'AAPL', 'metadata_index': 'symbol', 'source_time_column': 'Date', } def parse_args(argv, ipython_mode=False): """Parse list of arguments. If a config file is provided (via -c), it will read in the supplied options and overwrite any global defaults. All other directly supplied arguments will overwrite the config file settings. Arguments: * argv : list of strings List of arguments, e.g. ['-c', 'my.conf'] * ipython_mode : bool Whether to parse IPython specific arguments like --local_namespace Notes: Default settings can be found in zipline.utils.cli.DEFAULTS. """ # Parse any conf_file specification # We make this parser with add_help=False so that # it doesn't parse -h and print help. conf_parser = argparse.ArgumentParser( # Don't mess with format of description formatter_class=argparse.RawDescriptionHelpFormatter, # Turn off help, so we print all options in response to -h add_help=False ) conf_parser.add_argument("-c", "--conf_file", help="Specify config file", metavar="FILE") args, remaining_argv = conf_parser.parse_known_args(argv) defaults = copy(DEFAULTS) if args.conf_file: config = configparser.SafeConfigParser() config.read([args.conf_file]) defaults.update(dict(config.items("Defaults"))) # Parse rest of arguments # Don't suppress add_help here so it will handle -h parser = argparse.ArgumentParser( # Inherit options from config_parser description="Zipline version %s." % zipline.__version__, parents=[conf_parser] ) parser.set_defaults(**defaults) parser.add_argument('--algofile', '-f') parser.add_argument('--data-frequency', choices=('minute', 'daily')) parser.add_argument('--start', '-s') parser.add_argument('--end', '-e') parser.add_argument('--capital_base') parser.add_argument('--source', '-d', choices=('yahoo',)) parser.add_argument('--source_time_column', '-t') parser.add_argument('--symbols') parser.add_argument('--output', '-o') parser.add_argument('--metadata_path', '-m') parser.add_argument('--metadata_index', '-x') parser.add_argument('--print-algo', '-p', dest='print_algo', action='store_true') parser.add_argument('--no-print-algo', '-q', dest='print_algo', action='store_false') if ipython_mode: parser.add_argument('--local_namespace', action='store_true') args = parser.parse_args(remaining_argv) return(vars(args)) def parse_cell_magic(line, cell): """Parse IPython magic """ args_list = line.split(' ') args = parse_args(args_list, ipython_mode=True) # Remove print_algo kwarg to overwrite below. args.pop('print_algo') local_namespace = args.pop('local_namespace', False) # By default, execute inside IPython namespace if not local_namespace: args['namespace'] = get_ipython().user_ns # flake8: noqa # If we are running inside NB, do not output to file but create a # variable instead output_var_name = args.pop('output', None) perf = run_pipeline(print_algo=False, algo_text=cell, **args) if output_var_name is not None: get_ipython().user_ns[output_var_name] = perf # flake8: noqa def run_pipeline(print_algo=True, **kwargs): """Runs a full zipline pipeline given configuration keyword arguments. 1. Load data (start and end dates can be provided a strings as well as the source and symobls). 2. Instantiate algorithm (supply either algo_text or algofile kwargs containing initialize() and handle_data() functions). If algofile is supplied, will try to look for algofile_analyze.py and append it. 3. Run algorithm (supply capital_base as float). 4. Return performance dataframe. :Arguments: * print_algo : bool Whether to print the algorithm to command line. Will use pygments syntax coloring if pygments is found. """ start = kwargs['start'] end = kwargs['end'] # Compare against None because strings/timestamps may have been given if start is not None: start = pd.Timestamp(start, tz='UTC') if end is not None: end = pd.Timestamp(end, tz='UTC') # Fail out if only one bound is provided if ((start is None) or (end is None)) and (start != end): raise PipelineDateError(start=start, end=end) # Check if start and end are provided, and if the sim_params need to read # a start and end from the DataSource if start is None: overwrite_sim_params = True else: overwrite_sim_params = False symbols = kwargs['symbols'].split(',') asset_identifier = kwargs['metadata_index'] # Pull asset metadata asset_metadata = kwargs.get('asset_metadata', None) asset_metadata_path = kwargs['metadata_path'] # Read in a CSV file, if applicable if asset_metadata_path is not None: if os.path.isfile(asset_metadata_path): asset_metadata = pd.read_csv(asset_metadata_path, index_col=asset_identifier) source_arg = kwargs['source'] source_time_column = kwargs['source_time_column'] if source_arg is None: raise NoSourceError() elif source_arg == 'yahoo': source = zipline.data.load_bars_from_yahoo( stocks=symbols, start=start, end=end) elif os.path.isfile(source_arg): source = zipline.data.load_prices_from_csv( filepath=source_arg, identifier_col=source_time_column ) elif os.path.isdir(source_arg): source = zipline.data.load_prices_from_csv_folder( folderpath=source_arg, identifier_col=source_time_column ) else: raise NotImplementedError( 'Source %s not implemented.' % kwargs['source']) algo_text = kwargs.get('algo_text', None) if algo_text is None: # Expect algofile to be set algo_fname = kwargs['algofile'] with open(algo_fname, 'r') as fd: algo_text = fd.read() if print_algo: if PYGMENTS: highlight(algo_text, PythonLexer(), TerminalFormatter(), outfile=sys.stdout) else: print_(algo_text) algo = zipline.TradingAlgorithm(script=algo_text, namespace=kwargs.get('namespace', {}), capital_base=float(kwargs['capital_base']), algo_filename=kwargs.get('algofile'), equities_metadata=asset_metadata, identifiers=symbols, start=start, end=end) perf = algo.run(source, overwrite_sim_params=overwrite_sim_params) output_fname = kwargs.get('output', None) if output_fname is not None: perf.to_pickle(output_fname) return perf