From b9b4dc09a4315e6a03d2abaaa1e8bde23892aa1f Mon Sep 17 00:00:00 2001 From: Richard Frank Date: Mon, 16 May 2016 12:36:49 -0400 Subject: [PATCH] DEV: Adds type hinting stub for zipline.api as well as tooling and docs to generate this for each release Also moved Cython files to package_data, so that we install them, instead of just packaging them. --- MANIFEST.in | 2 - docs/source/release-process.rst | 26 ++ etc/gen_type_stubs.py | 42 ++ setup.py | 6 +- zipline/api.pyi | 688 ++++++++++++++++++++++++++++++++ 5 files changed, 761 insertions(+), 3 deletions(-) create mode 100644 etc/gen_type_stubs.py create mode 100644 zipline/api.pyi diff --git a/MANIFEST.in b/MANIFEST.in index 5911974b..7e1e1870 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,7 +2,5 @@ include LICENSE include etc/requirements*.txt recursive-include zipline/resources *.* -recursive-include zipline *.pyx -recursive-include zipline *.pxi include versioneer.py include zipline/_version.py diff --git a/docs/source/release-process.rst b/docs/source/release-process.rst index e766d678..a33e83c0 100644 --- a/docs/source/release-process.rst +++ b/docs/source/release-process.rst @@ -27,6 +27,32 @@ update the underline of the title to match the title's width. If you are renaming the release at this point, you'll need to git mv the file and also update releases.rst to reference the renamed file. +Updating the Python stub files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +PyCharm and other linters and type checkers can use `Python stub files +`__ for type hinting. For +example, we generate stub files for the :mod:`~zipline.api` namespace, since that +namespace is populated at import time by decorators on TradingAlgorithm +methods. Those functions are therefore hidden from static analysis tools, but +we can generate static files to make them available. Under **Python 3**, run +the following to generate any stub files: + +.. code-block:: bash + + $ python etc/gen_type_stubs.py + +.. note:: + + In order to make stub consumers aware of the classes referred to in the + stub, the stub file should import those classes. However, since + ``... import *`` and ``... import ... as ...`` in a stub file will export + those imports, we import the names explicitly. For the stub for + ``zipline.api``, this is done in a header string in the + ``gen_type_stubs.py`` script mentioned above. If new classes are added as + parameters or return types of ``zipline.api`` functions, then new imports + should be added to that header. + Updating the ``__version__`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/etc/gen_type_stubs.py b/etc/gen_type_stubs.py new file mode 100644 index 00000000..32922cc5 --- /dev/null +++ b/etc/gen_type_stubs.py @@ -0,0 +1,42 @@ +import inspect +from operator import attrgetter +from textwrap import dedent + +from zipline import api, TradingAlgorithm + + +def main(): + with open(api.__file__.rstrip('c') + 'i', 'w') as stub: + # Imports so that Asset et al can be resolved. + # "from MOD import *" will re-export the imports from the stub, so + # explicitly importing. + stub.write(dedent("""\ + from zipline.assets import Asset, Equity, Future + from zipline.assets.futures import FutureChain + from zipline.finance.cancel_policy import CancelPolicy + from zipline.pipeline import Pipeline + from zipline.protocol import Order + from zipline.utils.events import EventRule + + + """)) + + # Sort to generate consistent stub file: + for api_func in sorted(TradingAlgorithm.all_api_methods(), + key=attrgetter('__name__')): + sig = inspect._signature_bound_method(inspect.signature(api_func)) + + indent = ' ' * 4 + stub.write(dedent('''\ + def {func_name}{func_sig}: + """'''.format(func_name=api_func.__name__, + func_sig=sig))) + stub.write(dedent('{indent}{func_doc}'.format( + func_doc=api_func.__doc__ or '\n', # handle None docstring + indent=indent, + ))) + stub.write('{indent}"""\n\n'.format(indent=indent)) + + +if __name__ == '__main__': + main() diff --git a/setup.py b/setup.py index 3ec52cfa..ee44d023 100644 --- a/setup.py +++ b/setup.py @@ -270,9 +270,13 @@ setup( }, author='Quantopian Inc.', author_email='opensource@quantopian.com', - packages=find_packages('.', include=['zipline', 'zipline.*']), + packages=find_packages(include=['zipline', 'zipline.*']), ext_modules=ext_modules, include_package_data=True, + package_data={root.replace(os.sep, '.'): + ['*.pyi', '*.pyx', '*.pxi', '*.pxd'] + for root, dirnames, filenames in os.walk('zipline') + if '__pycache__' not in root}, license='Apache 2.0', classifiers=[ 'Development Status :: 4 - Beta', diff --git a/zipline/api.pyi b/zipline/api.pyi new file mode 100644 index 00000000..bb7faac5 --- /dev/null +++ b/zipline/api.pyi @@ -0,0 +1,688 @@ +from zipline.assets import Asset, Equity, Future +from zipline.assets.futures import FutureChain +from zipline.finance.cancel_policy import CancelPolicy +from zipline.pipeline import Pipeline +from zipline.protocol import Order +from zipline.utils.events import EventRule + + +def attach_pipeline(pipeline, name, chunksize=None): + """Register a pipeline to be computed at the start of each day. + + Parameters + ---------- + pipeline : Pipeline + The pipeline to have computed. + name : str + The name of the pipeline. + chunksize : int, optional + The number of days to compute pipeline results for. Increasing + this number will make it longer to get the first results but + may improve the total runtime of the simulation. + + Returns + ------- + pipeline : Pipeline + Returns the pipeline that was attached unchanged. + + See Also + -------- + :func:`zipline.api.pipeline_output` + """ + +def cancel_order(order_param): + """Cancel an open order. + + Parameters + ---------- + order_param : str or Order + The order_id or order object to cancel. + """ + +def fetch_csv(url, pre_func=None, post_func=None, date_column='date', date_format=None, timezone='UTC', symbol=None, mask=True, symbol_column=None, special_params_checker=None, **kwargs): + """Fetch a csv from a remote url and register the data so that it is + queryable from the ``data`` object. + + Parameters + ---------- + url : str + The url of the csv file to load. + pre_func : callable[pd.DataFrame -> pd.DataFrame], optional + A callback to allow preprocessing the raw data returned from + fetch_csv before dates are paresed or symbols are mapped. + post_func : callable[pd.DataFrame -> pd.DataFrame], optional + A callback to allow postprocessing of the data after dates and + symbols have been mapped. + date_column : str, optional + The name of the column in the preprocessed dataframe containing + datetime information to map the data. + date_format : str, optional + The format of the dates in the ``date_column``. If not provided + ``fetch_csv`` will attempt to infer the format. For information + about the format of this string, see :func:`pandas.read_csv`. + timezone : tzinfo or str, optional + The timezone for the datetime in the ``date_column``. + symbol : str, optional + If the data is about a new asset or index then this string will + be the name used to identify the values in ``data``. For example, + one may use ``fetch_csv`` to load data for VIX, then this field + could be the string ``'VIX'``. + mask : bool, optional + Drop any rows which cannot be symbol mapped. + symbol_column : str + If the data is attaching some new attribute to each asset then this + argument is the name of the column in the preprocessed dataframe + containing the symbols. This will be used along with the date + information to map the sids in the asset finder. + **kwargs + Forwarded to :func:`pandas.read_csv`. + + Returns + ------- + csv_data_source : zipline.sources.requests_csv.PandasRequestsCSV + A requests source that will pull data from the url specified. + """ + +def future_chain(root_symbol, as_of_date=None): + """Look up a future chain with the specified parameters. + + Parameters + ---------- + root_symbol : str + The root symbol of a future chain. + as_of_date : datetime.datetime or pandas.Timestamp or str, optional + Date at which the chain determination is rooted. I.e. the + existing contract whose notice date is first after this date is + the primary contract, etc. + + Returns + ------- + chain : FutureChain + The future chain matching the specified parameters. + + Raises + ------ + RootSymbolNotFound + If a future chain could not be found for the given root symbol. + """ + +def future_symbol(symbol): + """Lookup a futures contract with a given symbol. + + Parameters + ---------- + symbol : str + The symbol of the desired contract. + + Returns + ------- + future : Future + The future that trades with the name ``symbol``. + + Raises + ------ + SymbolNotFound + Raised when no contract named 'symbol' is found. + """ + +def get_datetime(tz=None): + """Returns the current simulation datetime. + + Parameters + ---------- + tz : tzinfo or str, optional + The timezone to return the datetime in. This defaults to utc. + + Returns + ------- + dt : datetime + The current simulation datetime converted to ``tz``. + """ + +def get_environment(field='platform'): + """Query the execution environment. + + Parameters + ---------- + field : {'platform', 'arena', 'data_frequency', + 'start', 'end', 'capital_base', 'platform', '*'} + The field to query. The options have the following meanings: + arena : str + The arena from the simulation parameters. This will normally + be ``'backtest'`` but some systems may use this distinguish + live trading from backtesting. + data_frequency : {'daily', 'minute'} + data_frequency tells the algorithm if it is running with + daily data or minute data. + start : datetime + The start date for the simulation. + end : datetime + The end date for the simulation. + capital_base : float + The starting capital for the simulation. + platform : str + The platform that the code is running on. By default this + will be the string 'zipline'. This can allow algorithms to + know if they are running on the Quantopian platform instead. + * : dict[str -> any] + Returns all of the fields in a dictionary. + + Returns + ------- + val : any + The value for the field queried. See above for more information. + + Raises + ------ + ValueError + Raised when ``field`` is not a valid option. + """ + +def get_order(order_id): + """Lookup an order based on the order id returned from one of the + order functions. + + Parameters + ---------- + order_id : str + The unique identifier for the order. + + Returns + ------- + order : Order + The order object. + """ + +def history(bar_count, frequency, field, ffill=True): + """DEPRECATED: use ``data.history`` instead. + """ + +def order(asset, amount, limit_price=None, stop_price=None, style=None): + """Place an order. + + Parameters + ---------- + asset : Asset + The asset that this order is for. + amount : int + The amount of shares to order. If ``amount`` is positive, this is + the number of shares to buy or cover. If ``amount`` is negative, + this is the number of shares to sell or short. + style : ExecutionStyle, optional + The execution style for the order. + + Returns + ------- + order_id : str + The unique identifier for this order. + + See Also + -------- + :class:`zipline.finance.execution.ExecutionStyle` + :func:`zipline.api.order_value` + :func:`zipline.api.order_percent` + """ + +def order_percent(asset, percent, limit_price=None, stop_price=None, style=None): + """Place an order in the specified asset corresponding to the given + percent of the current portfolio value. + + Parameters + ---------- + asset : Asset + The asset that this order is for. + percent : float + The percentage of the porfolio value to allocate to ``asset``. + This is specified as a decimal, for example: 0.50 means 50%. + style : ExecutionStyle + The execution style for the order. + + Returns + ------- + order_id : str + The unique identifier for this order. + + See Also + -------- + :class:`zipline.finance.execution.ExecutionStyle` + :func:`zipline.api.order` + :func:`zipline.api.order_value` + """ + +def order_target(asset, target, limit_price=None, stop_price=None, style=None): + """Place an order to adjust a position to a target number of shares. If + the position doesn't already exist, this is equivalent to placing a new + order. If the position does exist, this is equivalent to placing an + order for the difference between the target number of shares and the + current number of shares. + + Parameters + ---------- + asset : Asset + The asset that this order is for. + target : int + The desired number of shares of ``asset``. + style : ExecutionStyle + The execution style for the order. + + Returns + ------- + order_id : str + The unique identifier for this order. + + + Notes + ----- + ``order_target`` does not take into account any open orders. For + example: + + .. code-block:: python + + order_target(sid(0), 10) + order_target(sid(0), 10) + + This code will result in 20 shares of ``sid(0)`` because the first + call to ``order_target`` will not have been filled when the second + ``order_target`` call is made. + + See Also + -------- + :class:`zipline.finance.execution.ExecutionStyle` + :func:`zipline.api.order` + :func:`zipline.api.order_target_percent` + :func:`zipline.api.order_target_value` + """ + +def order_target_percent(asset, target, limit_price=None, stop_price=None, style=None): + """Place an order to adjust a position to a target percent of the + current portfolio value. If the position doesn't already exist, this is + equivalent to placing a new order. If the position does exist, this is + equivalent to placing an order for the difference between the target + percent and the current percent. + + Parameters + ---------- + asset : Asset + The asset that this order is for. + percent : float + The desired percentage of the porfolio value to allocate to + ``asset``. This is specified as a decimal, for example: + 0.50 means 50%. + style : ExecutionStyle + The execution style for the order. + + Returns + ------- + order_id : str + The unique identifier for this order. + + Notes + ----- + ``order_target_value`` does not take into account any open orders. For + example: + + .. code-block:: python + + order_target_percent(sid(0), 10) + order_target_percent(sid(0), 10) + + This code will result in 20% of the portfolio being allocated to sid(0) + because the first call to ``order_target_percent`` will not have been + filled when the second ``order_target_percent`` call is made. + + See Also + -------- + :class:`zipline.finance.execution.ExecutionStyle` + :func:`zipline.api.order` + :func:`zipline.api.order_target` + :func:`zipline.api.order_target_value` + """ + +def order_target_value(asset, target, limit_price=None, stop_price=None, style=None): + """Place an order to adjust a position to a target value. If + the position doesn't already exist, this is equivalent to placing a new + order. If the position does exist, this is equivalent to placing an + order for the difference between the target value and the + current value. + If the Asset being ordered is a Future, the 'target value' calculated + is actually the target exposure, as Futures have no 'value'. + + Parameters + ---------- + asset : Asset + The asset that this order is for. + target : float + The desired total value of ``asset``. + style : ExecutionStyle + The execution style for the order. + + Returns + ------- + order_id : str + The unique identifier for this order. + + Notes + ----- + ``order_target_value`` does not take into account any open orders. For + example: + + .. code-block:: python + + order_target_value(sid(0), 10) + order_target_value(sid(0), 10) + + This code will result in 20 dollars of ``sid(0)`` because the first + call to ``order_target_value`` will not have been filled when the + second ``order_target_value`` call is made. + + See Also + -------- + :class:`zipline.finance.execution.ExecutionStyle` + :func:`zipline.api.order` + :func:`zipline.api.order_target` + :func:`zipline.api.order_target_percent` + """ + +def order_value(asset, value, limit_price=None, stop_price=None, style=None): + """Place an order by desired value rather than desired number of + shares. + + Parameters + ---------- + asset : Asset + The asset that this order is for. + value : float + If the requested asset exists, the requested value is + divided by its price to imply the number of shares to transact. + If the Asset being ordered is a Future, the 'value' calculated + is actually the exposure, as Futures have no 'value'. + + value > 0 :: Buy/Cover + value < 0 :: Sell/Short + style : ExecutionStyle + The execution style for the order. + + Returns + ------- + order_id : str + The unique identifier for this order. + + See Also + -------- + :class:`zipline.finance.execution.ExecutionStyle` + :func:`zipline.api.order` + :func:`zipline.api.order_percent` + """ + +def pipeline_output(name): + """Get the results of the pipeline that was attached with the name: + ``name``. + + Parameters + ---------- + name : str + Name of the pipeline for which results are requested. + + Returns + ------- + results : pd.DataFrame + DataFrame containing the results of the requested pipeline for + the current simulation date. + + Raises + ------ + NoSuchPipeline + Raised when no pipeline with the name `name` has been registered. + + See Also + -------- + :func:`zipline.api.attach_pipeline` + :meth:`zipline.pipeline.engine.PipelineEngine.run_pipeline` + """ + +def record(*args, **kwargs): + """Track and record values each day. + + Parameters + ---------- + **kwargs + The names and values to record. + + Notes + ----- + These values will appear in the performance packets and the performance + dataframe passed to ``analyze`` and returned from + :func:`~zipline.run_algorithm`. + """ + +def schedule_function(func, date_rule=None, time_rule=None, half_days=True): + """Schedules a function to be called according to some timed rules. + + Parameters + ---------- + func : callable[(context, data) -> None] + The function to execute when the rule is triggered. + date_rule : EventRule, optional + The rule for the dates to execute this function. + time_rule : EventRule, optional + The rule for the times to execute this function. + half_days : bool, optional + Should this rule fire on half days? + + See Also + -------- + :class:`zipline.api.date_rules` + :class:`zipline.api.time_rules` + """ + +def set_benchmark(benchmark): + """Set the benchmark asset. + + Parameters + ---------- + benchmark : Asset + The asset to set as the new benchmark. + + Notes + ----- + Any dividends payed out for that new benchmark asset will be + automatically reinvested. + """ + +def set_cancel_policy(cancel_policy): + """Sets the order cancellation policy for the simulation. + + Parameters + ---------- + cancel_policy : CancelPolicy + The cancellation policy to use. + + See Also + -------- + :class:`zipline.api.EODCancel` + :class:`zipline.api.NeverCancel` + """ + +def set_commission(commission): + """Sets the commision model for the simulation. + + Parameters + ---------- + commision : PerShare, PerTrade, or PerDollar + The commision model to use. + + See Also + -------- + :class:`zipline.finance.commission.PerShare` + :class:`zipline.finance.commission.PerTrade` + :class:`zipline.finance.commission.PerDollar` + """ + +def set_do_not_order_list(restricted_list): + """Set a restriction on which assets can be ordered. + + Parameters + ---------- + restricted_list : container[Asset] + The assets that cannot be ordered. + """ + +def set_long_only(): + """Set a rule specifying that this algorithm cannot take short + positions. + """ + +def set_max_leverage(max_leverage): + """Set a limit on the maximum leverage of the algorithm. + + Parameters + ---------- + max_leverage : float + The maximum leverage for the algorithm. If not provided there will + be no maximum. + """ + +def set_max_order_count(max_count): + """Set a limit on the number of orders that can be placed in a single + day. + + Parameters + ---------- + max_count : int + The maximum number of orders that can be placed on any single day. + """ + +def set_max_order_size(asset=None, max_shares=None, max_notional=None): + """Set a limit on the number of shares and/or dollar value of any single + order placed for sid. Limits are treated as absolute values and are + enforced at the time that the algo attempts to place an order for sid. + + If an algorithm attempts to place an order that would result in + exceeding one of these limits, raise a TradingControlException. + + Parameters + ---------- + asset : Asset, optional + If provided, this sets the guard only on positions in the given + asset. + max_shares : int, optional + The maximum number of shares that can be ordered at one time. + max_notional : float, optional + The maximum value that can be ordered at one time. + """ + +def set_max_position_size(asset=None, max_shares=None, max_notional=None): + """Set a limit on the number of shares and/or dollar value held for the + given sid. Limits are treated as absolute values and are enforced at + the time that the algo attempts to place an order for sid. This means + that it's possible to end up with more than the max number of shares + due to splits/dividends, and more than the max notional due to price + improvement. + + If an algorithm attempts to place an order that would result in + increasing the absolute value of shares/dollar value exceeding one of + these limits, raise a TradingControlException. + + Parameters + ---------- + asset : Asset, optional + If provided, this sets the guard only on positions in the given + asset. + max_shares : int, optional + The maximum number of shares to hold for an asset. + max_notional : float, optional + The maximum value to hold for an asset. + """ + +def set_slippage(slippage): + """Set the slippage model for the simulation. + + Parameters + ---------- + slippage : SlippageModel + The slippage model to use. + + See Also + -------- + :class:`zipline.finance.slippage.SlippageModel` + """ + +def set_symbol_lookup_date(dt): + """Set the date for which symbols will be resolved to their assets + (symbols may map to different firms or underlying assets at + different times) + + Parameters + ---------- + dt : datetime + The new symbol lookup date. + """ + +def sid(sid): + """Lookup an Asset by its unique asset identifier. + + Parameters + ---------- + sid : int + The unique integer that identifies an asset. + + Returns + ------- + asset : Asset + The asset with the given ``sid``. + + Raises + ------ + SidsNotFound + When a requested ``sid`` does not map to any asset. + """ + +def symbol(symbol_str): + """Lookup an Equity by its ticker symbol. + + Parameters + ---------- + symbol_str : str + The ticker symbol for the equity to lookup. + + Returns + ------- + equity : Equity + The equity that held the ticker symbol on the current + symbol lookup date. + + Raises + ------ + SymbolNotFound + Raised when the symbols was not held on the current lookup date. + + See Also + -------- + :func:`zipline.api.set_symbol_lookup_date` + """ + +def symbols(*args): + """Lookup multuple Equities as a list. + + Parameters + ---------- + *args : iterable[str] + The ticker symbols to lookup. + + Returns + ------- + equities : list[Equity] + The equities that held the given ticker symbols on the current + symbol lookup date. + + Raises + ------ + SymbolNotFound + Raised when one of the symbols was not held on the current + lookup date. + + See Also + -------- + :func:`zipline.api.set_symbol_lookup_date` + """ +