Ejemplo n.º 1
0
    def _from_db(self,
                 ticker: str,
                 source: str,
                 start: dt.datetime,
                 end: dt.datetime,
                 filter_data: bool = True,
                 **kwargs) -> DfLibName:
        """
        Try to read data from the DB.

        :param ticker: The ticker to retrieve from the DB.
        :param source: Only used if there there is not enough data in the DB.
        :param start: The start of the range.
        :param end: The end of the range.
        :param filter_data: Passed to the read method.
        :param kwargs: Passed to the read method.
        :return: The data frame.
        :raises: NoDataFoundException if no data is found for the given ticker.
        """
        chunk_range = DateRange(start=start, end=end)

        try:
            logger.info(f'Checking DB for ticker: {ticker}')
            df = self.lib.read(ticker,
                               chunk_range=chunk_range,
                               filter_data=filter_data,
                               **kwargs)
        except NoDataFoundException as e:
            raise DataAccessError(f'No data in DB for ticker: {ticker}') from e
        except KeyError as e:
            # TODO: open a bug report against arctic...
            logger.warning('KeyError thrown by Arctic...', e)
            raise DataAccessError(
                f'Error reading DB for ticker: {ticker}') from e

        logger.debug(f'Found ticker: {ticker} in DB.')

        db_start = dt_utils.parse_date(df.index.min(axis=1))
        db_end = dt_utils.parse_date(df.index.max(axis=1))

        # check that all the requested data is present
        # TODO: deal with days that it is expected that data shouldn't exist.
        if db_start > start and dt_utils.is_trade_day(start):
            # db has less data than requested
            lower_df_lib_name = self._from_web(ticker, source, start,
                                               db_start - BDay())
            lower_df = lower_df_lib_name.df
        else:
            lower_df = None

        if db_end.date() < end.date() and dt_utils.is_trade_day(end):
            # db doesn't have as much data than requested
            upper_df_lib_name = self._from_web(ticker, source, db_end, end)
            upper_df = upper_df_lib_name.df
        else:
            upper_df = None

        new_df = _concat_dfs(lower_df, upper_df, df)
        return DfLibName(new_df, self.lib_name)
Ejemplo n.º 2
0
    def __init__(self,
                 ticker_list,
                 initial_capital,
                 start_date,
                 strategy,
                 end_date=None,
                 data_handler=None,
                 execution_handler=None,
                 portfolio=None,
                 balancer=None):
        """
        Initialize the backtest.

        :param iterable ticker_list: A list of tickers.
        :param initial_capital: Amount of starting capital.
        :param start_date: The date to start the backtest as of.
        :param strategy: The strategy to backtest.
        :param data_handler:
        :param execution_handler:
        :param portfolio:
        """
        self.logger = logging.getLogger(__name__)
        self.ticker_list = com_utils.iterable_to_set(ticker_list)
        self.initial_capital = initial_capital
        self.start_date = dt_utils.parse_date(start_date)

        if end_date is None:
            self.end_date = dt.datetime.utcnow()
        else:
            self.end_date = dt_utils.parse_date(end_date)
        self.strategy_cls = strategy

        if data_handler is None:
            self.data_handler_cls = Bars
        else:
            self.data_handler_cls = data_handler

        if execution_handler is None:
            self.execution_handler_cls = SimpleExecutionHandler
        else:
            self.execution_handler_cls = execution_handler

        if portfolio is None:
            self.portfolio_cls = BasicPortfolio
        else:
            self.portfolio_cls = portfolio

        self.events = queue.Queue()

        self.blotter = Blotter(self.events)

        self.signals = 0
        self.orders = 0
        self.fills = 0
        self.num_strats = 1

        self._init_trading_instances()
Ejemplo n.º 3
0
 def __init__(self, order_id: str, price: float, available_volume: int,
              dt: datetime or str):
     super().__init__()
     self.order_id = order_id
     self.price = price
     self.available_volume = available_volume
     self.dt = dt_utils.parse_date(dt)
Ejemplo n.º 4
0
    def __init__(self, start_date=None, end_date=None, ticker='^GPSC'):

        if start_date is None:
            self.start_date = dt_utils.get_default_date(is_start_date=True)
        else:
            self.start_date = dt_utils.parse_date(start_date)

        if end_date is None:
            self.end_date = dt_utils.get_default_date(is_start_date=False)
        else:
            self.end_date = dt_utils.parse_date(end_date)

        self.ticker = ticker

        self.ohlcv = web.DataReader(ticker, 'yahoo', start=self.start_date,
                                    end=self.end_date)
Ejemplo n.º 5
0
 def __init__(self, order_id: str, price: float, qty: int, dt: datetime
              or str):
     super().__init__()
     self.order_id = order_id
     self.price = price
     self.qty = qty
     self.dt = dt_utils.parse_date(dt)
Ejemplo n.º 6
0
    def test_get_latest_bar(self, yahoo_data_handler):
        """
        Test getting the latest bar.

        :param Bars yahoo_data_handler:
        :return:
        """
        bar = yahoo_data_handler.get_latest_bar('AAPL')
        dt = dt_utils.parse_date(bar.name)
        adj_close = bar[pd_utils.CLOSE_COL]
        aapl_close_expected = 101.17
        assert dt == dt_utils.parse_date('2016-03-10')
        assert adj_close == approx(aapl_close_expected)
        yahoo_data_handler.update_bars()
        bar = yahoo_data_handler.get_latest_bar('AAPL')
        dt = dt_utils.parse_date(bar.name)
        assert dt == dt_utils.parse_date('2016-03-11')
Ejemplo n.º 7
0
    def test_get_latest_bar_dt(self, yahoo_data_handler):
        """
        Test that the latest date returned is correct.

        :param Bars yahoo_data_handler:
        """
        test_date = yahoo_data_handler.get_latest_bar_dt('AAPL')
        assert test_date == dt_utils.parse_date('2016-03-10')

        yahoo_data_handler.update_bars()

        test_date = yahoo_data_handler.get_latest_bar_dt('AAPL')
        assert test_date == dt_utils.parse_date('2016-03-11')

        yahoo_data_handler.update_bars()

        test_date = yahoo_data_handler.get_latest_bar_dt('AAPL')
        assert test_date == dt_utils.parse_date('2016-03-14')
Ejemplo n.º 8
0
    def __init__(self,
                 qty,
                 price_per_share,
                 action,
                 strategy,
                 order,
                 avg_price_per_share,
                 commission=0.0,
                 trade_date=None,
                 ticker=None):
        """
        :param datetime trade_date: corresponding to the date and time of the 
            trade date
        :param int qty: number of shares traded
        :param float price_per_share: price per individual share in the trade 
            or the average share price in the trade
        :param Asset ticker: a :py:class:`~.ticker.Asset`, the ``ticker`` 
            object that was traded
        :param Order order: a :py:class:`~.order.Order` that was executed as a 
            result of the order executing
        :param float commission: the amount of commission paid to execute this trade
            (default: 0.0)
        :param TradeAction or str action: :py:class:`~.enum.TradeAction`
        :param str position: must be *long* or *short*

        .. note::

        Valid **action** parameter values are:

        * TradeAction.BUY
        * TradeAction.SELL
        * BUY
        * SELL

        `commission` is not necessarily the same commission associated with the 
        ``order`` this will depend on the
        type of :class:``AbstractCommissionModel`` used.
        """

        if trade_date:
            self.trade_date = dt_utils.parse_date(trade_date)
        else:
            self.trade_date = pd.Timestamp(datetime.now())

        self.action = TradeAction.check_if_valid(action)
        self.strategy = strategy
        self.ticker = ticker
        self.qty = qty
        self.price_per_share = price_per_share
        self.order = order
        self.commission = commission
        self.avg_price_per_share = avg_price_per_share
        self.logger = logging.getLogger(self.LOGGER_NAME)
Ejemplo n.º 9
0
    def update_total_position_value(self, latest_price, price_date):
        """
        Set the ``latest_price`` and ``latest_price_time`` and
        update the total position's value to reflect the latest
        market value price.

        :param float latest_price:
        :param datetime or str price_date:
        :return:
        """
        self.latest_price = latest_price
        self.latest_price_time = dt_utils.parse_date(price_date)

        if self.position is Position.SHORT:
            self.total_position_value = (
                (self.latest_price * self.shares_owned) * -1)
        else:
            self.total_position_value = self.latest_price * self.shares_owned
Ejemplo n.º 10
0
    def __init__(self,
                 ticker,
                 shares_owned,
                 position,
                 average_share_price,
                 purchase_date=None):
        self.ticker = ticker
        self.position = Position.check_if_valid(position)

        if purchase_date is None:
            self.purchase_date = pd.Timestamp(datetime.now())
        else:
            self.purchase_date = dt_utils.parse_date(purchase_date)

        self.average_share_price_paid = average_share_price
        self.latest_price = average_share_price
        self.latest_price_time = self.purchase_date.time()
        self.total_position_value = 0
        self.total_position_cost = 0

        self.shares_owned = shares_owned
        self._set_position_cost_and_value(average_share_price)
Ejemplo n.º 11
0
 def __init__(self,
              data_handler: DataHandler,
              events: queue.Queue,
              start_date: datetime,
              blotter: Blotter,
              initial_capital: float = 100000.00,
              raise_on_warnings=False):
     self.logger = logging.getLogger(__name__)
     self.bars = data_handler
     self.events = events
     self.blotter = blotter
     self.start_date = dt_utils.parse_date(start_date)
     self.initial_capital = initial_capital
     self.cash = initial_capital
     self.ticker_list = self.bars.tickers
     self.owned_assets = {}
     # holdings = mv
     self.all_holdings_mv = self._construct_all_holdings()
     # positions = qty
     self.all_positions_qty = self._construct_all_positions()
     self.total_commission = 0.0
     self.lib = ARCTIC_STORE['pytech.portfolio']
     self.positions_df = pd.DataFrame()
     self.raise_on_warnings = raise_on_warnings
Ejemplo n.º 12
0
    def __init__(self,
                 ticker: str,
                 action: TradeAction,
                 qty: int,
                 order_subtype: OrderSubType = None,
                 created: datetime = None,
                 max_days_open: int = None,
                 order_id: str = None,
                 *args,
                 **kwargs):
        """
        Order constructor

        :param ticker: The ticker for which the order is associated with.
            This can either be an instance of an
            :class:`pytech.fin.ticker.Asset` or a string with of ticker
            of the ticker. If an ticker is passed in the ticker
            will be taken from it.
        :type ticker: Asset or str
        :param TradeAction action: Either BUY or SELL
        :param OrderSubType order_subtype: The order subtype to create
        default: :py:class:`pytech.enums.OrderSubType.DAY`
        :param int qty: The amount of shares the order is for.
        This should be negative if it is a sell order and positive if it is
        a buy order.
        :param datetime created: The date and time that the order was created
        :param int max_days_open: The max calendar days that an order can stay
            open without being cancelled.
            This parameter is not relevant to Day orders since they will be
            closed at the end of the day regardless.
            (default: None if the order_type is Day)
            (default: 90 if the order_type is not Day)
        :param str order_id: A uuid hex
        :raises NotAnAssetError: If the ticker passed in is not an ticker
        :raises InvalidActionError: If the action passed in is not a valid action
        :raises NotAPortfolioError: If the portfolio passed in is not a portfolio

        NOTES
        -----
        See :class:`pytech.enums.OrderType` to see valid order types
        See :class:`pytech.enums.OrderSubType` to see valid order sub types
        See :py:func:`asymmetric_round_price_to_penny` for more information on how
            `stop_price` and `limit_price` will get rounded.
        """
        super().__init__()
        self.id = order_id or utils.make_id()
        self.ticker = ticker
        self.logger = logging.getLogger('{}_ticker_{}'.format(
            self.__class__.__name__, self.ticker))

        # TODO: validate that all of these inputs make sense together.
        # e.g. if its a stop order stop shouldn't be none
        self.action = TradeAction.check_if_valid(action)

        if order_subtype is not None:
            self.order_subtype = OrderSubType.check_if_valid(order_subtype)
        else:
            self.order_subtype = OrderSubType.DAY

        if self.order_subtype is OrderSubType.DAY:
            self.max_days_open = 1
        elif max_days_open is None:
            self.max_days_open = 90
        else:
            self.max_days_open = math.floor(max_days_open)

        self.qty = qty
        # How much commission has already been charged on the order.
        self.commission = 0.0
        self.filled = 0
        self._status = OrderStatus.OPEN
        self.reason = None

        if created is not None:
            self.created = dt_utils.parse_date(created)
        else:
            self.created = pd.Timestamp(datetime.now())

        # the last time the order changed
        self.last_updated = self.created
        self.close_date = None