mhallsmoore / qstrader

QuantStart.com - QSTrader backtesting simulation engine.
https://www.quantstart.com/qstrader/
MIT License
2.91k stars 854 forks source link

Running of buy and hold example strategy generates NotImplementedError: Could not find signature for display: <long> #95

Closed andrexmt closed 8 years ago

andrexmt commented 8 years ago

After setting up QSTrader using the instructions in the readme file, I attempted to run the example buy_and_hold_backtest.py. This generated the error:


Traceback (most recent call last):
  File "C:\Users\Andre\Anaconda3\envs\python27_env_tradeEngine\Lib\site-packages\qstrader\examples\buy_and_hold_backtest.py", line 85, in <module>
    main()
  File "C:\Users\Andre\Anaconda3\envs\python27_env_tradeengine\lib\site-packages\click\core.py", line 716, in __call__
    return self.main(*args, **kwargs)
  File "C:\Users\Andre\Anaconda3\envs\python27_env_tradeengine\lib\site-packages\click\core.py", line 696, in main
    rv = self.invoke(ctx)
  File "C:\Users\Andre\Anaconda3\envs\python27_env_tradeengine\lib\site-packages\click\core.py", line 889, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "C:\Users\Andre\Anaconda3\envs\python27_env_tradeengine\lib\site-packages\click\core.py", line 534, in invoke
    return callback(*args, **kwargs)
  File "C:\Users\Andre\Anaconda3\envs\python27_env_tradeEngine\Lib\site-packages\qstrader\examples\buy_and_hold_backtest.py", line 81, in main
    run(config, testing, tickers, filename)
  File "C:\Users\Andre\Anaconda3\envs\python27_env_tradeEngine\Lib\site-packages\qstrader\examples\buy_and_hold_backtest.py", line 65, in run
    results = backtest.simulate_trading(testing=testing)
  File "C:\Users\Andre\Anaconda3\envs\python27_env_tradeengine\lib\site-packages\qstrader\trading_session\backtest.py", line 68, in simulate_trading
    self._run_backtest()
  File "C:\Users\Andre\Anaconda3\envs\python27_env_tradeengine\lib\site-packages\qstrader\trading_session\backtest.py", line 54, in _run_backtest
    self.statistics.update(event.time, self.portfolio_handler)
  File "C:\Users\Andre\Anaconda3\envs\python27_env_tradeengine\lib\site-packages\qstrader\statistics\simple.py", line 50, in update
    current_equity = PriceParser.display(portfolio_handler.portfolio.equity)
  File "C:\Users\Andre\Anaconda3\envs\python27_env_tradeengine\lib\site-packages\multipledispatch\dispatcher.py", line 161, in __call__
    (self.name, str_signature(types)))
NotImplementedError: Could not find signature for display: <long>

I added the below code to the price_parser.py file and the backtest worked. However, the results were not correct; I suspect it is due to my introduction of the below code.

    @staticmethod
    @dispatch(long)
    def display(x):
        return round(x / PriceParser.PRICE_MULTIPLIER, 2)

What would the correct solution to the original problem be? The platform is Windows+Anaconda (Python 2.7)

Thanks.

mhallsmoore commented 8 years ago

Hi Drex91,

I'm going to have access to my Windows machine briefly next week (currently only have access to my Mac laptop!), so I'll try and replicate the error and then fix.

Although, @femtotrader and @ryankennedyio are the experts with regards multiple dispatch, as they jointly built up a lot of code for this aspect, so they might have more insight before then!

Cheers,

Mike.

femtotrader commented 8 years ago

My version of Python is

$ python --version
Python 3.5.2 :: Anaconda 4.1.1 (x86_64)

under Mac OS 10.10.5 (64 bits)

Are you using 32 bits or 64 bits OS ?

This may help http://stackoverflow.com/questions/2104884/how-does-python-manage-int-and-long

femtotrader commented 8 years ago

union-types are supported by multiple-dispatch http://multiple-dispatch.readthedocs.io/en/latest/resolution.html#union-types

maybe we should define in price_parser.py

int_t = (int, long)

and replace (nearly) every int by int_t

so code could be:

from __future__ import division
from multipledispatch import dispatch

int_t = (int, long)

class PriceParser(object):
    """
    PriceParser is designed to abstract away the underlying number used as a price
    within qstrader. Due to efficiency and floating point precision limitations,
    QSTrader uses an integer to represent all prices. This means that $0.10 is,
    internally, 10,000,000. Because such large numbers are rather unwieldy
    for humans, the PriceParser will take "normal" 2dp numbers as input, and show
    "normal" 2dp numbers as output when requested to `display()`

    For consistency's sake, PriceParser should be used for ALL prices that enter
    the qstrader system. Numbers should also always be parsed correctly to view.

    """

    # 10,000,000
    PRICE_MULTIPLIER = 10000000

    @staticmethod
    @dispatch(int_t)
    def parse(x):  # flake8: noqa
        return x

    @staticmethod
    @dispatch(float)
    def parse(x):  # flake8: noqa
        return int(x * PriceParser.PRICE_MULTIPLIER)

    @staticmethod
    @dispatch(int_t)
    def display(x):  # flake8: noqa
        return round(x / PriceParser.PRICE_MULTIPLIER, 2)

    @staticmethod
    @dispatch(float)
    def display(x):  # flake8: noqa
        return round(x, 2)

    @staticmethod
    @dispatch(int_t, int_t)
    def display(x, dp):  # flake8: noqa
        return round(x / PriceParser.PRICE_MULTIPLIER, dp)

    @staticmethod
    @dispatch(float, int_t)
    def display(x, dp):  # flake8: noqa
        return round(x, dp)

but I'm not sure of results as I haven't been able to reproduce this issue.

femtotrader commented 8 years ago

in fact long doesn't exist with Python 3 but exists with Python 2

Maybe we might use six here https://pythonhosted.org/six/

six.integer_types
Possible integer types. In Python 2, this is long and int, and in Python 3, just int.

add so

import six

int_t = six.integer_types