Skip to content

Commit 9e65df5

Browse files
committed
Clean up example script
1 parent 109e6d8 commit 9e65df5

File tree

1 file changed

+31
-18
lines changed

1 file changed

+31
-18
lines changed

examples/overnight_hold.py

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,34 @@
88
from pytz import timezone
99

1010
stocks_to_hold = 150 # Max 200
11-
max_stock_price = 20
12-
min_stock_price = 10
1311

12+
# Only stocks with prices in this range will be considered.
13+
max_stock_price = 26
14+
min_stock_price = 6
15+
16+
# API datetimes will match this format. (-04:00 represents the market's TZ.)
1417
api_time_format = '%Y-%m-%dT%H:%M:%S.%f-04:00'
1518

1619
# Rate stocks based on the volume's deviation from the previous 5 days and
1720
# momentum. Returns a dataframe mapping stock symbols to ratings and prices.
21+
# Note: If algo_time is None, the API's default behavior of the current time
22+
# as `end` will be used. We use this for live trading.
1823
def get_ratings(symbols, algo_time):
1924
assets = api.list_assets()
2025
assets = [asset for asset in assets if asset.tradable ]
2126
ratings = pd.DataFrame(columns=['symbol', 'rating', 'price'])
2227
index = 0
2328
batch_size = 200 # The maximum number of stocks to request data for
2429
window_size = 5 # The number of days of data to consider
25-
# Convert the time to something compatable with the Alpaca API
26-
formatted_time = algo_time.date().strftime(api_time_format)
30+
formatted_time = None
31+
if algo_time is not None:
32+
# Convert the time to something compatable with the Alpaca API.
33+
formatted_time = algo_time.date().strftime(api_time_format)
2734
while index < len(assets):
2835
symbol_batch = [
2936
asset.symbol for asset in assets[index:index+batch_size]
3037
]
38+
# Retrieve data for this batch of symbols.
3139
barset = api.get_barset(
3240
symbols=symbol_batch,
3341
timeframe='day',
@@ -38,23 +46,28 @@ def get_ratings(symbols, algo_time):
3846
for symbol in symbol_batch:
3947
bars = barset[symbol]
4048
if len(bars) == window_size:
41-
# Make sure we aren't missing the most recent data
49+
# Make sure we aren't missing the most recent data.
4250
latest_bar = bars[-1].t.to_pydatetime().astimezone(
4351
timezone('EST')
4452
)
4553
gap_from_present = algo_time - latest_bar
4654
if gap_from_present.days > 1:
4755
continue
56+
57+
# Now, if the stock is within our target range, rate it.
4858
price = bars[-1].c
4959
if price <= max_stock_price and price >= min_stock_price:
5060
price_change = price - bars[0].c
5161
# Calculate standard deviation of previous volumes
5262
past_volumes = [bar.v for bar in bars[:-1]]
5363
volume_stdev = statistics.stdev(past_volumes)
54-
# Then, compare it to the change in volume since yesterday
64+
if volume_stdev == 0:
65+
# The data for the stock might be low quality.
66+
continue
67+
# Then, compare it to the change in volume since yesterday.
5568
volume_change = bars[-1].v - bars[-2].v
5669
volume_factor = volume_change / volume_stdev
57-
# Rating = Number of volume standard deviations * momentum
70+
# Rating = Number of volume standard deviations * momentum.
5871
rating = price_change/bars[0].c * volume_factor
5972
if rating > 0:
6073
ratings = ratings.append({
@@ -78,7 +91,7 @@ def get_shares_to_buy(ratings_df, portfolio):
7891
return shares
7992

8093

81-
# Returns a string version of a timestamp compatible with the Alpaca API
94+
# Returns a string version of a timestamp compatible with the Alpaca API.
8295
def api_format(dt):
8396
return dt.strftime(api_time_format)
8497

@@ -108,11 +121,14 @@ def backtest(api, days_to_test, portfolio_amount):
108121
)
109122

110123
if cal_index == len(calendars) - 1:
124+
# We've reached the end of the backtesting window.
111125
break
112126

127+
# Get the ratings for a particular day
113128
ratings = get_ratings(symbols, timezone('EST').localize(calendar.date))
114129
shares = get_shares_to_buy(ratings, portfolio_amount)
115130
for _, row in ratings.iterrows():
131+
# "Buy" our shares on that day and subtract the cost.
116132
shares_to_buy = shares[row['symbol']]
117133
cost = row['price'] * shares_to_buy
118134
portfolio_amount -= cost
@@ -140,7 +156,7 @@ def get_value_of_assets(api, shares_bought, on_date):
140156
return 0
141157

142158
total_value = 0
143-
formatted_date = on_date.strftime("%Y-%m-%dT00:00:00-04:00")
159+
formatted_date = api_format(on_date)
144160
barset = api.get_barset(
145161
symbols=shares_bought.keys(),
146162
timeframe='day',
@@ -153,10 +169,10 @@ def get_value_of_assets(api, shares_bought, on_date):
153169

154170

155171
def run_live(api):
156-
cycle = 0
172+
cycle = 0 # Only used to print a "waiting" message every few minutes.
157173

158-
# See if we've already bought or sold positions today. Useful in case the
159-
# script is restarted during market hours.
174+
# See if we've already bought or sold positions today. If so, we don't want to do it again.
175+
# Useful in case the script is restarted during market hours.
160176
bought_today = False
161177
sold_today = False
162178
try:
@@ -180,9 +196,6 @@ def run_live(api):
180196
# We don't have any orders, so we've obviously not done anything today.
181197
pass
182198

183-
bought_today = False
184-
sold_today = True
185-
186199
while True:
187200
# We'll wait until the market's open to do anything.
188201
clock = api.get_clock()
@@ -195,7 +208,7 @@ def run_live(api):
195208
print('Buying positions...')
196209
portfolio_cash = float(api.get_account().cash)
197210
ratings = get_ratings(
198-
api, clock.timestamp.astimezone(timezone('EST'))
211+
api, None
199212
)
200213
shares_to_buy = get_shares_to_buy(ratings, portfolio_cash)
201214
for symbol in shares_to_buy:
@@ -219,15 +232,15 @@ def run_live(api):
219232
else:
220233
bought_today = False
221234
sold_today = False
222-
if cycle % 5 == 0:
235+
if cycle % 10 == 0:
223236
print("Waiting for next market day...")
224237
time.sleep(30)
225238
cycle+=1
226239

227240

228241

229242
if __name__ == '__main__':
230-
api = tradeapi.REST()
243+
api = tradeapi.REST('PKLF4UR7WCI5U9D6QKTU', '0pEmERtAlypbsm/peYlFSacQljQs5AxuQt3yAeJE', 'https://paper-api.alpaca.markets')
231244

232245
if len(sys.argv) < 2:
233246
print('Error: please specify a command; either "run" or "backtest".')

0 commit comments

Comments
 (0)