Skip to content

Commit eb03217

Browse files
authored
Merge branch 'master' into extend-list-orders-2
2 parents 22e2462 + 5be8f76 commit eb03217

File tree

5 files changed

+75
-37
lines changed

5 files changed

+75
-37
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ The solution - manually install these package before installing alpaca-trade-api
2424
```bash
2525
pip install pandas==1.1.5 numpy==1.19.4 scipy==1.5.4
2626
```
27+
Also note that we do not limit the version of the websockets library, but we advice using
28+
```
29+
websockets>=9.0
30+
```
2731

2832
Installing using pip
2933
```bash

alpaca_trade_api/rest.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,7 @@ def _request(self,
108108
path,
109109
data=None,
110110
base_url: URL = None,
111-
api_version: str = None
112-
):
111+
api_version: str = None):
113112
base_url = base_url or self._base_url
114113
version = api_version if api_version else self._api_version
115114
url: URL = URL(base_url + '/' + version + path)
@@ -127,7 +126,7 @@ def _request(self,
127126
# It's better to fail early if the URL isn't right.
128127
'allow_redirects': False,
129128
}
130-
if method.upper() == 'GET':
129+
if method.upper() in ['GET', 'DELETE']:
131130
opts['params'] = data
132131
else:
133132
opts['json'] = data
@@ -423,9 +422,24 @@ def get_position(self, symbol: str) -> Position:
423422
resp = self.get('/positions/{}'.format(symbol))
424423
return self.response_wrapper(resp, Position)
425424

426-
def close_position(self, symbol: str) -> Position:
425+
def close_position(self, symbol: str, *,
426+
qty: float = None,
427+
# percentage: float = None # currently unsupported api
428+
) -> Position:
427429
"""Liquidates the position for the given symbol at market price"""
428-
resp = self.delete('/positions/{}'.format(symbol))
430+
# if qty and percentage:
431+
# raise Exception("Can't close position with qty and pecentage")
432+
# elif qty:
433+
# data = {'qty': qty}
434+
# elif percentage:
435+
# data = {'percentage': percentage}
436+
# else:
437+
# data = {}
438+
if qty:
439+
data = {'qty': qty}
440+
else:
441+
data = {}
442+
resp = self.delete('/positions/{}'.format(symbol), data=data)
429443
return self.response_wrapper(resp, Position)
430444

431445
def close_all_positions(self) -> Positions:

alpaca_trade_api/stream.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ async def _run_forever(self):
234234
await self._start_ws()
235235
self._running = True
236236
retries = 0
237-
await self._consume()
237+
await self._consume()
238238
except websockets.WebSocketException as wse:
239239
retries += 1
240240
if retries > int(os.environ.get('APCA_RETRY_MAX', 3)):

examples/overnight_hold.py

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
# momentum. Returns a dataframe mapping stock symbols to ratings and prices.
2222
# Note: If algo_time is None, the API's default behavior of the current time
2323
# as `end` will be used. We use this for live trading.
24-
def get_ratings(symbols, algo_time):
24+
def get_ratings(api, algo_time):
2525
assets = api.list_assets()
2626
assets = [asset for asset in assets if asset.tradable ]
2727
ratings = pd.DataFrame(columns=['symbol', 'rating', 'price'])
@@ -31,22 +31,34 @@ def get_ratings(symbols, algo_time):
3131
formatted_time = None
3232
if algo_time is not None:
3333
# Convert the time to something compatable with the Alpaca API.
34+
start_time = (algo_time.date() -
35+
timedelta(days=window_size)).strftime(api_time_format)
3436
formatted_time = algo_time.date().strftime(api_time_format)
3537
while index < len(assets):
3638
symbol_batch = [
3739
asset.symbol for asset in assets[index:index+batch_size]
3840
]
3941
# Retrieve data for this batch of symbols.
40-
barset = {}
41-
for symbol in symbol_batch:
42-
bars = api.get_bars(symbol,
43-
TimeFrame.Day,
44-
formatted_time - timedelta(days=window_size),
45-
formatted_time,
46-
limit=window_size,
47-
adjustment='raw')
48-
barset[symbol] = bars
4942

43+
# note: soon get_barset() will be deprecated and you need to use the
44+
# commented out code instead
45+
46+
# barset = {}
47+
# bars = api.get_bars(symbol,
48+
# TimeFrame.Day,
49+
# start_time,
50+
# formatted_time,
51+
# limit=window_size,
52+
# adjustment='raw')
53+
# barset[symbol] = bars
54+
55+
barset = api.get_barset(
56+
symbols=symbol_batch,
57+
timeframe='day',
58+
limit=window_size,
59+
end=formatted_time
60+
)
61+
algo_time = pd.Timestamp('now', tz=timezone('EST'))
5062
for symbol in symbol_batch:
5163
bars = barset[symbol]
5264
if len(bars) == window_size:
@@ -102,10 +114,6 @@ def api_format(dt):
102114
def backtest(api, days_to_test, portfolio_amount):
103115
# This is the collection of stocks that will be used for backtesting.
104116
assets = api.list_assets()
105-
# Note: for longer testing windows, this should be replaced with a list
106-
# of symbols that were active during the time period you are testing.
107-
symbols = [asset.symbol for asset in assets]
108-
109117
now = datetime.now(timezone('EST'))
110118
beginning = now - timedelta(days=days_to_test)
111119

@@ -129,7 +137,8 @@ def backtest(api, days_to_test, portfolio_amount):
129137
break
130138

131139
# Get the ratings for a particular day
132-
ratings = get_ratings(symbols, timezone('EST').localize(calendar.date))
140+
ratings = \
141+
get_ratings(api, timezone('EST').localize(calendar.date))
133142
shares = get_shares_to_buy(ratings, portfolio_amount)
134143
for _, row in ratings.iterrows():
135144
# "Buy" our shares on that day and subtract the cost.
@@ -139,11 +148,11 @@ def backtest(api, days_to_test, portfolio_amount):
139148
cal_index += 1
140149

141150
# Print market (S&P500) return for the time period
142-
sp500_bars = self.oapi.get_bars('SPY',
143-
TimeFrame.Day,
144-
api_format(calendars[0].date),
145-
api_format(calendars[-1].date),
146-
adjustment='raw')
151+
sp500_bars = api.get_bars('SPY',
152+
TimeFrame.Day,
153+
api_format(calendars[0].date),
154+
api_format(calendars[-1].date),
155+
adjustment='raw')
147156
sp500_change = (sp500_bars[-1].c - sp500_bars[0].c) / sp500_bars[0].c
148157
print('S&P 500 change during backtesting window: {:.4f}%'.format(
149158
sp500_change*100)
@@ -161,15 +170,26 @@ def get_value_of_assets(api, shares_bought, on_date):
161170
total_value = 0
162171
formatted_date = api_format(on_date)
163172

164-
barset = {}
165-
for symbol in shares_bought.keys():
166-
bars = api.get_bars(symbol,
167-
TimeFrame.Day,
168-
on_date,
169-
on_date,
170-
limit=1,
171-
adjustment='raw')
172-
barset[symbol] = bars
173+
# note: soon get_barset() will be deprecated and you need to use the
174+
# commented out code instead
175+
176+
# barset = {}
177+
# for symbol in shares_bought.keys():
178+
# bars = api.get_bars(symbol,
179+
# TimeFrame.Day,
180+
# on_date.date(),
181+
# on_date.date(),
182+
# limit=1,
183+
# adjustment='raw')
184+
# barset[symbol] = bars
185+
186+
barset = api.get_barset(
187+
symbols=shares_bought.keys(),
188+
timeframe='day',
189+
limit=1,
190+
end=formatted_date
191+
)
192+
173193
for symbol in shares_bought:
174194
total_value += shares_bought[symbol] * barset[symbol][0].o
175195
return total_value

requirements/requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ pandas
22
numpy
33
requests>2,<3
44
urllib3>1.24,<2
5-
websocket-client>=0.56.0,<1
6-
websockets>=8.0,<9
5+
websocket-client>=0.56.0,<2
6+
websockets>=8.0,<10
77
msgpack==1.0.2

0 commit comments

Comments
 (0)