Skip to content

Commit

Permalink
Added new polyfeed endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
julio-santiesteban committed Feb 16, 2020
1 parent b21b89b commit 34a3c60
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 3 deletions.
65 changes: 65 additions & 0 deletions alpaca_trade_api/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,71 @@ def df(self):
return self._df



class _Timestamped(object):
def __getattr__(self, key):
if key in self._raw:
val = self._raw[key]
if key == 'timestamp':
return pd.Timestamp(val, tz=NY, unit='ms')
return val
return getattr(super(), key)


class Agg(_Timestamped, Entity):
pass


class Aggs(list):
def __init__(self, raw):
self._raw = raw
super().__init__([
Agg(tick) for tick in self.rename_keys()
])

def _raw_results(self):
return self._raw.get('results', [])

def rename_keys(self):
colmap = {
"o": "open",
"h": "high",
"l": "low",
"c": "close",
"v": "volume",
"t": "timestamp",
}
return [
{colmap.get(k, k): v for k, v in tick.items()}
for tick in self._raw_results()
]

@property
def df(self):
if not hasattr(self, '_df'):
columns = ('timestamp', 'open', 'high', 'low', 'close', 'volume')
df = pd.DataFrame(
self.rename_keys(),
columns=columns
)
df.set_index('timestamp', inplace=True)
df.index = pd.to_datetime(
df.index.astype('int64'),
unit='ms', utc=True
).tz_convert(NY)

self._df = df
return self._df


class Trade(_Timestamped, Entity):
pass


class Quote(_Timestamped, Entity):
pass


class Clock(Entity):
def __getattr__(self, key):
if key in self._raw:
Expand Down
18 changes: 17 additions & 1 deletion alpaca_trade_api/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from .entity import (
Account, AccountConfigurations, AccountActivity,
Asset, Order, Position, BarSet, Clock, Calendar,
Watchlist
Aggs, Trade, Quote, Watchlist
)
from . import polygon
from . import alpha_vantage
Expand Down Expand Up @@ -359,6 +359,22 @@ def get_barset(self,
resp = self.data_get('/bars/{}'.format(timeframe), params)
return BarSet(resp)

def get_aggregates(self, symbol, timespan, _from, to):
resp = self.data_get('/aggs/ticker/{}/range/1/{}/{}/{}'.format(
symbol, timespan, _from, to
))
return Aggs(resp)

def get_last_trade(self, symbol):
'''Get the last trade for the given symbol'''
resp = self.data_get('/last/stocks/{}'.format(symbol))
return Trade(resp['last'])

def get_last_quote(self, symbol):
'''Get the last trade for the given symbol'''
resp = self.data_get('/last_quote/stocks/{}'.format(symbol))
return Quote(resp['last'])

def get_clock(self):
resp = self.get('/clock')
return Clock(resp)
Expand Down
104 changes: 102 additions & 2 deletions tests/test_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@
import requests_mock


if 'APCA_API_BASE_URL' in os.environ:
del os.environ['APCA_API_BASE_URL']
@pytest.fixture(autouse=True)
def delete_base_url_envs():
if 'APCA_API_BASE_URL' in os.environ:
del os.environ['APCA_API_BASE_URL']
if 'APCA_API_DATA_URL' in os.environ:
del os.environ['APCA_API_DATA_URL']


@pytest.fixture
Expand Down Expand Up @@ -331,6 +335,102 @@ def test_data(reqmock):
assert barset['AAPL'][0].t.day == 23
assert barset['AAPL'].df.index[0].day == 23

# Aggs
reqmock.get(
'https://data.alpaca.markets/v1/aggs/ticker/AAPL/'
'range/1/day/2020-02-10/2020-02-12',
text='''
{
"ticker": "AAPL",
"status": "OK",
"adjusted": true,
"queryCount": 3,
"resultsCount": 3,
"results": [
{
"v": 23748626,
"o": 314.18,
"c": 321.54,
"h": 321.54,
"l": 313.85,
"t": 1581310800000,
"n": 1
},
{
"v": 21107108,
"o": 323.6,
"c": 319.61,
"h": 323.9,
"l": 318.71,
"t": 1581397200000,
"n": 1
},
{
"v": 24425223,
"o": 321.62,
"c": 327.2,
"h": 327.21,
"l": 321.47,
"t": 1581483600000,
"n": 1
}
]
}'''
)
aggs = api.get_aggregates('AAPL', 'day', '2020-02-10', '2020-02-12')
assert len(aggs) == 3
assert aggs[0].open == 314.18
assert aggs.df.iloc[1].high == 323.9
with pytest.raises(AttributeError):
aggs[2].foo

# Last trade
reqmock.get(
'https://data.alpaca.markets/v1/last/stocks/AAPL',
text='''
{
"status": "success",
"symbol": "AAPL",
"last": {
"price": 159.59,
"size": 20,
"exchange": 11,
"cond1": 14,
"cond2": 16,
"cond3": 0,
"cond4": 0,
"timestamp": 1518086464720
}
}
'''
)
trade = api.get_last_trade('AAPL')
assert trade.price == 159.59
assert trade.timestamp.day == 8

# Last quote
reqmock.get(
'https://data.alpaca.markets/v1/last_quote/stocks/AAPL',
text='''
{
"status": "success",
"symbol": "AAPL",
"last": {
"askprice": 159.59,
"asksize": 2,
"askexchange": 11,
"bidprice": 159.45,
"bidsize": 20,
"bidexchange": 12,
"timestamp": 1518086601843
}
}'''
)

quote = api.get_last_quote('AAPL')
assert quote.askprice == 159.59
assert quote.timestamp.day == 8


def test_errors(reqmock):
api = tradeapi.REST('key-id', 'secret-key', api_version='v1')
Expand Down

0 comments on commit 34a3c60

Please sign in to comment.