This version of SPY Follows AAPL attempts to utilize Alpaca Markets’ web-socket for realtime data reception as opposed to spontaneous client-side data requests. The program is more a proof-of-concept than anything truly functional. Its fundamental premise is that $SPY follows $AAPL's tick changes, which is of course not always true and is not something I have proven in trialing this program.
The trading logic is quite simple.
import websocket, json, time
from config import *
from keys import alpaca_apiKey, alpaca_secretKey
from datetime import datetime as dt
from texts import *
closes = []
tradestaken = 0
socket = 'wss://stream.data.alpaca.markets/v2/iex'
def shutdownProtocol(ws):
ws.keep_running = False
print(f'Clock is closed so websocket is closed.')
print('')
account = api.get_account
accountPercentChange = str(round(100*((float(account.equity) - float(account.last_equity))/float(account.last_equity)), 3)) + "%"
print(f"Time for a daily update. Account % change today: {accountPercentChange}")
response = Sms.send_message(
{
"from": sender,
"to": mynumber,
"text": f'Sir, our trading bot, SPY Follows AAPL, is done for the day. You may want to sit down '\
f'before I tell you this. It has performed {accountPercentChange} in the entire account, sir.'
}
)
print(f'{response = }')
print('Quitting program as market is closed and text update has been sent.')
quit()
def on_open(ws):
auth_data = {
"action": "auth",
"key": alpaca_apiKey,
"secret": alpaca_secretKey
}
ws.send(json.dumps(auth_data))
subscribe_data = {
"action": "subscribe",
"bars": ["AAPL"]
}
ws.send(json.dumps(subscribe_data))
def on_message(ws, message):
if clock.is_open == False:
shutdownProtocol(ws)
global closes
print(message)
data = json.loads(message)
if data[0]["T"] == "b": #if the message is a bar
closes.append(data[0]["c"]) # add the close to closes
lenCloses = len(closes)
if lenCloses >= 6 and data[0]["c"] > ((closes[lenCloses - 1] + closes[lenCloses - 2] + closes[lenCloses - 3]\
+ closes[lenCloses -4] + closes[lenCloses -5])/5): #if the last close is higher than avg. of prev. 5
buyTrigger() # buy SPY defined in config.py
else:
pass
else:
pass
ws = websocket.WebSocketApp(socket, on_open=on_open, on_message=on_message)
def main():
print('Trading bot is online.')
print('')
while True:
now = dt.now().strftime("%H-%M")
if now == "06-35":
ws.run_forever()
else:
time.sleep(1)
if __name__ == "__main__":
main()
import alpaca_trade_api as tradeapi
import math
from main import tradestaken
from keys import alpaca_apiKey, alpaca_secretKey
base_url = 'https://paper-api.alpaca.markets'
api = tradeapi.REST(alpaca_apiKey, alpaca_secretKey, base_url)
clock = api.get_clock()
account = api.get_account()
leader = 'AAPL'
trigger = 'SPY'
def buyTrigger():
global tradestaken
spy_price = round(float(api.get_last_trade(trigger).price), 2) # SPY price
api.submit_order(
symbol=trigger,
qty = math.floor(0.05*(math.floor(float(account.equity)/spy_price))), # 5% of account equity
side = 'buy',
type = 'market',
time_in_force='gtc',
order_class = 'bracket', # 0.1% profit and loss
stop_loss={'stop_price': spy_price * 0.999,
'limit_price': spy_price * 0.998},
take_profit={'limit_price': spy_price * 1.001}
)
print(f'Trade taken.')
tradestaken = tradestaken + 1
print(f'Total trades taken in runtime: {tradestaken}')
texts.py contains phone numbers and client establishments from the Vonage API. It is referenced in main.py to send a text message with an account update on market close.
keys.py was omitted for obvious reasons.