Exemplo n.º 1
0
class AlpacaTradeInterface(TradeInterface):
    def __init__(self, key, secret, endpoint):
        self.api = Alpaca(key, secret, endpoint)
        self.pending_orders = {}
        self.cached_tickers = {}

    def initialize(self, env: Environment):
        try:
            # Load cash balance
            account = self.api.get_account()
            env.get_portfolio().cash = float(
                account.cash)  # We have margin but shouldn't use it

            # Load open orders
            orders = self.api.list_orders(status='open', limit=500)
            self.pending_orders = {
                order.id: Order(order.symbol,
                                OrderType(order.order_type),
                                order.qty,
                                order.side == 'buy',
                                order_id=order.id,
                                limit_price=order.limit_price,
                                stop_price=order.stop_price)
                for order in orders
            }

            # Load open positions
            positions = self.api.list_positions()
            for pos in positions:
                env.get_portfolio()._add_position(
                    Position(pos.symbol, pos.qty, pos.avg_entry_price,
                             pos.current_price))

        except Exception:
            logger.exception('Failed to pull current orders and cash balance')
            return False
        return True

    def update(self, env: Environment):
        orders = self.api.list_orders(status='closed',
                                      after=datetime.now() - timedelta(days=2),
                                      limit=500)
        for order in orders:
            if order.id in self.pending_orders:
                logger.info(f'Order {order.id} executed')
                env.notify_order_completed(
                    ExecutedOrder(order.symbol, order.filled_qty,
                                  order.filled_avg_price, order.side == 'buy'))

    def place_order(self, order: Order):
        logger.info(f'Placing order for {order.quantity} {order.ticker}')
        try:
            result = self.api.submit_order(order.ticker,
                                           order.quantity,
                                           'buy' if order.is_buy else 'sell',
                                           order.order_type.value,
                                           'day',
                                           limit_price=order.limit_price,
                                           stop_price=order.stop_price)
            order.order_id = result.id
            self.pending_orders[result.id] = order
            return True

        except Exception:
            logger.exception(
                f'Failed to place order for {order.quantity} {order.ticker}')
            return False

    def cancel_order(self, order_id):
        logger.info(f'Canceling order {order_id}')
        try:
            self.api.cancel_order(order_id)
            self.pending_orders.pop(order_id, None)
            return True
        except Exception:
            logger.exception(f'Failed to cancel order {order_id}')
            return False

    def market_open(self):
        clock = self.api.get_clock()
        return clock.is_open  # TODO - we also have open/close times. May want to avoid holding overnight

    def open_orders(self):
        return [order for order in self.pending_orders.values()]

    def ticker_exists(self, ticker):
        try:
            if ticker in self.cached_tickers:
                return self.cached_tickers[ticker]
            asset = self.api.get_asset(ticker)
            self.cached_tickers[ticker] = asset.tradable
            return asset.tradable
        except:
            self.cached_tickers[ticker] = False
            return False
        df_sell = df_sell.set_index('symbol', drop=True)
        print('\nSELLING:')
        alert += '\n**SELL (Estimated gains)**'
        for symbol in df_sell.index:
            qty = df_sell['qty'][symbol]
            alert += f'\n{symbol}: {qty}'
            print(f'{symbol}: {qty}')
            latest_price = df_sell['latest_price'][symbol]
            purchase_price = df_sell['purchase_price'][symbol]
            gain = (latest_price - purchase_price) / purchase_price * 100
            alert += ' ({}{}%)'.format('+' if gain > 0 else '', '%.1f' % gain)

            # Submit limit sell order
            api.submit_order(symbol=symbol,
                             qty=str(qty),
                             side='sell',
                             type='limit',
                             limit_price=str(latest_price),
                             time_in_force='day')

    # Buy stocks in DataFrame
    if not df_buy.empty:
        df_buy = df_buy.set_index('symbol', drop=True)
        print('\nBUYING:')
        alert += '\n\n**BUY**'
        for symbol in df_buy.index:
            qty = df_buy['qty'][symbol]
            price = df_buy['close'][symbol]
            print(f'{symbol}: {qty}')
            alert += f'\n{symbol}: {qty}'

            # Submit limit sell order
Exemplo n.º 3
0
class AlpacaExchange(Exchange):
    client: REST

    access_key: str
    secret_key: str

    @staticmethod
    def build(account: Account, is_paper=False):
        base_url_live = "https://api.alpaca.markets"
        base_url_paper = "https://paper-api.alpaca.markets"

        return AlpacaExchange(
            account=account,
            base_url=base_url_paper if is_paper else base_url_live)

    def __init__(self, account: Account, base_url: str):
        super().__init__()
        self.client = REST(key_id=account.get_access_key(),
                           secret_key=account.get_secret_key(),
                           base_url=URL(base_url))

    def get_day_candles(self, ticker: str, start: str,
                        end: str) -> List[Candle]:
        pass

    def get_last_candle(self, ticker: str) -> Candle:
        snapshot = self.client.get_snapshot(symbol=ticker)
        return BarAdapter(ticker=ticker, bar=snapshot.minute_bar)

    def get_all_assets(self) -> List[Asset]:
        account = self.client.get_account()
        positions = self.client.list_positions()

        cash = AccountAdapter(account.__dict__.get('_raw'))
        stocks = [
            PositionAdapter(position.__dict__.get('_raw'))
            for position in positions
        ]
        return [cash, *stocks]

    def buy(self, ticker: str, amount: Decimal) -> Optional[Order]:
        return self.__order(side="buy", ticker=ticker, amount=amount)

    def sell(self, ticker: str, volume: Decimal) -> Optional[Order]:
        return self.__order(side="sell", ticker=ticker, volume=volume)

    def get_order(self, order_id: str) -> Optional[Order]:
        order = self.client.get_order_by_client_order_id(
            client_order_id=order_id)
        return OrderAdapter(AlpacaOrder(**order.__dict__.get("_raw")))

    def __order(self,
                side: str,
                ticker: str,
                amount: Decimal = None,
                volume: Decimal = None) -> Optional[Order]:
        if amount is None and volume is None:
            raise ValueError("order amount or volume should not be None")

        if not self.__is_open_now():
            return None

        custom_order_id = str(uuid.uuid4())
        qty = float(volume) if volume is not None else None
        notional = float(amount) if amount is not None else None

        order = self.client.submit_order(symbol=ticker,
                                         qty=qty,
                                         notional=notional,
                                         side=side,
                                         type="market",
                                         client_order_id=custom_order_id)
        return OrderAdapter(AlpacaOrder(**order.__dict__.get("_raw")))

    def __is_open_now(self):
        """check if US exchanges (NYSE, NASDAQ, etc) are open
		09:30 (EST) ~ 16:00 (EST)
		"""
        clock = self.client.get_clock()
        return clock.is_open