Alpaca Trader
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import time
|
||||
import backoff
|
||||
import alpaca_trade_api as tradeapi
|
||||
|
||||
|
||||
class AlpacaClient:
|
||||
def __init__(self, api_key_id, api_secret_key, base_url, api_version="v2"):
|
||||
self.api = tradeapi.REST(api_key_id, api_secret_key, base_url, api_version=api_version)
|
||||
|
||||
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter)
|
||||
def get_account(self):
|
||||
return self.api.get_account()
|
||||
|
||||
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter)
|
||||
def get_clock(self):
|
||||
return self.api.get_clock()
|
||||
|
||||
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter)
|
||||
def get_bars(self, symbol, timeframe, **kwargs):
|
||||
bars = self.api.get_bars(symbol, timeframe, **kwargs)
|
||||
if bars is None:
|
||||
return None
|
||||
return bars.df
|
||||
|
||||
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter)
|
||||
def get_latest_quote(self, symbol):
|
||||
return self.api.get_latest_quote(symbol)
|
||||
|
||||
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter)
|
||||
def submit_order(self, **kwargs):
|
||||
return self.api.submit_order(**kwargs)
|
||||
|
||||
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter)
|
||||
def get_order(self, order_id):
|
||||
return self.api.get_order(order_id)
|
||||
|
||||
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter)
|
||||
def cancel_order(self, order_id):
|
||||
return self.api.cancel_order(order_id)
|
||||
|
||||
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter)
|
||||
def list_positions(self):
|
||||
return self.api.list_positions()
|
||||
|
||||
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter)
|
||||
def list_orders(self, **kwargs):
|
||||
return self.api.list_orders(**kwargs)
|
||||
|
||||
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter)
|
||||
def close_all_positions(self):
|
||||
return self.api.close_all_positions()
|
||||
|
||||
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter)
|
||||
def get_position(self, symbol):
|
||||
return self.api.get_position(symbol)
|
||||
|
||||
def place_order(self, symbol, side, notional, limit_price, limit_order_timeout):
|
||||
quote = self.get_latest_quote(symbol)
|
||||
if quote is None:
|
||||
return None
|
||||
bid_price = getattr(quote, 'bid_price', None)
|
||||
ask_price = getattr(quote, 'ask_price', None)
|
||||
if limit_price:
|
||||
price_source = limit_price
|
||||
else:
|
||||
price_source = bid_price if side == "buy" else ask_price
|
||||
if price_source is None:
|
||||
return None
|
||||
shares = int(notional / price_source)
|
||||
if shares == 0:
|
||||
return None
|
||||
if limit_price:
|
||||
order = self.submit_order(symbol=symbol, qty=shares, side=side, type="limit", limit_price=round(limit_price, 2), time_in_force="day")
|
||||
start = time.time()
|
||||
while time.time() - start < limit_order_timeout:
|
||||
status = self.get_order(order.id)
|
||||
if status.status == "filled":
|
||||
return float(status.filled_avg_price)
|
||||
if status.status in {"cancelled", "expired", "rejected"}:
|
||||
return None
|
||||
time.sleep(2)
|
||||
self.cancel_order(order.id)
|
||||
return None
|
||||
order = self.submit_order(symbol=symbol, qty=shares, side=side, type="market", time_in_force="day")
|
||||
status = self.get_order(order.id)
|
||||
while status.status not in {"filled", "cancelled", "expired", "rejected"}:
|
||||
time.sleep(0.5)
|
||||
status = self.get_order(order.id)
|
||||
if status.status == "filled":
|
||||
return float(status.filled_avg_price)
|
||||
return None
|
||||
Reference in New Issue
Block a user