Skip to content

Commit d27bb4b

Browse files
author
Shlomi Kushchi
authored
Merge branch 'master' into remove_alpha_vantage
2 parents ab9ab20 + 93af35a commit d27bb4b

File tree

6 files changed

+60
-28
lines changed

6 files changed

+60
-28
lines changed

NOTICE

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Apache [PRODUCT_NAME]
2+
Copyright [XXXX-XXXX] The Apache Software Foundation
3+
4+
This product includes software developed at
5+
The Apache Software Foundation (http://www.apache.org/).

README.md

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# alpaca-trade-api-python
77

88
`alpaca-trade-api-python` is a python library for the [Alpaca Commission Free Trading API](https://alpaca.markets).
9-
It allows rapid trading algo development easily, with support for the
9+
It allows rapid trading algo development easily, with support for
1010
both REST and streaming data interfaces. For details of each API behavior,
1111
please see the online [API document](https://docs.alpaca.markets).
1212

@@ -21,7 +21,7 @@ $ pip3 install alpaca-trade-api
2121

2222
## Example
2323

24-
In order to call Alpaca's trade API, you need to sign up for a account and obtain API key pairs. Replace <key_id> and <secret_key> with what you get from the web console.
24+
In order to call Alpaca's trade API, you need to sign up for an account and obtain API key pairs. Replace <key_id> and <secret_key> with what you get from the web console.
2525

2626
### REST example
2727
```python
@@ -42,7 +42,7 @@ The HTTP API document is located at https://docs.alpaca.markets/
4242

4343
## API Version
4444

45-
API Version now defaults to 'v2', however if you still have a 'v1' account, you may need to specify api_version='v1' to properly use the API until you migrate.
45+
API Version now defaults to 'v2', however, if you still have a 'v1' account, you may need to specify api_version='v1' to properly use the API until you migrate.
4646

4747
## Authentication
4848

@@ -53,7 +53,7 @@ outlined below.
5353

5454
## Alpaca Environment Variables
5555

56-
The Alpaca SDK will check the environment for a number of variables which can be used rather than hard-coding these into your scripts.
56+
The Alpaca SDK will check the environment for a number of variables that can be used rather than hard-coding these into your scripts.
5757

5858
| Environment | default | Description |
5959
| -------------------------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
@@ -73,7 +73,7 @@ The `REST` class is the entry point for the API request. The instance of this
7373
class provides all REST API calls such as account, orders, positions,
7474
and bars.
7575

76-
Each returned object is wrapped by a subclass of `Entity` class (or a list of it).
76+
Each returned object is wrapped by a subclass of the `Entity` class (or a list of it).
7777
This helper class provides property access (the "dot notation") to the
7878
json object, backed by the original object stored in the `_raw` field.
7979
It also converts certain types to the appropriate python object.
@@ -87,7 +87,7 @@ account.status
8787
=> 'ACTIVE'
8888
```
8989

90-
The `Entity` class also converts timestamp string field to a pandas.Timestamp
90+
The `Entity` class also converts the timestamp string field to a pandas.Timestamp
9191
object. Its `_raw` property returns the original raw primitive data unmarshaled
9292
from the response JSON text.
9393

@@ -220,9 +220,9 @@ exception is raised, and each event handler is called asynchronously
220220
upon the message arrivals.
221221

222222
The `run` method tries to reconnect to the server in the event of
223-
connection failure. In this case you may want to reset your state
223+
connection failure. In this case, you may want to reset your state
224224
which is best in the `connect` event. The method still raises
225-
exception in the case any other unknown error happens inside the
225+
an exception in the case any other unknown error happens inside the
226226
event loop.
227227

228228
The `msg` object passed to each handler is wrapped by the entity
@@ -284,8 +284,29 @@ same `channel_pat` will overwrite the old handler.
284284
Deregisters the event handler function that was previously registered via `on` or
285285
`register` method.
286286

287+
#### Debugging
288+
Websocket exceptions may occur during execution.
289+
It will usually happen during the `consume()` method, which basically is the
290+
websocket steady-state.<br>
291+
exceptions during the consume method may occur due to:
292+
- server disconnections
293+
- error while handling the response data
294+
295+
We handle the first issue by reconnecting the websocket every time there's a disconnection.
296+
The second issue, is usually a user's code issue. To help you find it, we added a flag to the
297+
StreamConn object called `debug`. It is set to False by default, but you can turn it on to get a more
298+
verbose logs when this exception happens.
299+
Turn it on like so `StreamConn(debug=True)`
300+
301+
## Logging
302+
You should define a logger in your app in order to make sure you get all the messages from the different components.<br>
303+
It will help you debug, and make sure you don't miss issues when they occur.<br>
304+
The simplest way to define a logger, if you have no experience with the python logger - will be something like this:
305+
```py
306+
import logging
307+
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)
308+
```
287309

288-
---
289310
# Polygon API Service
290311

291312
Alpaca's API key ID can be used to access Polygon API, the documentation for
@@ -320,7 +341,7 @@ df = api.polygon.historic_agg_v2('AAPL', 1, 'minute', _from=start, to=end).df
320341
```
321342

322343
## polygon/REST
323-
It is initialized through alpaca `REST` object.
344+
It is initialized through the alpaca `REST` object.
324345

325346
### polygon/REST.exchanges()
326347
Returns a list of `Exchange` entity.
@@ -331,7 +352,7 @@ Returns a `SymbolTypeMap` object.
331352
### polygon/REST.historic_trades_v2(symbol, date,timestamp=None, timestamp_limit=None, reverse=None, limit=None)
332353
Returns a `TradesV2` which is a list of `Trade` entities.
333354

334-
- `date` is a date string such as '2018-2-2'. The returned quotes are from this day onyl.
355+
- `date` is a date string such as '2018-2-2'. The returned quotes are from this day only.
335356
- `timestamp` is an integer in Unix Epoch nanoseconds as the lower bound filter, exclusive.
336357
- `timestamp_limit` is an integer in Unix Epoch nanoseconds as the maximum timestamp allowed in the results.
337358
- `limit` is an integer for the number of ticks to return. Default and max is 50000.
@@ -342,7 +363,7 @@ Returns a pandas DataFrame object with the ticks returned by `historic_trades_v2
342363
### polygon/REST.historic_quotes_v2(symbol, date,timestamp=None, timestamp_limit=None, reverse=None, limit=None)
343364
Returns a `QuotesV2` which is a list of `Quote` entities.
344365

345-
- `date` is a date string such as '2018-2-2'. The returned quotes are from this day onyl.
366+
- `date` is a date string such as '2018-2-2'. The returned quotes are from this day only.
346367
- `timestamp` is an integer in Unix Epoch nanoseconds as the lower bound filter, exclusive.
347368
- `timestamp_limit` is an integer in Unix Epoch nanoseconds as the maximum timestamp allowed in the results.
348369
- `limit` is an integer for the number of ticks to return. Default and max is 50000.
@@ -357,8 +378,7 @@ object.
357378
- `multiplier` is an integer affecting the amount of data contained in each Agg object.
358379
- `timespan` is a string affecting the length of time represented by each Agg object. It is one of the following values:
359380
- `minute`, `hour`, `day`, `week`, `month`, `quarter`, `year`
360-
- `_from` is an Eastern Time timestamp string/object that filters the result
361-
for the lower bound, inclusive. we accept the date in these formats:
381+
- `_from` is an Eastern Time timestamp string/object that filters the result for the lower bound, inclusive. we accept the date in these formats:
362382
datetime.datetime, datetime.date, pd.Timestamp, datetime.timestamp,
363383
isoformat string (YYYY-MM-DD)
364384
- `to` is an Eastern Time timestamp string that filters the result for the upper bound, inclusive. we support the same formats as the _from field
@@ -411,5 +431,5 @@ For technical issues particular to this module, please report the
411431
issue on this GitHub repository. Any API issues can be reported through
412432
Alpaca's customer support.
413433

414-
New features, as well as bug fixes, by sending pull request is always
434+
New features, as well as bug fixes, by sending a pull request is always
415435
welcomed.

alpaca_trade_api/common.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,11 @@ class FLOAT(str):
4848
invalid strings all the way to the servers.
4949
"""
5050
def __new__(cls, value):
51-
if not value:
52-
raise ValueError('Unexpected empty string')
5351
if isinstance(value, float) or isinstance(value, int):
54-
pass # we're good
55-
elif isinstance(value, str):
56-
value = value.strip() # make sure no spaces
57-
if isinstance(float(value), float):
58-
pass # we're good
59-
else:
60-
raise ValueError(f'Unexpected float format "{value}"')
61-
return value
52+
return value
53+
if isinstance(value, str):
54+
return float(value.strip())
55+
raise ValueError(f'Unexpected float format "{value}"')
6256

6357

6458
def get_base_url() -> URL:

alpaca_trade_api/polygon/entity.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,14 @@ def __init__(self, raw):
9393
])
9494

9595
def _raw_results(self):
96-
return self._raw.get('results', [])
96+
results = self._raw.get('results')
97+
if not results:
98+
# this is not very pythonic but it's written like this because
99+
# the raw response for empty aggs was None, and this:
100+
# self._raw.get('results', []) returns None, not [] which breaks
101+
# when we try to iterate it.
102+
return []
103+
return results
97104

98105
def rename_keys(self):
99106
colmap = {

alpaca_trade_api/stream2.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import json
33
import os
44
import re
5+
import traceback
56
from asyncio import CancelledError
67

78
import websockets
@@ -183,7 +184,9 @@ def __init__(
183184
secret_key: str = None,
184185
base_url: URL = None,
185186
data_url: URL = None,
186-
data_stream: str = None):
187+
data_stream: str = None,
188+
debug: bool = False
189+
):
187190
self._key_id, self._secret_key, _ = get_credentials(key_id, secret_key)
188191
self._base_url = base_url or get_base_url()
189192
self._data_url = data_url or get_data_url()
@@ -196,6 +199,7 @@ def __init__(
196199
else:
197200
_data_stream = 'alpacadatav1'
198201
self._data_stream = _data_stream
202+
self._debug = debug
199203

200204
self.trading_ws = _StreamConn(self._key_id,
201205
self._secret_key,
@@ -293,6 +297,8 @@ def run(self, initial_channels: List[str] = []):
293297
except Exception as e:
294298
m = 'consume cancelled' if isinstance(e, CancelledError) else e
295299
logging.error(f"error while consuming ws messages: {m}")
300+
if self._debug:
301+
traceback.print_exc()
296302
loop.run_until_complete(self.close(should_renew))
297303
if loop.is_running():
298304
loop.close()

requirements/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pandas>1,<2
1+
pandas # pyup: ignore
22
requests>2,<3
33
urllib3>1.24,<1.26
44
websocket-client>=0.56.0,<1

0 commit comments

Comments
 (0)