예제 #1
0
    def standard_symbol_query(self, market_client):

        start_epoch_s = IntuitiveDateConverter.to_epoch_s(
            STOCKS_DAILY['AAPL']['start'])
        end_epoch_s = IntuitiveDateConverter.to_epoch_s(
            STOCKS_DAILY['AAPL']['end'])
        moments = market_client.get_moments(symbols=['AAPL'],
                                            start_epoch_s=start_epoch_s,
                                            end_epoch_s=end_epoch_s)

        self.assertEqual(len(STOCKS_DAILY['AAPL']['records']), len(moments))

        for record in STOCKS_DAILY['AAPL']['records']:
            record['symbol'] = 'AAPL'
            moment = Moment.from_dict(record)
            self.assertIn(moment, moments)
    def test_add_later_moment(self):

        moment_later = Moment.from_dict(
            STOCKS_DAILY['AAPL']['records'][self.I_RECORD_LATER])
        self.day_series.add_moment(moment_later)
        moment_1 = Moment.from_dict(
            STOCKS_DAILY['AAPL']['records'][self.I_RECORD_1])
        self.day_series.add_moment(moment_1)
        moment_earlier = Moment.from_dict(
            STOCKS_DAILY['AAPL']['records'][self.I_RECORD_EARLIER])
        self.day_series.add_moment(moment_earlier)

        expected_timestamps = [
            IntuitiveDateConverter.to_epoch_s(x) for x in [
                STOCKS_DAILY['AAPL']['records'][self.I_RECORD_EARLIER]
                ['epoch_ms'], STOCKS_DAILY['AAPL']['records'][self.I_RECORD_1]
                ['epoch_ms'], STOCKS_DAILY['AAPL']['records'][
                    self.I_RECORD_LATER]['epoch_ms']
            ]
        ]

        expected_moments = [moment_earlier, moment_1, moment_later]

        self.assertEqual(expected_timestamps, self.day_series.timestamps)
        self.assertEqual(
            expected_moments,
            [self.day_series.moments[x] for x in self.day_series.timestamps])
예제 #3
0
 def __init__(self, data_ingest_manager: DataIngestManager,
              traders: List[Trader]) -> List:
     self.data_ingest_manager = data_ingest_manager
     self.traders = traders
     self.today_day_str = IntuitiveDateConverter.to_day_str(datetime.now())
     self.today_epoch_s = IntuitiveDateConverter.to_epoch_s(
         self.today_day_str)
예제 #4
0
    def test_date_str_representation(self):

        moment_data = {
            'symbol': 'TEST',
            'epoch_s': IntuitiveDateConverter.to_epoch_s('2021-01-01')
        }
        moment = Moment.from_dict(moment_data)
        self.assertEqual('2021-01-01', moment.date_str)
예제 #5
0
    def _populate_recent_register(self, stocks: List[Stock]):

        start_epoch_s = IntuitiveDateConverter.to_epoch_s(
            IntuitiveDateConverter.to_datetime(self.today_epoch_s) -
            timedelta(days=20))
        end_epoch_s = IntuitiveDateConverter.to_epoch_s(
            IntuitiveDateConverter.to_datetime(self.today_epoch_s) -
            timedelta(days=1))

        self.data_ingest_manager.populate_warehouse(stocks,
                                                    start_epoch_s,
                                                    end_epoch_s,
                                                    batch_size=1)
        register = self.data_ingest_manager.create_register_from_warehouse(
            stocks, start_epoch_s, end_epoch_s)

        return register
예제 #6
0
 def run(self, candidates: List[Stock], current_holdings: List[Stock]):
     self.today_day_str = IntuitiveDateConverter.to_day_str(datetime.now())
     self.today_epoch_s = IntuitiveDateConverter.to_epoch_s(
         self.today_day_str)
     all_stocks = candidates + current_holdings
     register = self._populate_register(all_stocks)
     signals = self.apply_traders(register, candidates, current_holdings)
     return signals
예제 #7
0
    def _run_and_evaluate_to_epoch_s(self,
                                     conversion_input: object,
                                     expected_output_type: type,
                                     expected_output_value: object) -> None:

        idc = IntuitiveDateConverter()
        actual_output = idc.to_epoch_s(conversion_input)
        actual_output_type = type(actual_output)

        self.assertEqual(expected_output_type, actual_output_type)
        self.assertEqual(expected_output_value, actual_output)
예제 #8
0
    def test_moment_from_dict_3(self):

        moment_data = {
            'Symbol': 'TEST',
            'EPOCH_S': IntuitiveDateConverter.to_epoch_s('2017-04-30'),
            'oPen': 10.5,
            'cloSE': 11.7,
            'high': 12,
            'loW': 9.2,
            'VOLume': 5598
        }

        moment = Moment.from_dict(moment_data)
        self.assertEqual('TEST', moment.symbol)
        self.assertEqual(IntuitiveDateConverter.to_epoch_s('2017-04-30'),
                         moment.epoch_s)
        self.assertEqual(10.5, moment.open)
        self.assertEqual(11.7, moment.close)
        self.assertEqual(12, moment.high)
        self.assertEqual(9.2, moment.low)
        self.assertEqual(5598, moment.volume)
        self.assertEqual('2017-04-30', moment.date_str)
예제 #9
0
    def multiple_symbol_query(self, market_client):

        start_epoch_s = IntuitiveDateConverter.to_epoch_s(
            STOCKS_DAILY['BYND']['start'])
        end_epoch_s = IntuitiveDateConverter.to_epoch_s(
            STOCKS_DAILY['BYND']['end'])
        moments = market_client.get_moments(symbols=['BYND', 'GOOG'],
                                            start_epoch_s=start_epoch_s,
                                            end_epoch_s=end_epoch_s)

        n_expected = len(STOCKS_DAILY['BYND']['records']) + len(
            STOCKS_DAILY['GOOG']['records'])
        self.assertEqual(n_expected, len(moments))

        for record in STOCKS_DAILY['BYND']['records']:
            record['symbol'] = 'BYND'
            moment = Moment.from_dict(record)
            self.assertIn(moment, moments)

        for record in STOCKS_DAILY['GOOG']['records']:
            record['symbol'] = 'GOOG'
            moment = Moment.from_dict(record)
            self.assertIn(moment, moments)
예제 #10
0
    def get(self, stock: Stock, day: str, offset=0) -> Moment:
        """
        Retrieve a moment by stock and date.
        """
        epoch_s = IntuitiveDateConverter.to_epoch_s(day)

        if offset == 0:
            return self.series[stock].get(epoch_s)

        _series = self.series.get(stock, None)
        if _series is None:
            return None

        if epoch_s not in _series.timestamps:
            return None

        _ix = _series.timestamps.index(epoch_s)
        _ix += offset
        if _ix < 0:
            return None
        if _ix >= len(_series.timestamps):
            return None
        return _series.get(_series.timestamps[_ix])
예제 #11
0
 def get(self, day: str) -> Moment:
     """
     Retrieve a moment by date.
     """
     epoch_s = IntuitiveDateConverter.to_epoch_s(day)
     return self.moments.get(epoch_s, None)
    def get_moments(self, stocks: List[Stock], start_epoch_s: float,
                    end_epoch_s: float) -> List[Moment]:
        """
        Retrieve all daily market data for symbols and date range and convert into list of moments.

        Retries after a pause if API rate limit is reached.
        """
        moments = list()
        for i_0 in range(0, len(stocks), BATCH_SIZE):

            i_f = i_0 + BATCH_SIZE
            i_f = len(stocks) if i_f > len(stocks) else i_f
            stock_batch = stocks[i_0:i_f]

            for _ in range(MAX_RETRIES):
                try:
                    stock_batch_str = ','.join(
                        [s.to_str() for s in stock_batch])
                    print(f'api request for symbols: {stock_batch_str}')
                    content = self.make_api_call(stock_batch, start_epoch_s,
                                                 end_epoch_s)
                except requests.exceptions.ConnectionError as conerr:
                    print(conerr)
                    print(
                        f'Schedule to retry after {RETRY_PAUSE_CONNECTION_ERROR} seconds'
                    )
                    time.sleep(RETRY_PAUSE_CONNECTION_ERROR)
                except ChunkedEncodingError as _:
                    print(
                        f'intermittent ChunkedEncodingError. Retrying in {RETRY_PAUSE_CHUNK_ERROR}s'
                    )
                    time.sleep(RETRY_PAUSE_CHUNK_ERROR)
                    print('retrying')
                    continue
                else:
                    if 'code' in content:
                        if content['code'] == ERROR_CODE_TOO_MANY_REQUESTS:
                            print(
                                f'Too many requests. Pausing for {RETRY_PAUSE_S} seconds before retrying.'
                            )
                            time.sleep(RETRY_PAUSE_S)
                            continue
                    break

            if len(stocks) == 1:
                content = {stocks[0].symbol: content}

            for symbol, data in content.items():

                if 'values' not in data:
                    if data['code'] == ERROR_CODE_NO_DATA_AVAILABLE:
                        print(f'No data available for {symbol}. Skipping.')
                        continue
                    print('unexpected response:')
                    print(content)

                exchange = data['meta']['exchange']

                for record in data['values']:
                    day_data = dict()
                    day_data['exchange'] = exchange
                    day_data['symbol'] = symbol
                    day_data['epoch_s'] = IntuitiveDateConverter.to_epoch_s(
                        record['datetime'])
                    day_data['open'] = float(record['open'])
                    day_data['close'] = float(record['close'])
                    day_data['high'] = float(record['high'])
                    day_data['low'] = float(record['low'])
                    day_data['volume'] = int(record['volume'])
                    moment = Moment(**day_data)
                    moments.append(moment)

        return moments
    def get_technical_indicator(self, stock: Stock, indicator_name: str,
                                start_epoch_s: float,
                                end_epoch_s: float) -> List[Moment]:

        start_date = IntuitiveDateConverter.to_day_str(start_epoch_s)
        end_datetime = IntuitiveDateConverter.to_datetime(end_epoch_s)
        end_datetime += timedelta(days=1)
        end_date = IntuitiveDateConverter.to_day_str(end_datetime)

        request_url = f'https://api.twelvedata.com/{indicator_name.lower()}?symbol=' \
                      f'{stock.symbol}&exchange={stock.exchange}&interval=1day&start_date=' \
                      f'{start_date}&end_date={end_date}&apikey={self.api_key}'

        for _ in range(MAX_RETRIES):
            try:
                print(
                    f'API call for technical indicator {indicator_name.lower()} for {stock.to_str()}'
                )
                response = requests.get(request_url)
            except ChunkedEncodingError:
                print(
                    f'intermittent ChunkedEncodingError. Retrying in {RETRY_PAUSE_CHUNK_ERROR}s'
                )
                time.sleep(RETRY_PAUSE_CHUNK_ERROR)
                print('retrying')
                continue
            except Exception as e:
                print("Stopping retry loop because of un-handled error")
                raise e
            else:
                content = response.content
                content = json.loads(content)
                if 'code' in content.keys():
                    if content['code'] == ERROR_CODE_TOO_MANY_REQUESTS:
                        print(
                            f'Too many requests. Pausing for {RETRY_PAUSE_S} seconds before retrying.'
                        )
                        time.sleep(RETRY_PAUSE_S)
                        continue

                if 'values' not in content:
                    print('unexpected response:')
                    print(content)

                break

        moments = list()

        if 'values' not in content:
            return moments

        for item in content['values']:

            epoch_s = IntuitiveDateConverter.to_epoch_s(item['datetime'])
            for k, v in item.items():

                if k == 'datetime':
                    continue

                if k == indicator_name:
                    moment_data = {
                        'epoch_s': epoch_s,
                        'exchange': stock.exchange,
                        'symbol': stock.symbol,
                        indicator_name: item[k]
                    }
                    moment = Moment(**moment_data)
                    moments.append(moment)
                else:
                    moment_data = {
                        'epoch_s': epoch_s,
                        'exchange': stock.exchange,
                        'symbol': stock.symbol,
                        f'{indicator_name}_{k}': item[k]
                    }
                    moment = Moment(**moment_data)
                    moments.append(moment)

        return moments