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)
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()
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)
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)
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)
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')
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')
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)
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
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)
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
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