mirror of
https://github.com/hydrosquall/tiingo-python.git
synced 2025-12-17 11:54:19 +01:00
Cleanup Docstrings, Tests, and Todos
This commit is contained in:
2
setup.py
2
setup.py
@@ -18,7 +18,7 @@ requirements = [
|
||||
|
||||
setup_requirements = [
|
||||
'pytest-runner',
|
||||
# TODO(hydrosquall): put setup requirements (distutils extensions, etc.) here
|
||||
# TODO: put setup requirements (distutils extensions, etc.) here
|
||||
]
|
||||
|
||||
test_requirements = [
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Tests for `tiingo` package."""
|
||||
|
||||
import pytest
|
||||
from tiingo import TiingoClient
|
||||
|
||||
# TODO
|
||||
# Add tests for
|
||||
# Invalid API key
|
||||
# Invalid ticker, etc
|
||||
# Use unittest asserts rather than regular asserts
|
||||
# Wrap server errors with client side descriptive errors
|
||||
# Coerce startDate/endDate to string if they are passed in as datetime
|
||||
# Use VCR.py to enable offline testing
|
||||
|
||||
|
||||
# Refactor fixtures into separate file
|
||||
@pytest.fixture
|
||||
def ticker_price_response():
|
||||
"""Test /tiingo/<ticker>/prices endpoint"""
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Top-level package for Tiingo Python."""
|
||||
from tiingo.api import TiingoClient
|
||||
|
||||
__author__ = """Cameron Yick"""
|
||||
__email__ = 'cameron.yick@enigma.com'
|
||||
__version__ = '0.1.0'
|
||||
|
||||
from tiingo.api import TiingoClient
|
||||
__version__ = '0.1.1'
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import pkg_resources
|
||||
from tiingo.restclient import RestClient
|
||||
|
||||
VERSION = pkg_resources.get_distribution("tiingo").version
|
||||
|
||||
|
||||
class TiingoClient(RestClient):
|
||||
"""Class for managing interactions with the Tiingo Platform
|
||||
"""Class for managing interactions with the Tiingo REST API
|
||||
|
||||
Supply API Key via Environment Variable TIINGO_API_KEY
|
||||
or via the Config Object
|
||||
Supply API Key via Environment Variable TIINGO_API_KEY
|
||||
or via the Config Object
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -24,7 +27,7 @@ class TiingoClient(RestClient):
|
||||
self._headers = {
|
||||
'Authorization': "Token {}".format(api_key),
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': 'tiingo-python-client'
|
||||
'User-Agent': 'tiingo-python-client {}'.format(VERSION)
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
@@ -32,31 +35,40 @@ class TiingoClient(RestClient):
|
||||
|
||||
# TICKER PRICE ENDPOINTS
|
||||
# https://api.tiingo.com/docs/tiingo/daily
|
||||
def list_tickers(self):
|
||||
"""Return a list of all supported tickers.
|
||||
https://apimedia.tiingo.com/docs/tiingo/daily/supported_tickers.zip
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_ticker_metadata(self, ticker):
|
||||
"""Return metadata for 1 ticker.
|
||||
"""Return metadata for 1 ticker
|
||||
Use TiingoClient.list_tickers() to get available options
|
||||
|
||||
Args:
|
||||
ticker (str) : Unique identifier for stock
|
||||
"""
|
||||
url = "tiingo/daily/{}".format(ticker)
|
||||
response = self._request('GET', url)
|
||||
return response.json()
|
||||
|
||||
def get_ticker_price(self, ticker, startDate=None, endDate=None,
|
||||
fmt='json',
|
||||
frequency='daily'):
|
||||
"""
|
||||
By default, return latest EOD Composite Price for a stock ticker.
|
||||
Each feed on average contains 3 data sources.
|
||||
def get_ticker_price(self, ticker,
|
||||
startDate=None, endDate=None,
|
||||
fmt='json', frequency='daily'):
|
||||
"""By default, return latest EOD Composite Price for a stock ticker.
|
||||
On average, each feed contains 3 data sources.
|
||||
|
||||
Supported tickers + Available Day Ranges are here:
|
||||
https://apimedia.tiingo.com/docs/tiingo/daily/supported_tickers.zip
|
||||
https://apimedia.tiingo.com/docs/tiingo/daily/supported_tickers.zip
|
||||
|
||||
Args:
|
||||
ticker (string): Unique identifier for stock ticker
|
||||
startDate (string): Start of ticker range in YYYY-MM-DD format
|
||||
endDate (string): End of ticker range in YYYY-MM-DD format
|
||||
fmt (string): 'csv' or 'json'
|
||||
frequency (string): Resample frequency
|
||||
"""
|
||||
url = "tiingo/daily/{}/prices".format(ticker)
|
||||
|
||||
params = {
|
||||
'format': fmt,
|
||||
'frequency': frequency
|
||||
@@ -67,29 +79,38 @@ class TiingoClient(RestClient):
|
||||
if endDate:
|
||||
params['endDate'] = endDate
|
||||
|
||||
# TODO: evaluate whether to stream CSV to cache on disk, or
|
||||
# load as array in memory, or just pass plain text
|
||||
response = self._request('GET', url, params=params)
|
||||
return response.json()
|
||||
if fmt == "json":
|
||||
return response.json()
|
||||
else:
|
||||
return response.content
|
||||
|
||||
# FUND DATA (From over 26,000 mutual funds)
|
||||
# https://api.tiingo.com/docs/tiingo/funds
|
||||
# TODO: Validate the models returned by the fund
|
||||
def get_fund_metadata(self, fund):
|
||||
"""Return metadata for 1 mutual fund / ETF
|
||||
|
||||
Args:
|
||||
fund (string): Unique identifier for fund/ETF
|
||||
"""
|
||||
url = "tiingo/funds/{}".format(fund)
|
||||
response = self._request('GET', url)
|
||||
return response.json()
|
||||
|
||||
def get_fund_metrics(self, fund, startDate=None, endDate=None):
|
||||
"""Return metrics about a fund. By default, return latest metrics.
|
||||
"""Return metrics about a fund. If no date provided,
|
||||
return latest metrics.
|
||||
Args:
|
||||
fund (string): Unique identifier for fund/ETF
|
||||
startDate (string): Start of fund range in YYYY-MM-DD format
|
||||
endDate (string): End of fund range in YYYY-MM-DD format
|
||||
fmt (string): 'csv' or 'json'
|
||||
frequency (string): Resample frequency
|
||||
"""
|
||||
url = "tiingo/funds/{}/metrics".format(fund)
|
||||
params = {}
|
||||
|
||||
if startDate:
|
||||
params['startDate'] = startDate
|
||||
if endDate:
|
||||
@@ -99,32 +120,37 @@ class TiingoClient(RestClient):
|
||||
return response.json()
|
||||
|
||||
# NEWS FEEDS
|
||||
# tiingo/news
|
||||
def get_news(self, tickers=[], tags=[], sources=[], startDate=None,
|
||||
endDate=None, limit=100, offset=0, sortBy="publishedDate"):
|
||||
"""Return metrics about a fund. By default, return latest metrics.
|
||||
"""Return list of news articles matching given search terms
|
||||
|
||||
https://api.tiingo.com/docs/tiingo/news
|
||||
|
||||
Args:
|
||||
startDate (string): Start of fund range in YYYY-MM-DD format
|
||||
endDate (string): End of fund range in YYYY-MM-DD format
|
||||
fmt (string): 'csv' or 'json'
|
||||
frequency (string): Resample frequency
|
||||
tickers [string] : List of unique Stock Tickers to search
|
||||
tags [string] : List of topics tagged by Tiingo Algorithms
|
||||
sources [string]: List of base urls to include as news sources
|
||||
startDate, endDate [date]: Boundaries of news search window
|
||||
limit (int): Max results returned. Default 100, max 1000
|
||||
offset (int): Search results offset, used for paginating
|
||||
sortBy (string): "publishedDate" OR (#TODO: UPDATE THIS)
|
||||
"""
|
||||
# Stub:
|
||||
# https://api.tiingo.com/docs/tiingo/news
|
||||
# "Finish later"
|
||||
# params = {}
|
||||
# if tickers:
|
||||
# tickers = ",".join(tickers)
|
||||
raise NotImplementedError
|
||||
|
||||
def get_bulk_news(self, file_id=None):
|
||||
"""Only available to institutional clients.
|
||||
If no ID is provided, return array of available ids.
|
||||
If ID is NOT provided, return array of available file_ids.
|
||||
If ID is provided, provides URL which you can use to download your
|
||||
file, as well as some metadata about that file.
|
||||
"""
|
||||
# Stub:
|
||||
# https://api.tiingo.com/docs/tiingo/news
|
||||
# "Finish later"
|
||||
if file_id:
|
||||
url = "tiingo/news/bulk_download"
|
||||
else:
|
||||
url = "tiingo/news/bulk_download{}".format(file_id)
|
||||
|
||||
response = self._request('GET', url)
|
||||
return response.json()
|
||||
|
||||
@@ -3,6 +3,7 @@ import requests
|
||||
from requests.exceptions import HTTPError
|
||||
|
||||
|
||||
# TODO: Possibly print HTTP json response if available?
|
||||
class RestClientError(Exception):
|
||||
"Wrapper around HTTP Errors"
|
||||
pass
|
||||
@@ -12,11 +13,10 @@ class RestClient(object):
|
||||
|
||||
def __init__(self, config={}):
|
||||
"""Base class for interacting with RESTful APIs
|
||||
|
||||
Child class MUST have a ._base_url property!
|
||||
|
||||
Args:
|
||||
config (dict): Arbitrary configuration options
|
||||
config (dict): Arbitrary options that child classes can access
|
||||
"""
|
||||
self._config = config
|
||||
|
||||
@@ -29,7 +29,13 @@ class RestClient(object):
|
||||
return '<RestClient(url="{}")>'.format(self._base_url)
|
||||
|
||||
def _request(self, method, url, **kwargs):
|
||||
"""Make HTTP request and return response object"""
|
||||
"""Make HTTP request and return response object
|
||||
|
||||
Args:
|
||||
method (str): GET, POST, PUT, DELETE
|
||||
url (str): path appended to the base_url to create request
|
||||
**kwargs: passed directly to a requests.request object
|
||||
"""
|
||||
resp = self._session.request(method,
|
||||
'{}/{}'.format(self._base_url, url),
|
||||
headers=self._headers,
|
||||
|
||||
Reference in New Issue
Block a user