Skip to content

Commit b32ceff

Browse files
authored
Merge pull request #17 from alpacahq/feature/clock
Clock/Calendar API support
2 parents 35e2c59 + 2411dcf commit b32ceff

File tree

4 files changed

+83
-10
lines changed

4 files changed

+83
-10
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ Calls `GET /assets` and returns a list of `Asset` entities.
120120
### REST.get_asset(symbol)
121121
Calls `GET /assets/{symbol}` and returns an `Asset` entity.
122122

123+
### REST.get_clock()
124+
Calls `GET /clock` and returns a `Clock` entity.
125+
126+
### REST.get_calendar(start=None, end=None)
127+
Calls `GET /calendar` and returns a `Calendar` entity.
128+
123129
### REST.list_quotes(symbols)
124130
Calls `GET /quotes` with symbols and returns a list of `Quote` entities. If `symbols` is not a string, it is concatenated with commas.
125131

alpaca_trade_api/rest.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import dateutil.parser
21
import pandas as pd
32
import pprint
43
import re
@@ -206,6 +205,19 @@ def get_bars(
206205
resp = self.get('/assets/{}/bars'.format(symbol), params)
207206
return AssetBars(resp)
208207

208+
def get_clock(self):
209+
resp = self.get('/clock')
210+
return Clock(resp)
211+
212+
def get_calendar(self, start=None, end=None):
213+
params = {}
214+
if start is not None:
215+
params['start'] = start
216+
if end is not None:
217+
params['end'] = end
218+
resp = self.get('/calendar', data=params)
219+
return [Calendar(o) for o in resp]
220+
209221

210222
class Entity(object):
211223
'''This helper class provides property access (the "dot notation")
@@ -224,7 +236,7 @@ def __getattr__(self, key):
224236
key.endswith('_timestamp') or
225237
key.endswith('_time')) and
226238
ISO8601YMD.match(val)):
227-
return dateutil.parser.parse(val)
239+
return pd.Timestamp(val)
228240
else:
229241
return val
230242
return getattr(super(), key)
@@ -298,3 +310,27 @@ class Quote(Entity):
298310

299311
class Fundamental(Entity):
300312
pass
313+
314+
315+
class Clock(Entity):
316+
def __getattr__(self, key):
317+
if key in self._raw:
318+
val = self._raw[key]
319+
if key in ('timestamp', 'next_open', 'next_close'):
320+
return pd.Timestamp(val)
321+
else:
322+
return val
323+
return getattr(super(), key)
324+
325+
326+
class Calendar(Entity):
327+
def __getattr__(self, key):
328+
if key in self._raw:
329+
val = self._raw[key]
330+
if key in ('date',):
331+
return pd.Timestamp(val)
332+
elif key in ('open', 'close'):
333+
return pd.Timestamp(val).time()
334+
else:
335+
return val
336+
return getattr(super(), key)

tests/test_rest.py

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,10 +197,9 @@ def test_orders(reqmock):
197197
# Get an order by client order id
198198
client_order_id = 'client-order-id'
199199
reqmock.get(
200-
'https://api.alpaca.markets/v1/orders:by_client_order_id?client_order_id={}'.format(
201-
client_order_id,
202-
),
203-
text='''{
200+
'https://api.alpaca.markets/v1/orders:by_client_order_id?'
201+
'client_order_id={}'.format(
202+
client_order_id, ), text='''{
204203
"id": "904837e3-3b76-47ec-b432-046db621571b",
205204
"client_order_id": "904837e3-3b76-47ec-b432-046db621571b",
206205
"account_id": "904837e3-3b76-47ec-b432-046db621571b",
@@ -223,8 +222,7 @@ def test_orders(reqmock):
223222
"failured_reason": "string",
224223
"cancel_requested_at": "2018-03-09T05:50:50Z",
225224
"submitted_at": "2018-03-09T05:50:50Z"
226-
}'''
227-
)
225+
}''')
228226
order = api.get_order_by_client_order_id(client_order_id)
229227
assert order.submitted_at.minute == 50
230228

@@ -311,6 +309,39 @@ def test_positions(reqmock):
311309
assert position.cost_basis == '500.0'
312310

313311

312+
def test_chronos(reqmock):
313+
api = tradeapi.REST('key-id', 'secret-key')
314+
315+
# clock
316+
reqmock.get(
317+
'https://api.alpaca.markets/v1/clock',
318+
text='''{
319+
"timestamp": "2018-04-01T12:00:00.000Z",
320+
"is_open": true,
321+
"next_open": "2018-04-01T12:00:00.000Z",
322+
"next_close": "2018-04-01T12:00:00.000Z"
323+
}'''
324+
)
325+
clock = api.get_clock()
326+
assert clock.is_open
327+
assert clock.next_open.day == 1
328+
329+
# calendar
330+
reqmock.get(
331+
'https://api.alpaca.markets/v1/calendar?start=2018-01-03',
332+
text='''[
333+
{
334+
"date": "2018-01-03",
335+
"open": "09:30",
336+
"close": "16:00"
337+
}
338+
]'''
339+
)
340+
calendar = api.get_calendar(start='2018-01-03')
341+
assert calendar[0].date.day == 3
342+
assert calendar[0].open.minute == 30
343+
344+
314345
def test_assets(reqmock):
315346
api = tradeapi.REST('key-id', 'secret-key')
316347
# Bars

tests/test_stream.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
import json
33
import pytest
44
try:
5-
from unittest.mock import patch
5+
from unittest.mock import patch
66
except ImportError:
7-
from mock import patch
7+
from mock import patch
88
from websocket import WebSocketConnectionClosedException
99

1010

0 commit comments

Comments
 (0)