def __init__(
     self,
     q,
     api_key="",
     api_secret="",
     instrument="",
     method="",
     base_url="",
     data_url="",
     data_stream="",
     *args,
     **kwargs,
 ):
     try:
         # make sure we have an event loop, if not create a new one
         asyncio.get_event_loop()
     except RuntimeError:
         asyncio.set_event_loop(asyncio.new_event_loop())
     self.data_stream = data_stream
     self.conn = tradeapi.StreamConn(
         api_key,
         api_secret,
         base_url,
         data_url=data_url,
         data_stream=self.data_stream,
     )
     self.instrument = instrument
     self.method = method
     self.q = q
     self.conn.on("authenticated")(self.on_auth)
     self.conn.on(r"Q.*")(self.on_quotes)
     self.conn.on(r"account_updates")(self.on_account)
     self.conn.on(r"trade_updates")(self.on_trade)
Exemplo n.º 2
0
    def _get_stream(self, context):
        set_context(context)
        asyncio.set_event_loop(asyncio.new_event_loop())
        conn = tradeapi.StreamConn(self._key_id, self._secret, self._base_url)
        channels = ['trade_updates']

        @conn.on(r'trade_updates')
        async def handle_trade_update(conn, channel, data):
            # Check for any pending orders
            waiting_order = self._orders_pending_submission.get(
                data.order['client_order_id']
            )
            if waiting_order is not None:
                if data.event == 'fill':
                    # Submit the waiting order
                    self.order(*waiting_order)
                    self._orders_pending_submission.pop(
                        data.order['client_order_id'], None
                    )
                elif data.event in ['canceled', 'rejected']:
                    # Remove the waiting order
                    self._orders_pending_submission.pop(
                        data.order['client_order_id'], None
                    )

            if data.event in ['canceled', 'rejected', 'fill']:
                self._open_orders.pop(data.order['client_order_id'], None)
            else:
                self._open_orders[data.order['client_order_id']] = (
                    self._order2zp(Order(data.order))
                )

        conn.run(channels)
Exemplo n.º 3
0
 def __init__(self,
              q,
              api_key='',
              api_secret='',
              instrument='',
              method='',
              base_url='',
              data_stream='',
              *args,
              **kwargs):
     try:
         # make sure we have an event loop, if not create a new one
         asyncio.get_event_loop()
     except RuntimeError:
         asyncio.set_event_loop(asyncio.new_event_loop())
     self.data_stream = data_stream
     self.conn = tradeapi.StreamConn(api_key,
                                     api_secret,
                                     base_url,
                                     data_stream=self.data_stream)
     self.instrument = instrument
     self.method = method
     self.q = q
     self.conn.on('authenticated')(self.on_auth)
     self.conn.on(r'Q.*')(self.on_quotes)
     self.conn.on(r'account_updates')(self.on_account)
     self.conn.on(r'trade_updates')(self.on_trade)
Exemplo n.º 4
0
def run(args):
    symbols = [s.upper() for s in args.symbols.split(',')]
    max_shares = args.quantity
    opts = {}
    if args.key_id:
        opts['key_id'] = args.key_id
    if args.secret_key:
        opts['secret_key'] = args.secret_key
    if args.base_url:
        opts['base_url'] = args.base_url
    elif 'key_id' in opts and opts['key_id'].startswith('PK'):
        opts['base_url'] = 'https://paper-api.alpaca.markets'
    # Create an API object which can be used to submit orders, etc.
    api = tradeapi.REST(**opts)

    qc = ['Q.' + symbol for symbol in symbols]
    tc = ['T.' + symbol for symbol in symbols]
    unit = args.unit

    # Establish streaming connection
    conn = tradeapi.StreamConn(**opts)

    setup(api, conn, symbols, unit, max_shares)

    conn.run(['trade_updates'] + tc + qc)
Exemplo n.º 5
0
def consumer_thread(channels):
    try:
        # make sure we have an event loop, if not create a new one
        asyncio.get_event_loop()
    except RuntimeError:
        asyncio.set_event_loop(asyncio.new_event_loop())
    global conn
    if not conn:
        conn = tradeapi.StreamConn(
            key_id=_key_id,
            secret_key=_secret_key if not USE_POLYGON else 'DUMMY',
            base_url=URL(_base_url),
            data_url=URL(_data_url),
            data_stream='polygon' if USE_POLYGON else 'alpacadatav1',
            raw_data=True,
        )

        conn.on('authenticated')(on_auth)
        conn.on(r'Q.*')(on_message)
        conn.on(r'T.*')(on_message)

        conn.on(r'listening')(listen)

        if USE_POLYGON:
            conn.on(r'A.*')(on_message)
        conn.on(r'AM.*')(on_message)
        conn.on(r'account_updates')(on_account)
        conn.on(r'trade_updates')(on_trade)
        conn.run(channels)
Exemplo n.º 6
0
def main(args):
    api = alpaca.REST(
    base_url=base_url,
    key_id=api_key_id,
    secret_key=api_secret)
    stream = alpaca.StreamConn(base_url=base_url, key_id=api_key_id, secret_key=api_secret, data_stream='polygon')

     tickers = api.polygon.all_tickers()
Exemplo n.º 7
0
 def __init__(self, q, api_key='', api_secret='', instrument='', method='', base_url='', *args, **kwargs):
     self.conn = tradeapi.StreamConn(api_key, api_secret, base_url)
     self.instrument = instrument
     self.method = method
     self.q = q
     self.conn.on('authenticated')(self.on_auth)
     self.conn.on(r'Q.*')(self.on_quotes)
     self.conn.on(r'account_updates')(self.on_account)
     self.conn.on(r'trade_updates')(self.on_trade)
Exemplo n.º 8
0
def main():
    api = alpaca.REST()
    stream = alpaca.StreamConn()

    symbol = args.symbol
    scalpalgo = Algo(api, symbol, lot=args.lot)

    @stream.on(r'^AM')
    async def on_bars(conn, channel, data):
        if data.symbol in fleet:
            fleet[data.symbol].on_bar(data)
Exemplo n.º 9
0
def main(args):
    print(base_url)
    api = alpaca.REST(
    base_url=base_url,
    key_id=api_key_id,
    secret_key=api_secret
    )
    stream = alpaca.StreamConn(
    base_url=base_url,
    key_id=api_key_id,
    secret_key=api_secret
    )

    fleet = {}
    symbols = args.symbols
    for symbol in symbols:
        algo = ScalpAlgo(api, symbol, lot=args.lot)
        fleet[symbol] = algo

    @stream.on(r'^AM')
    async def on_bars(conn, channel, data):
        if data.symbol in fleet:
            fleet[data.symbol].on_bar(data)

    @stream.on(r'trade_updates')
    async def on_trade_updates(conn, channel, data):
        logger.info(f'trade_updates {data}')
        symbol = data.order['symbol']
        if symbol in fleet:
            fleet[symbol].on_order_update(data.event, data.order)

    async def periodic():
        while True:
            if not api.get_clock().is_open:
                logger.info('exit as market is not open')
                sys.exit(0)
            await asyncio.sleep(30)
            positions = api.list_positions()
            for symbol, algo in fleet.items():
                pos = [p for p in positions if p.symbol == symbol]
                algo.checkup(pos[0] if len(pos) > 0 else None)
    channels = ['trade_updates'] + [
        'AM.' + symbol for symbol in symbols
    ]

    loop = stream.loop
    loop.run_until_complete(asyncio.gather(
        stream.subscribe(channels),
        periodic(),
    ))
    loop.close()
Exemplo n.º 10
0
    def _get_stream(self, context):
        set_context(context)
        asyncio.set_event_loop(asyncio.new_event_loop())
        conn = tradeapi.StreamConn(self._key_id, self._secret, self._base_url)
        channels = ['trade_updates']

        @conn.on(r'trade_updates')
        async def handle_trade_update(conn, channel, data):
            if data.event in ['canceled', 'rejected', 'fill']:
                del self._open_orders[data.order['client_order_id']]
            else:
                self._open_orders[data.order['client_order_id']] = (
                    self._order2zp(Order(data.order)))

        conn.run(channels)
Exemplo n.º 11
0
    def __init__(self, key_id: str, secret_key: str, base_url: str,
                 data_url: str, db: str, table: str, lock: Lock,
                 submitted: Dict[str, str], closing: Set[str]):

        self.conn = alpaca.StreamConn(key_id=key_id,
                                      secret_key=secret_key,
                                      base_url=base_url,
                                      data_url=data_url,
                                      data_stream='alpacadatav1')
        self.db = db
        self.table = table
        self.lock = lock
        self._submitted = submitted
        self._closing = closing
        self.thread = None
        logger.info('WSHandler initialized')
Exemplo n.º 12
0
def main(args):
    base_url = 'https://paper-api.alpaca.markets'  # 'https://paper-api.alpaca.markets' - used for paper account
    api_key_id = 'PKDCQCXDXMFDHBARUXDE'  #paper trading(PKK2HPWQFY9I25KIDVP9)
    api_secret = 'HhXt0uYUqW3KqEv2G2w8dTdPSeMECU7rDFcxppDC'  #paper trading(IKSKvUlQp5iv9fGMkVlJ27pFiKqE0symg2KseZpQ)
    api = alpaca.REST(base_url=base_url,
                      key_id=api_key_id,
                      secret_key=api_secret,
                      api_version='v2')
    stream = alpaca.StreamConn(base_url=base_url,
                               key_id=api_key_id,
                               secret_key=api_secret)

    fleet = {}
    symbols = args.symbols
    for symbol in symbols:
        algo = ScalpAlgo(api, symbol, lot=args.lot)
        fleet[symbol] = algo

    @stream.on(r'^AM')
    async def on_bars(conn, channel, data):
        if data.symbol in fleet:
            fleet[data.symbol].on_bar(data)

    @stream.on(r'trade_updates')
    async def on_trade_updates(conn, channel, data):
        logger.info(f'trade_updates {data}')
        symbol = data.order['symbol']
        if symbol in fleet:
            fleet[symbol].on_order_update(data.event, data.order)

    async def periodic():
        while True:
            if not api.get_clock().is_open:
                logger.info('exit as market is not open')
                sys.exit(0)
            await asyncio.sleep(30)
            positions = api.list_positions()
            for symbol, algo in fleet.items():
                pos = [p for p in positions if p.symbol == symbol]
                algo.checkup(pos[0] if len(pos) > 0 else None)

    channels = ['trade_updates'] + ['AM.' + symbol for symbol in symbols]

    loop = stream.loop
    loop.run_until_complete(
        asyncio.gather(stream.subscribe(channels), periodic()))
    loop.close()
 def __init__(self,
              q,
              api_key='ayQ7JwRPeUF2k_2UyLkM_6PDe_VdM8yJdagJm7',
              api_secret='eu6M9B3dKL3MdGrc8o6CivArUtvsYNKhDQC4kXj/',
              instrument='',
              method='',
              base_url='',
              *args,
              **kwargs):
     self.conn = tradeapi.StreamConn(api_key, api_secret, base_url)
     self.instrument = instrument
     self.method = method
     self.q = q
     self.conn.on('authenticated')(self.on_auth)
     self.conn.on(r'Q.*')(self.on_quotes)
     self.conn.on(r'account_updates')(self.on_account)
     self.conn.on(r'trade_updates')(self.on_trade)
Exemplo n.º 14
0
def main(args):
    api = alpaca.REST(API_KEY_ID, API_SECRET_KEY, api_version='v2')
    stream = alpaca.StreamConn(
        LIVE_API_KEY_ID,
        LIVE_API_SECRET_KEY)  # need live account to access streaming

    fleet = {}
    symbols = [symbol.strip() for symbol in TRADE_SYMBOLS.split(',')]
    for symbol in symbols:
        algo = RSIstrategy(symbol, lot=args.lot)
        fleet[symbol] = algo

    # Event handlers:
    @stream.on(r'^AM')
    async def on_bars(conn, channel, data):
        if data.symbol in fleet:
            fleet[data.symbol].on_bar(data)

    @stream.on(r'trade_updates')
    async def on_trade_updates(conn, channel, data):
        logger.info(f'trade_updates {data}')
        symbol = data.order['symbol']
        if symbol in fleet:
            fleet[symbol].on_order_update(data.event, data.order)

    async def periodic():
        while True:
            if not api.get_clock().is_open:
                logger.info('exit as market is not open')
                sys.exit(0)
            await asyncio.sleep(30)
            positions = api.list_positions()
            for symbol, algo in fleet.items():
                pos = [p for p in positions if p.symbol == symbol]
                algo.checkup(pos[0] if len(pos) > 0 else None)

    channels = ['trade_updates'] + ['AM.' + symbol for symbol in symbols]

    loop = stream.loop
    loop.run_until_complete(
        asyncio.gather(
            stream.subscribe(channels),
            periodic(),
        ))
    loop.close()
Exemplo n.º 15
0
def main(args):
    api = alpaca.REST()
    stream = alpaca.StreamConn()

    fleet = {}
    symbols = args.symbols
    for symbol in symbols:
        algo = ScalpAlgo(api, symbol, lot=args.lot)
        fleet[symbol] = algo

    @stream.on(r"^AM")
    async def on_bars(conn, channel, data):
        if data.symbol in fleet:
            fleet[data.symbol].on_bar(data)

    @stream.on(r"trade_updates")
    async def on_trade_updates(conn, channel, data):
        logger.info(f"trade_updates {data}")
        symbol = data.order["symbol"]
        if symbol in fleet:
            fleet[symbol].on_order_update(data.event, data.order)

    async def periodic():
        while True:
            if not api.get_clock().is_open:
                logger.info("exit as market is not open")
                sys.exit(0)
            await asyncio.sleep(30)
            positions = api.list_positions()
            for symbol, algo in fleet.items():
                pos = [p for p in positions if p.symbol == symbol]
                algo.checkup(pos[0] if len(pos) > 0 else None)

    channels = ["trade_updates"] + ["AM." + symbol for symbol in symbols]

    loop = stream.loop
    loop.run_until_complete(
        asyncio.gather(
            stream.subscribe(channels),
            periodic(),
        ))
    loop.close()
Exemplo n.º 16
0
    def _get_stream(self, context):
        set_context(context)
        asyncio.set_event_loop(asyncio.new_event_loop())
        conn = tradeapi.StreamConn(
            self._key_id,
            self._secret,
            self._base_url,
            data_url=os.environ.get("DATA_PROXY_WS", ''),
            data_stream='polygon' if self._use_polygon else 'alpacadatav1')
        channels = ['trade_updates']

        @conn.on(r'trade_updates')
        async def handle_trade_update(conn, channel, data):
            # Check for any pending orders
            waiting_order = self._orders_pending_submission.get(
                data.order['client_order_id'])
            if waiting_order is not None:
                if data.event == 'fill':
                    # Submit the waiting order
                    self.order(*waiting_order)
                    self._orders_pending_submission.pop(
                        data.order['client_order_id'], None)
                elif data.event in ['canceled', 'rejected']:
                    # Remove the waiting order
                    self._orders_pending_submission.pop(
                        data.order['client_order_id'], None)

            if data.event in ['canceled', 'rejected', 'fill']:
                self._open_orders.pop(data.order['client_order_id'], None)
            else:
                self._open_orders[data.order['client_order_id']] = (
                    self._order2zp(Order(data.order)))

        while 1:
            try:
                conn.run(channels)
                log.info("Connection reestablished")
            except Exception:
                from time import sleep
                sleep(5)
                asyncio.set_event_loop(asyncio.new_event_loop())
Exemplo n.º 17
0
def run():
    global quote
    global position

    # init global variables
    quote = Quote()
    position = Position()

    # inputs
    symbol = 'SNAP'
    max_shares = 500
    opts = dict(base_url="https://paper-api.alpaca.markets", **cfg.keys)

    # Create API objects which can be used to submit orders, stream data etc.
    api = tradeapi.REST(**opts)
    conn = tradeapi.StreamConn(**opts)

    # Initiliaze our message handling
    @conn.on(r"Q.*$")
    async def on_quote(conn, channel, data):
        """
        Quote update
        """
        streams.process_quote(channel, data, old_quote=quote)

    @conn.on(r"T.*$")
    async def on_trade(conn, channel, data):
        """
        Trade update
        """
        streams.process_trade(channel, data)

    @conn.on(r"trade_updates")
    async def on_trade_updates(conn, channel, data, position=position):
        """
        Order update
        """
        streams.process_order(channel, data)

    # start listening - blocks forever
    conn.run(["trade_updates", f"T.{symbol}", f"Q.{symbol}"])
async def producer_async_main(
    queues: List[Queue],
    scanner_queue: Queue,
    num_consumer_processes: int,
):
    await create_db_connection(str(config.dsn))

    await run(queues=queues)

    trade_ws = tradeapi.StreamConn(
        base_url=config.alpaca_base_url,
        key_id=config.alpaca_api_key,
        secret_key=config.alpaca_api_secret,
    )

    trade_updates_task = asyncio.create_task(
        trade_run(ws=trade_ws, queues=queues),
        name="trade_updates_task",
    )

    scanner_input_task = asyncio.create_task(
        scanner_input(scanner_queue, queues, num_consumer_processes),
        name="scanner_input",
    )
    tear_down = asyncio.create_task(
        teardown_task(
            timezone("America/New_York"),
            [trade_ws],
            [scanner_input_task],
        )
    )

    await asyncio.gather(
        trade_updates_task,
        scanner_input_task,
        tear_down,
        return_exceptions=True,
    )

    tlog("producer_async_main() completed")
def run():
    # conn = tradeapi.stream2.StreamConn(base_url=base_url, key_id=api_key, secret_key=secret_key)
    conn = tradeapi.StreamConn(base_url=base_url, key_id=api_key, secret_key=secret_key)

    symbols = [ 'AAPL', 'MSFT', 'TSLA' ]

    channels = ['trade_updates']
    for symbol in symbols:
        symbol_channels = ['AM.{}'.format(symbol)]
        channels += symbol_channels
    print('Watching {} symbols.'.format(len(symbols)))

    count = {
        'AAPL': -1,
        'MSFT': 0,
        'TSLA': 1
    }

    @conn.on(r'^AM\..+$')
    async def on_bar(connection, channel, data):
        symbol = data.symbol
        if symbol in symbols:
            count[symbol] += 1
            print(f'{symbol}: {count[symbol]}')
            if count[symbol] > 3:
                symbols.remove(symbol)
                if len(symbols) <= 0:
                    print(conn)
                    conn.close(renew=False)
                    print('Stream connection closed.')
                conn.deregister(['AM.{}'.format(symbol)])
                print(f'Stopped watching {symbol}.')
    
    @conn.on(r'close')
    async def on_close():
        print('On close')

    run_ws(conn, channels)
    print('Trading completed!')
Exemplo n.º 20
0
def main(args):
    logger.info("Inside main function")
    api = alpaca.REST()
    stream = alpaca.StreamConn()
    logger.info("Instantiating scalp object with arguments ...")

    scalp = algo.Algo(api=api, symbol=args.symbol, lot=args.lot)

    @stream.on(r'^AM')
    async def on_bars(conn, channel, data):
        scalp.on_bar(data)

    @stream.on(r'trade_updates')
    async def on_trade_updates(conn, channel, data):
        logger.info(f'trade_updates {data}')
        symbol = data.order['symbol']
        if symbol == args.symbol:
            scalp.on_order_update(data.event, data.order)

    async def periodic():
        while True:
            if not api.get_clock().is_open:
                logger.info('exit as market is not open')
                sys.exit(0)
            await asyncio.sleep(30)
            positions = api.list_positions()
            pos = [p for p in positions if p.symbol == args.symbol]
            scalp.checkup(pos[0] if len(pos) > 0 else None)

    channels = ['trade_updates', args.symbol]

    loop = stream.loop
    loop.run_until_complete(
        asyncio.gather(
            stream.subscribe(channels),
            periodic(),
        ))
    def start_trading(self):
        conn = tradeapi.StreamConn(self.key_id, self.secret_key, self.base_url)

        # Listen for second aggregates and perform trading logic
        @conn.on(r'A$', [self.symbol])
        async def handle_agg(conn, channel, data):
            self.tick_index = (self.tick_index + 1) % (self.tick_size)
            print(self.tick_index)
            if self.tick_index == 0:
                # It's time to update

                # Update price info
                tick_open = self.last_price
                tick_close = data.close
                self.last_price = tick_close

                # Update streak info
                diff = truncate(tick_close, 2) - truncate(tick_open, 2)
                if diff != 0:
                    # There was a meaningful change in the price
                    self.streak_count += 1
                    increasing = tick_open > tick_close
                    if self.streak_increasing != increasing:
                        # It moved in the opposite direction of the streak.
                        # Therefore, the streak is over, and we should reset.

                        # Empty out the position
                        self.send_order(0)

                        # Reset variables
                        self.streak_increasing = increasing
                        self.streak_start = tick_open
                        self.streak_count = 0
                    else:
                        # Calculate the number of shares we want to be holding
                        total_buying_power = self.equity * \
                            self.margin_multiplier
                        target_value = (2**self.streak_count) * \
                            (self.base_bet / 100) * total_buying_power
                        if target_value > total_buying_power:
                            # Limit the amount we can buy to a bit (1 share)
                            # less than our total buying power
                            target_value = total_buying_power - self.last_price
                        target_qty = int(target_value / self.last_price)
                        if self.streak_increasing:
                            target_qty = target_qty * -1
                        self.send_order(target_qty)

                # Update our account balance
                self.equity = float(self.api.get_account().equity)

        # Listen for updates to our orders
        @conn.on(r'trade_updates')
        async def handle_trade(conn, channel, data):
            symbol = data.order['symbol']
            if symbol != self.symbol:
                # The order was for a position unrelated to this script
                return

            event_type = data.event
            qty = int(data.order['filled_qty'])
            side = data.order['side']
            oid = data.order['id']

            if event_type == 'fill' or event_type == 'partial_fill':
                # Our position size has changed
                self.position = int(data.position_qty)
                print(f'New position size due to order fill: {self.position}')
                if (event_type == 'fill' and self.current_order
                        and self.current_order.id == oid):
                    self.current_order = None
            elif event_type == 'rejected' or event_type == 'canceled':
                if self.current_order and self.current_order.id == oid:
                    # Our last order should be removed
                    self.current_order = None
            elif event_type != 'new':
                print(f'Unexpected order event type {event_type} received')

        conn.run([f'A.{self.symbol}', 'trade_updates'])
Exemplo n.º 22
0
def run(args):
    symbol = args.symbol
    max_shares = args.quantity
    opts = {}
    if args.key_id:
        opts['PKWN2459FP6IFHSCDMO9'] = args.key_id
    if args.secret_key:
        opts['ibvqEPLK7BQvb/7nCfqIsW6Jsi8kMf4xE/QOYz4u'] = args.secret_key
    if args.base_url:
        opts['base_url'] = args.base_url
    elif 'PKWN2459FP6IFHSCDMO9' in opts and opts['PKWN2459FP6IFHSCDMO9'].startswith('PK'):
        opts['base_url'] = 'https://paper-api.alpaca.markets'
    # Create an API object which can be used to submit orders, etc.
    api = tradeapi.REST(**opts)

    symbol = symbol.upper()
    quote = Quote()
    qc = 'Q.%s' % symbol
    tc = 'T.%s' % symbol
    position = Position()

    # Establish streaming connection
    conn = tradeapi.StreamConn(**opts)

    # Define our message handling
    @conn.on(r'Q$')
    async def on_quote(conn, channel, data):
        # Quote update received
        quote.update(data)

    @conn.on(r'T$')
    async def on_trade(conn, channel, data):
        if quote.traded:
            return
        # We've received a trade and might be ready to follow it
        if (
            data.timestamp <= (
                quote.time + pd.Timedelta(np.timedelta64(50, 'ms'))
            )
        ):
            # The trade came too close to the quote update
            # and may have been for the previous level
            return
        if data.size >= 100:
            # The trade was large enough to follow, so we check to see if
            # we're ready to trade. We also check to see that the
            # bid vs ask quantities (order book imbalance) indicate
            # a movement in that direction. We also want to be sure that
            # we're not buying or selling more than we should.
            if (
                data.price == quote.ask
                and quote.bid_size > (quote.ask_size * 1.8)
                and (
                    position.total_shares + position.pending_buy_shares
                ) < max_shares - 100
            ):
                # Everything looks right, so we submit our buy at the ask
                try:
                    o = api.submit_order(
                        symbol=symbol, qty='100', side='buy',
                        type='limit', time_in_force='day',
                        limit_price=str(quote.ask)
                    )
                    # Approximate an IOC order by immediately cancelling
                    api.cancel_order(o.id)
                    position.update_pending_buy_shares(100)
                    position.orders_filled_amount[o.id] = 0
                    print('Buy at', quote.ask, flush=True)
                    quote.traded = True
                except Exception as e:
                    print(e)
            elif (
                data.price == quote.bid
                and quote.ask_size > (quote.bid_size * 1.8)
                and (
                    position.total_shares - position.pending_sell_shares
                ) >= 100
            ):
                # Everything looks right, so we submit our sell at the bid
                try:
                    o = api.submit_order(
                        symbol=symbol, qty='100', side='sell',
                        type='limit', time_in_force='day',
                        limit_price=str(quote.bid)
                    )
                    # Approximate an IOC order by immediately cancelling
                    api.cancel_order(o.id)
                    position.update_pending_sell_shares(100)
                    position.orders_filled_amount[o.id] = 0
                    print('Sell at', quote.bid, flush=True)
                    quote.traded = True
                except Exception as e:
                    print(e)

    @conn.on(r'trade_updates')
    async def on_trade_updates(conn, channel, data):
        # We got an update on one of the orders we submitted. We need to
        # update our position with the new information.
        event = data.event
        if event == 'fill':
            if data.order['side'] == 'buy':
                position.update_total_shares(
                    int(data.order['filled_qty'])
                )
            else:
                position.update_total_shares(
                    -1 * int(data.order['filled_qty'])
                )
            position.remove_pending_order(
                data.order['id'], data.order['side']
            )
        elif event == 'partial_fill':
            position.update_filled_amount(
                data.order['id'], int(data.order['filled_qty']),
                data.order['side']
            )
        elif event == 'canceled' or event == 'rejected':
            position.remove_pending_order(
                data.order['id'], data.order['side']
            )

    conn.run(
        ['trade_updates', tc, qc]
    )
Exemplo n.º 23
0
# Asset used as volatility indicator (VXX by default)
volatility = 'VXX'

# ETF for up (leverage should ideally match spy_down)
spy_up = 'SPXL'
# ETF for down  (leverage should ideally match spy_up)
spy_down = 'SPXS'

# REST API Instance for placing orders
order_api = api.REST(base_url=os.getenv("ORDERS_BASE_URL"),
                     key_id=os.getenv("ORDERS_KEY"),
                     secret_key=os.getenv("ORDERS_SECRET"))

# Streaming API instance for real-time quotes
quote_api = api.StreamConn(base_url=os.getenv("QUOTES_BASE_URL"),
                           key_id=os.getenv("QUOTES_KEY"),
                           secret_key=os.getenv("QUOTES_SECRET"))


def run(tickers, order_api, quote_api):

    # Use trade updates to keep track of our portfolio
    @quote_api.on(r'trade_update')
    async def handle_trade_update(conn, channel, data):
        print('Trade update')

    @quote_api.on(r'A$')
    async def handle_second_bar(conn, channel, data):
        print('1s bar update')

    # Enter main loop
Exemplo n.º 24
0
 def connect(self):
     return tradeapi.StreamConn(str(self.api_key), str(self.api_secret),
                                str(self.base_url))
Exemplo n.º 25
0
    key_id = "PKW2JHY75SLSKA1VUX74"
    secret_key = "mS59yaHEGv5CyBFmaGqrmziRRkF14ioEWS8ENmGG"
    base_url = 'https://paper-api.alpaca.markets'
    data_url = 'https://data.alpaca.markets'

    # The connection to the Alpaca API
    api = tradeapi.REST(
        key_id,
        secret_key,
        base_url
    )

    conn = tradeapi.StreamConn(
            key_id,
            secret_key,
            base_url=base_url,
            data_url=data_url,
            data_stream='polygon'
        )
    # This is another way to setup wrappers for websocket callbacks, handy if conn is not global.
    on_minute = conn.on(r'AM$')(on_minute)
    on_tick = conn.on(r'A$')(on_tick)
    on_data = conn.on(r'.*')(on_data)

    # This is an example of how you can add your own async functions into the loop
    # This one just watches this program for edits and tries to restart it
    asyncio.ensure_future(reloadWatch(__file__, sys.argv)())

    try:
        if opt.all:
            # Note to see all these channels, you'd need to add a handler
class AlpacaStockTradingEnv(gym.Env):
    """
    Description:
        A live stock trading environment utilizing Alpaca Trade API.
        The environment utilizes websockets for trade updates and market data.
        Alpaca API keys are required for this environment to operate.
        The data is the normalized OHLC and volume values for
        1 minute candlesticks.

    Observation:
        Type: Box(low=0, high=1, shape=(5, observation_size), dtype=np.float16)
        Description: The observation_size is set during environment creation
            and represents the number of 1min candlesticks in the observation.
        Num	Observation               Min             Max
        0	Open                      0               1
        1	High                      0               1
        2	Low                       0               1
        3	Close                     0               1
        4   Volume                    0               1

    Actions:
        Type: spaces.Box(
            low=np.array([-1]), high=np.array([1]), dtype=np.float16)
        Description: The action space represents the percentage of the account
            to invest. Negative is short, Positive is long.
        Num	Action                              Min         Max
        0	Percentage of account to invest     -1          1

    Reward:
        Reward is the unrealized profit/loss for the next observation.
        Realized Profit/Loss is also tracked in the info dict.
    Starting State:
        First candlestick observation once the market opens
    Episode Termination:
        Agent loses more than 5% or 11 minutes before market close.
    """

    metadata = {'render.modes': ['human']}
    eastern = timezone('US/Eastern')
    channels = ['AM.*', 'trade_updates']

    live_conn = tradeapi.StreamConn(LIVE_APCA_API_KEY_ID,
                                    LIVE_APCA_API_SECRET_KEY)
    paper_conn = tradeapi.StreamConn(PAPER_APCA_API_KEY_ID,
                                     PAPER_APCA_API_SECRET_KEY,
                                     PAPER_APCA_API_BASE_URL)

    try:
        # tLWS = threading.Thread(
        #     target=live_conn.run, args=[channels])
        # tLWS.start()
        logger.info('Connecting to channels: %s', channels)
        tPWS = threading.Thread(target=paper_conn.run, args=[channels])
        tPWS.start()
    except RuntimeError as e:
        # Already running
        logger.error(e)

    def __init__(self,
                 symbol,
                 previous_close,
                 daily_avg_volume=None,
                 live=False,
                 observation_size=1,
                 volume_enabled=True,
                 allotted_amount=10000.0):
        super(AlpacaStockTradingEnv, self).__init__()

        self.current_step = 0
        self.current_episode = 0

        self.live = live

        self.symbol = symbol
        self.market = None

        self.paper_api = tradeapi.REST(PAPER_APCA_API_KEY_ID,
                                       PAPER_APCA_API_SECRET_KEY,
                                       PAPER_APCA_API_BASE_URL,
                                       api_version='v2')

        self.live_api = tradeapi.REST(LIVE_APCA_API_KEY_ID,
                                      LIVE_APCA_API_SECRET_KEY,
                                      api_version='v2')

        if self.live:
            self._on_minute_bars =\
                self.live_conn.on(r'^AM$')(self._on_minute_bars)
            self._on_trade_updates =\
                self.live_conn.on(r'trade_updates$')(self._on_trade_updates)
        else:
            self._on_minute_bars =\
                self.paper_conn.on(r'^AM$')(self._on_minute_bars)
            self._on_trade_updates =\
                self.paper_conn.on(r'trade_updates$')(self._on_trade_updates)

        self.volume_enabled = volume_enabled
        self.asset_data = None
        self.normalized_asset_data = None
        self.previous_close = previous_close

        if self.volume_enabled:
            self.daily_avg_volume = daily_avg_volume
        self.observation_size = observation_size

        self.base_value = allotted_amount
        self.equity = [allotted_amount]
        self.cash = [allotted_amount]
        self.profit_loss = [0.0]
        self.positions = [(0, 0.0)]  # (qty, price)
        self.alpaca_positions = [(0, 0.0)]  # actual traded positions
        self.current_alpaca_position = (0, 0.0)
        self.trades = []
        self.rewards = [0.0]
        self.max_qty = None

        # Each action represents the amount of the portfolio that should be
        # invested ranging from -1 to 1. Negative is short, positive is long.
        self.action_space = spaces.Box(low=np.array([-1]),
                                       high=np.array([1]),
                                       dtype=np.float16)

        # Normalized values for: Open, High, Low, Close, Volume
        self.observation_space = spaces.Box(low=0,
                                            high=1,
                                            shape=(5, observation_size),
                                            dtype=np.float16)

        logger.info('Initialized AlpacaStockTradingEnv: ')
        logger.info('Live: %s', self.live)
        logger.info('Symbol: %s', self.symbol)
        logger.info('volume_enabled: %s', self.volume_enabled)
        logger.info('previous_close: %s', self.previous_close)
        if self.volume_enabled:
            logger.info('daily_avg_volume: %s', self.daily_avg_volume)
        logger.info('observation_size: %s', self.observation_size)
        logger.info('base_value: %s', self.base_value)
        logger.info('equity: %s', self.equity)
        logger.info('cash: %s', self.cash)
        logger.info('profit_loss: %s', self.profit_loss)

    def _normalize_data(self):

        normalized_dataframe = self.asset_data.copy()

        normalized_dataframe['open'] =\
            normalized_dataframe['open'] / (2 * self.previous_close)

        normalized_dataframe['high'] =\
            normalized_dataframe['high'] / (2 * self.previous_close)

        normalized_dataframe['low'] =\
            normalized_dataframe['low'] / (2 * self.previous_close)

        normalized_dataframe['close'] =\
            normalized_dataframe['close'] / (2 * self.previous_close)

        normalized_dataframe['volume'] =\
            normalized_dataframe['volume'] / self.daily_avg_volume

        return normalized_dataframe

    def _initialize_data(self):
        self.market = self.live_api.get_clock()

        today = datetime.datetime.now(self.eastern)

        if self.market.is_open:
            _open = int(
                self.eastern.localize(
                    datetime.datetime.combine(today, datetime.time(
                        9, 30))).timestamp() * 1000)
        else:
            _open = int(self.market.next_open.timestamp() * 1000)

        close = int(self.market.next_close.timestamp() * 1000)

        self.asset_data = self.live_api.polygon.historic_agg_v2(
            self.symbol, 1, 'minute', _open, close).df

        self.current_step = len(self.asset_data)

        self.normalized_asset_data = self._normalize_data()

    def _await_market_open(self):
        while not self.market.is_open:
            curr_time = datetime.datetime.now(self.eastern)
            next_open = self.market.next_open.astimezone(self.eastern)
            wait_time = (next_open - curr_time).seconds + 10

            print('Waiting ' + str(wait_time) + ' seconds for market to open.')

            time.sleep(wait_time)
            self.market = self.live_api.get_clock()

    async def _on_minute_bars(self, conn, channel, bar):
        if self.normalized_asset_data is not None:
            if self.market.is_open:
                if bar.symbol == self.symbol:

                    # if len(self.asset_data) != 0:
                    #     # TODO test
                    #     # Missing a bar or market is frozen
                    #     if bar.start >\
                    #             self.asset_data.index[-1]\
                    #             + timedelta(seconds=90):
                    #         self._initialize_data()

                    new_row = {
                        'open': bar.open,
                        'high': bar.high,
                        'low': bar.low,
                        'close': bar.close,
                        'volume': bar.volume
                    }
                    self.asset_data.loc[bar.start] = new_row
                    self.normalized_asset_data = self._normalize_data()

    async def _on_trade_updates(self, conn, channel, account):
        event = account.event
        order = account.order
        # logger.info('async _on_trade_updates called for %s', order)
        if order['symbol'] == self.symbol:
            if event == 'fill' or event == 'partial_fill':
                close_price = self.asset_data.iloc[-1].close

                fill_price = float(account.price)
                position_qty = int(account.position_qty)
                curr_qty = self.current_alpaca_position[0]
                curr_avg_price = self.current_alpaca_position[1]

                if order['side'] == 'buy':
                    fill_qty = int(account.qty)
                    slippage_per_share = close_price - fill_price
                else:
                    fill_qty = -int(account.qty)
                    slippage_per_share = fill_price - close_price

                total_slippage = slippage_per_share * abs(fill_qty)

                if position_qty != 0:
                    if curr_qty >= 0 and fill_qty > 0\
                            or curr_qty <= 0 and fill_qty < 0:
                        avg_price = ((abs(fill_qty) * fill_price) +
                                     (abs(curr_qty) *
                                      curr_avg_price)) / abs(position_qty)
                    else:
                        avg_price = curr_avg_price
                else:
                    avg_price = 0.0

                self.current_alpaca_position = (position_qty, avg_price)

                trade = {
                    'symbol': self.symbol,
                    'order_type': order['order_type'],
                    'filled_at': order['filled_at'],
                    'fill_price': fill_price,
                    'fill_qty': int(account.qty),
                    'observation_time': self.asset_data.index[-1],
                    'observation_price': close_price,
                    'side': order['side'],
                    'slippage_per_share': slippage_per_share,
                    'total_slippage': total_slippage,
                }
                self.trades.append(trade)

    def _next_observation(self):
        """Get the stock data for the current observation size."""

        if not self.market.is_open:
            tAMO = threading.Thread(target=self._await_market_open)
            tAMO.start()
            tAMO.join()

        # TODO clean up with wait() or threading
        while len(self.normalized_asset_data) <= self.current_step:
            # Wait for new data to be appended
            continue

        offset = self.current_step + 1 - self.observation_size

        if offset < 0:
            # Less data than observation_size
            if self.volume_enabled:
                observation_zeros = np.zeros([5, abs(offset)])
            else:
                observation_zeros = np.zeros([4, abs(offset)])
            offset = 0

        observation = np.array([
            self.normalized_asset_data.iloc[offset:self.current_step +
                                            1]['open'].values,
            self.normalized_asset_data.iloc[offset:self.current_step +
                                            1]['high'].values,
            self.normalized_asset_data.iloc[offset:self.current_step +
                                            1]['low'].values,
            self.normalized_asset_data.iloc[offset:self.current_step +
                                            1]['close'].values
        ])

        if self.volume_enabled:
            observation = np.vstack(
                (observation,
                 self.normalized_asset_data.iloc[offset:self.current_step +
                                                 1]['volume'].values))

        if observation.shape[1] < self.observation_size:
            observation = np.concatenate((observation, observation_zeros),
                                         axis=1)

        return observation

    def _submit_order(self, qty, order_type='market'):
        if qty == 0:
            # no trade needed
            return

        side = 'buy' if qty > 0 else 'sell'

        try:
            if self.live:
                self.live_api.submit_order(symbol=self.symbol,
                                           qty=abs(qty),
                                           side=side,
                                           type=order_type,
                                           time_in_force='day')
            else:
                self.paper_api.submit_order(symbol=self.symbol,
                                            qty=abs(qty),
                                            side=side,
                                            type=order_type,
                                            time_in_force='day')
        except Exception as e:
            print(e)
            # TODO return error
            # check for:
            # 403 Forbidden: Buying power or shares is not sufficient.
            # 422 Unprocessable: Input parameters are not recognized.
            return e

    def _close_position(self):
        if self.current_alpaca_position[0] != 0:
            if self.live:
                self.live_api.close_position(self.symbol)
            else:
                self.paper_api.close_position(self.symbol)

            # TODO clean up with wait() or threading
            while self.current_alpaca_position[0] != 0:
                # wait for position to close
                continue

    def _take_action(self, action):
        curr_price = self.asset_data.iloc[self.current_step]['close']

        if self.positions[-1][0] != self.current_alpaca_position[0]:
            print('Order did not complete..')
            # TODO determine how to cancel incomplete orders
            pass

        # Current position
        curr_qty, avg_price = self.current_alpaca_position
        curr_invested = curr_qty / self.max_qty

        if action == 0:
            # Close position
            trade_qty = -curr_qty
        else:
            target_change = action - curr_invested
            trade_qty = int(target_change * self.equity[-1] / curr_price)

        if curr_qty == 0:
            # Simple short or long trade
            purchase_amount = abs(trade_qty * curr_price)
            new_position = (trade_qty, curr_price)
            self.positions.append(new_position)
            self.cash.append(self.cash[-1] - purchase_amount)
            self.equity.append(self.cash[-1] + purchase_amount)
            self.profit_loss.append(0.0)

            # Submit order
            tOrder = threading.Thread(target=self._submit_order,
                                      args=[trade_qty])
            tOrder.start()

        elif curr_qty > 0 and curr_qty + trade_qty < 0 or\
                curr_qty < 0 and curr_qty + trade_qty > 0:
            # Trade crosses from short to long or long to short

            # Close current position, update P/L and cash
            if curr_qty > 0:
                # Closing long position
                self.cash.append(self.cash[-1] + curr_qty * curr_price)
                self.profit_loss.append((curr_price - avg_price) * curr_qty)

                tOrder = threading.Thread(target=self._close_position)
                tOrder.start()
                tOrder.join()
            else:
                # Closing short position
                self.cash.append(self.cash[-1] + abs(curr_qty) *
                                 (avg_price - (curr_price - avg_price)))
                self.profit_loss.append(
                    (avg_price - curr_price) * abs(curr_qty))

                tOrder = threading.Thread(target=self._close_position)
                tOrder.start()
                tOrder.join()

            # Simple short or long trade
            trade_qty += curr_qty
            purchase_amount = abs(trade_qty * curr_price)
            new_position = (trade_qty, curr_price)
            self.positions.append(new_position)
            self.cash.append(self.cash[-1] - purchase_amount)
            self.equity.append(self.cash[-1] + purchase_amount)

            # Submit order
            tOrder = threading.Thread(target=self._submit_order,
                                      args=[trade_qty])
            tOrder.start()
        else:
            # Trade increases or reduces position (including closing out)

            if curr_qty > 0 and trade_qty > 0 or\
                    curr_qty < 0 and trade_qty < 0:
                # Adding to position

                purchase_amount = abs(trade_qty * curr_price)

                while self.cash[-1] < purchase_amount:
                    # Descrease trade_qty if not enough cash
                    trade_qty = trade_qty - 1 if trade_qty > 0\
                            else trade_qty + 1
                    purchase_amount = abs(trade_qty * curr_price)

                total_qty = trade_qty + curr_qty
                avg_price = (((trade_qty * curr_price) +
                              (curr_qty * avg_price)) / total_qty)
                new_position = (total_qty, avg_price)
                self.positions.append(new_position)
                self.cash.append(self.cash[-1] - purchase_amount)
                self.profit_loss.append(0.0)

                if total_qty > 0:
                    # Long position
                    self.equity.append(self.cash[-1] +
                                       (total_qty * curr_price))
                else:
                    # Short position
                    self.equity.append(self.cash[-1] + abs(total_qty) *
                                       (avg_price - (curr_price - avg_price)))

                # Submit order
                tOrder = threading.Thread(target=self._submit_order,
                                          args=[trade_qty])
                tOrder.start()

            # Reducing position or not changing
            else:
                if trade_qty > 0:
                    # Reducing short position
                    self.cash.append(self.cash[-1] + abs(trade_qty) *
                                     (avg_price - (curr_price - avg_price)))
                    self.profit_loss.append(
                        (avg_price - curr_price) * trade_qty)
                else:
                    # Reducing long position
                    self.cash.append(self.cash[-1] +
                                     abs(trade_qty * curr_price))
                    self.profit_loss.append(
                        (curr_price - avg_price) * abs(trade_qty))

                net_qty = curr_qty + trade_qty

                if net_qty == 0:
                    new_position = (net_qty, 0.0)
                else:
                    new_position = (net_qty, avg_price)

                self.positions.append(new_position)

                if net_qty > 0:
                    # Long position
                    self.equity.append(self.cash[-1] +
                                       abs(net_qty * curr_price))
                else:
                    # Short position
                    self.equity.append(self.cash[-1] + abs(net_qty) *
                                       (avg_price - (curr_price - avg_price)))

                # Submit order
                tOrder = threading.Thread(target=self._submit_order,
                                          args=[trade_qty])
                tOrder.start()

    def step(self, action):
        # Execute one time step within the environment
        self._take_action(action)

        curr_price = self.asset_data.iloc[self.current_step]['close']

        self.current_step += 1

        obs = self._next_observation()

        next_price = self.asset_data.iloc[self.current_step]['close']

        reward = (next_price - curr_price) * self.positions[-1][0]
        self.equity[-1] += reward
        self.rewards.append(reward)
        self.alpaca_positions.append(self.current_alpaca_position)

        info = {
            "profit_loss": {
                "date_time": self.asset_data.index[-2],
                "profit_loss": self.profit_loss[-1],
                "reward": reward,
                "mode": 'live' if self.live else 'paper'
            },
            "trades": self.trades,
            "positions": {
                "env_position": self.positions[-1],
                "actual_position": self.current_alpaca_position
            }
        }

        # Clear trades for future trades
        self.trades = []

        # Close 11 minutes before end of day
        now = datetime.datetime.now(self.eastern)

        stop_time = self.eastern.localize(
            datetime.datetime.combine(now, datetime.time(15, 49)))

        if now > stop_time:
            done = True
        # TODO this needs to be more reflective of real data in future
        elif (self.equity[-1] - self.base_value) / self.base_value <= -0.02:
            done = True
        else:
            done = False

        if done:
            self.close()
            reward = 0.0
            self.rewards.append(reward)
            self.alpaca_positions.append(self.current_alpaca_position)
            info["closing_trades"] = {
                "profit_loss": {
                    "date_time": self.asset_data.index[-1],
                    "profit_loss": self.profit_loss[-1],
                    "reward": reward,
                    "mode": 'live' if self.live else 'paper'
                },
                "trades": self.trades,
                "positions": {
                    "env_position": self.positions[-1],
                    "actual_position": self.current_alpaca_position
                }
            }
        return obs, reward, done, info

    def reset(self):
        """Reset the state of the environment to an initial state"""
        self.current_step = 0

        self._initialize_data()

        self.equity = [self.base_value]
        self.profit_loss = [0.0]
        self.cash = [self.base_value]
        self.positions = [(0, 0.0)]
        self.rewards = [0.0]
        self.trades = []

        observation = self._next_observation()

        self.max_qty = int((self.base_value /
                            self.asset_data.iloc[self.current_step]['open']))

        return observation

    def render(self, mode='human', close=False):
        """Render the environment to the screen"""
        pass

    def close(self):
        # Ensure no positions are held over night
        # tOrder = threading.Thread(
        #     target=self._close_position)
        # tOrder.start()
        # tOrder.join()

        # Close positions
        action = np.array([0.0])
        self._take_action(action)
Exemplo n.º 27
0
    def start_trading(self):
        conn = tradeapi.StreamConn(
            self.key_id,
            self.secret_key,
            base_url=self.base_url,
            data_url=self.data_url,
            data_stream='polygon' if USE_POLYGON else 'alpacadatav1'
        )

        # Listen for second aggregates and perform trading logic
        @conn.on(r'A$', [self.symbol])
        async def handle_agg(conn, channel, data):
            self.tick_index = (self.tick_index + 1) % self.tick_size
            if self.tick_index == 0:
                # It's time to update

                # Update price info
                tick_open = self.last_price
                tick_close = data.close
                self.last_price = tick_close

                self.process_current_tick(tick_open, tick_close)

        # Listen for quote data and perform trading logic
        @conn.on(r'T\..+', [self.symbol])
        async def handle_alpaca_aggs(conn, channel, data):
            now = datetime.datetime.utcnow()
            if now - self.last_trade_time < datetime.timedelta(seconds=1):
                # don't react every tick unless at least 1 second past
                return
            self.last_trade_time = now
            self.tick_index = (self.tick_index + 1) % self.tick_size
            if self.tick_index == 0:
                # It's time to update

                # Update price info
                tick_open = self.last_price
                tick_close = data.price
                self.last_price = tick_close

                self.process_current_tick(tick_open, tick_close)

        # Listen for updates to our orders
        @conn.on(r'trade_updates')
        async def handle_trade(conn, channel, data):
            symbol = data.order['symbol']
            if symbol != self.symbol:
                # The order was for a position unrelated to this script
                return

            event_type = data.event
            qty = int(data.order['filled_qty'])
            side = data.order['side']
            oid = data.order['id']

            if event_type == 'fill' or event_type == 'partial_fill':
                # Our position size has changed
                self.position = int(data.position_qty)
                print(f'New position size due to order fill: {self.position}')
                if (event_type == 'fill' and self.current_order
                    and self.current_order.id == oid):
                    self.current_order = None
            elif event_type == 'rejected' or event_type == 'canceled':
                if self.current_order and self.current_order.id == oid:
                    # Our last order should be removed
                    self.current_order = None
            elif event_type != 'new':
                print(f'Unexpected order event type {event_type} received')

        if USE_POLYGON:
            conn.run([f'A.{self.symbol}', 'trade_updates'])
        else:
            conn.run([f'alpacadatav1/T.{self.symbol}', 'trade_updates'])
Exemplo n.º 28
0
SECRET_KEY = ""  # Your Secret Key
SLACK_TOKEN = ""  # Slack OAuth Access Token
CHANNEL = ""

config = {
    "key_id": os.environ.get("KEY_ID", KEY_ID),
    "secret_key": os.environ.get("SECRET_KEY", SECRET_KEY),
    "base_url": os.environ.get("BASE_URL", "https://paper-api.alpaca.markets"),
    "slack_token": os.environ.get("SLACK_TOKEN", SLACK_TOKEN),
    "channel": os.environ.get("CHANNEL", CHANNEL)
}

# Set up environment
conn = tradeapi.StreamConn(
    key_id=config.get('key_id'),
    secret_key=config.get('secret_key'),
    base_url=config.get('base_url'),
)
api = tradeapi.REST(
    key_id=config.get('key_id'),
    secret_key=config.get('secret_key'),
    base_url=config.get('base_url'),
)

# Initialize the Flask object which will be used to handle HTTP requests
# from Slack
app = Flask(__name__)

# Initialize the dictionary of streams that we are listening to; None
# denotes not listening
streams = {
Exemplo n.º 29
0
def run(tickers, market_open_dt, market_close_dt):
    # Establish streaming connection
    conn = tradeapi.StreamConn(base_url=base_url,
                               key_id=api_key_id,
                               secret_key=api_secret)

    # Update initial state with information from tickers
    volume_today = {}
    prev_closes = {}
    for ticker in tickers:
        symbol = ticker.ticker
        prev_closes[symbol] = ticker.prevDay['c']
        volume_today[symbol] = ticker.day['v']

    symbols = [ticker.ticker for ticker in tickers]
    print('Tracking {} symbols.'.format(len(symbols)))
    minute_history = get_1000m_history_data(symbols)

    portfolio_value = float(api.get_account().portfolio_value)

    open_orders = {}
    positions = {}

    # Cancel any existing open orders on watched symbols
    existing_orders = api.list_orders(limit=500)
    for order in existing_orders:
        if order.symbol in symbols:
            api.cancel_order(order.id)

    stop_prices = {}
    latest_cost_basis = {}

    # Track any positions bought during previous executions
    existing_positions = api.list_positions()
    for position in existing_positions:
        if position.symbol in symbols:
            positions[position.symbol] = float(position.qty)
            # Recalculate cost basis and stop price
            latest_cost_basis[position.symbol] = float(position.cost_basis)
            stop_prices[position.symbol] = (float(position.cost_basis) *
                                            default_stop)

    # Keep track of what we're buying/selling
    target_prices = {}
    partial_fills = {}

    # Use trade updates to keep track of our portfolio
    @conn.on(r'trade_update')
    async def handle_trade_update(conn, channel, data):
        symbol = data.order['symbol']
        last_order = open_orders.get(symbol)
        if last_order is not None:
            event = data.event
            if event == 'partial_fill':
                qty = int(data.order['filled_qty'])
                if data.order['side'] == 'sell':
                    qty = qty * -1
                positions[symbol] = (positions.get(symbol, 0) -
                                     partial_fills.get(symbol, 0))
                partial_fills[symbol] = qty
                positions[symbol] += qty
                open_orders[symbol] = data.order
            elif event == 'fill':
                qty = int(data.order['filled_qty'])
                if data.order['side'] == 'sell':
                    qty = qty * -1
                positions[symbol] = (positions.get(symbol, 0) -
                                     partial_fills.get(symbol, 0))
                partial_fills[symbol] = 0
                positions[symbol] += qty
                open_orders[symbol] = None
            elif event == 'canceled' or event == 'rejected':
                partial_fills[symbol] = 0
                open_orders[symbol] = None

    @conn.on(r'A$')
    async def handle_second_bar(conn, channel, data):
        symbol = data.symbol

        # First, aggregate 1s bars for up-to-date MACD calculations
        ts = data.start
        ts -= timedelta(seconds=ts.second, microseconds=ts.microsecond)
        try:
            current = minute_history[data.symbol].loc[ts]
        except KeyError:
            current = None
        new_data = []
        if current is None:
            new_data = [
                data.open, data.high, data.low, data.close, data.volume
            ]
        else:
            new_data = [
                current.open,
                data.high if data.high > current.high else current.high,
                data.low if data.low < current.low else current.low,
                data.close, current.volume + data.volume
            ]
        minute_history[symbol].loc[ts] = new_data

        # Next, check for existing orders for the stock
        existing_order = open_orders.get(symbol)
        if existing_order is not None:
            # Make sure the order's not too old
            submission_ts = existing_order.submitted_at.astimezone(
                timezone('America/New_York'))
            order_lifetime = ts - submission_ts
            if order_lifetime.seconds // 60 > 1:
                # Cancel it so we can try again for a fill
                api.cancel_order(existing_order.id)
            return

        # Now we check to see if it might be time to buy or sell
        since_market_open = ts - market_open_dt
        until_market_close = market_close_dt - ts
        if (since_market_open.seconds // 60 > 15
                and since_market_open.seconds // 60 < 60):
            # Check for buy signals

            # See if we've already bought in first
            position = positions.get(symbol, 0)
            if position > 0:
                return

            # See how high the price went during the first 15 minutes
            lbound = market_open_dt
            ubound = lbound + timedelta(minutes=15)
            high_15m = 0
            try:
                high_15m = minute_history[symbol][lbound:ubound]['high'].max()
            except Exception as e:
                # Because we're aggregating on the fly, sometimes the datetime
                # index can get messy until it's healed by the minute bars
                return

            # Get the change since yesterday's market close
            daily_pct_change = ((data.close - prev_closes[symbol]) /
                                prev_closes[symbol])
            if (daily_pct_change > .04 and data.close > high_15m
                    and volume_today[symbol] > 30000):
                # check for a positive, increasing MACD
                hist = macd(minute_history[symbol]['close'].dropna(),
                            n_fast=12,
                            n_slow=26)
                if (hist[-1] < 0 or not (hist[-3] < hist[-2] < hist[-1])):
                    return
                hist = macd(minute_history[symbol]['close'].dropna(),
                            n_fast=40,
                            n_slow=60)
                if hist[-1] < 0 or np.diff(hist)[-1] < 0:
                    return

                # Stock has passed all checks; figure out how much to buy
                stop_price = find_stop(data.close, minute_history[symbol], ts)
                stop_prices[symbol] = stop_price
                target_prices[symbol] = data.close + (
                    (data.close - stop_price) * 3)
                shares_to_buy = portfolio_value * risk // (data.close -
                                                           stop_price)
                if shares_to_buy == 0:
                    shares_to_buy = 1
                shares_to_buy -= positions.get(symbol, 0)
                if shares_to_buy <= 0:
                    return

                print('Submitting buy for {} shares of {} at {}'.format(
                    shares_to_buy, symbol, data.close))
                try:
                    o = api.submit_order(symbol=symbol,
                                         qty=str(shares_to_buy),
                                         side='buy',
                                         type='limit',
                                         time_in_force='day',
                                         limit_price=str(data.close))
                    open_orders[symbol] = o
                    latest_cost_basis[symbol] = data.close
                except Exception as e:
                    print(e)
                return
        if (since_market_open.seconds // 60 >= 24
                and until_market_close.seconds // 60 > 15):
            # Check for liquidation signals

            # We can't liquidate if there's no position
            position = positions.get(symbol, 0)
            if position == 0:
                return

            # Sell for a loss if it's fallen below our stop price
            # Sell for a loss if it's below our cost basis and MACD < 0
            # Sell for a profit if it's above our target price
            hist = macd(minute_history[symbol]['close'].dropna(),
                        n_fast=13,
                        n_slow=21)
            if (data.close <= stop_prices[symbol]
                    or (data.close >= target_prices[symbol] and hist[-1] <= 0)
                    or
                (data.close <= latest_cost_basis[symbol] and hist[-1] <= 0)):
                print('Submitting sell for {} shares of {} at {}'.format(
                    position, symbol, data.close))
                try:
                    o = api.submit_order(symbol=symbol,
                                         qty=str(position),
                                         side='sell',
                                         type='limit',
                                         time_in_force='day',
                                         limit_price=str(data.close))
                    open_orders[symbol] = o
                    latest_cost_basis[symbol] = data.close
                except Exception as e:
                    print(e)
            return
        elif (until_market_close.seconds // 60 <= 15):
            # Liquidate remaining positions on watched symbols at market
            try:
                position = api.get_position(symbol)
            except Exception as e:
                # Exception here indicates that we have no position
                return
            print('Trading over, liquidating remaining position in {}'.format(
                symbol))
            api.submit_order(symbol=symbol,
                             qty=position.qty,
                             side='sell',
                             type='market',
                             time_in_force='day')
            symbols.remove(symbol)
            if len(symbols) <= 0:
                conn.close()
            conn.deregister(['A.{}'.format(symbol), 'AM.{}'.format(symbol)])

    # Replace aggregated 1s bars with incoming 1m bars
    @conn.on(r'AM$')
    async def handle_minute_bar(conn, channel, data):
        ts = data.start
        ts -= timedelta(microseconds=ts.microsecond)
        minute_history[data.symbol].loc[ts] = [
            data.open, data.high, data.low, data.close, data.volume
        ]
        volume_today[data.symbol] += data.volume

    channels = ['trade_updates']
    for symbol in symbols:
        symbol_channels = ['A.{}'.format(symbol), 'AM.{}'.format(symbol)]
        channels += symbol_channels
    print('Watching {} symbols.'.format(len(symbols)))
    run_ws(conn, channels)
def run(tickers):
    # Establish streaming connection
    conn = tradeapi.StreamConn()

    symbols = [ticker.ticker for ticker in tickers]
    # symbols = ["SNOA"]

    print('Tracking {} symbols.'.format(len(symbols)))
    minute_history = get_min_history_data(symbols)

    # Connect to Minute Bars Data via Polygon
    @conn.on(r'AM$')
    async def handle_minute_bar(conn, channel, data):

        # add the new bar data to the minute history dataframe
        ts = data.start
        ts -= timedelta(microseconds=ts.microsecond)
        minute_history[data.symbol].loc[ts] = [
            data.open, data.high, data.low, data.close, data.volume
        ]

        alert = False

        # strip out only the bars we need
        totalBarsToEval = trend_bar_count + eval_bar_count
        history_in_scope = minute_history[data.symbol].tail(totalBarsToEval)
        df = history_in_scope.copy()

        # add the Heiken Ashi bar data:
        df = addHeikenAshi(df)

        # print('symbol = ' , data.symbol)
        # print('df =', df)

        # add some extra data to the frame
        df['symbol'] = data.symbol

        # price change
        df['prev_close'] = df['close'].shift()
        df['price_change'] = df['close'] - df['prev_close']
        df['%_price_change'] = (
            (df['close'] - df['prev_close']) / df['prev_close']) * 100

        # volume changes
        df['prev_volume'] = df['volume'].shift()
        df['volume_change'] = df['volume'] - df['prev_volume']
        df['%_volume_change'] = (
            (df['volume'] - df['prev_volume']) / df['prev_volume']) * 100

        # bar sizes absolute
        df['bar_size_abs'] = (df['close'] - df['open']).abs()

        # df.index = df.index.strftime("%x %I %p")
        # df = df.tail(1)
        # print(df)

        # trend values:
        trend_bars = df.head(trend_bar_count)
        trend_volume = trend_bars["volume"].mean()
        trend_price_avg = trend_bars["close"].mean()
        trend_price_max = trend_bars["close"].max()
        trend_price_min = trend_bars["close"].min()
        trend_bar_size_avg = trend_bars["bar_size_abs"].mean()

        # evaluation values:
        eval_bars = df.tail(eval_bar_count)
        eval_volume = eval_bars["volume"].mean()
        eval_price_avg = eval_bars["close"].mean()
        eval_ % _price_change_avg = eval_bars["%_price_change"].mean()
        eval_bar_size_avg = eval_bars["bar_size_abs"].mean()

        # calculated variables
        # FIXME: invalid value encountered in double_scalars
        # bar_size_factor = eval_bar_size_avg / trend_bar_size_avg

        # determine if it should be alerted:
        # if( eval_price_avg > trend_price_max and eval_volume > minimum_bar_volume):
        #         if(eval_%_price_change_avg > price_percentage_threshold or
        #            (bar_size_factor > bar_size_factor_threshold and eval_bar_size_avg > bar_size_threshold)):
        #             alert = True

        if (eval_price_avg > trend_price_max
                and eval_volume > minimum_bar_volume):
            if (eval_ % _price_change_avg > price_percentage_threshold):
                alert = True

        # return if the alert signal is flase
        if (alert == False):
            return

        # drop some unecesarry columns
        df = df.drop(columns=[
            'open', 'high', 'low', 'prev_close', 'prev_volume',
            'volume_change', '%_volume_change'
        ])

        # trim to the last 5 items in the frame
        alert_df = df.tail(5)

        # message = 'Price Momentum Alert:\n' + tabulate(alert_df, headers='keys', tablefmt='github', showindex=False, floatfmt=(",.2f",",.2f",",.2f",",.2f",",.2f",",.2f",",.0f"))
        message = 'Price Momentum Alert:\n' + tabulate(
            alert_df, headers='keys', tablefmt='github', showindex=True)
        print(message)

        if (postToDiscord):
            # retrieve the channel
            channel = client.get_channel(721931969138786364)
            print('Sending Results to Discord Channel - ', channel)

            # format the message as a block for discord
            message = '```' + message + '```'

            await channel.send(message)

    # define channels and run the scanner for each
    channels = []
    for symbol in symbols:
        symbol_channels = ['A.{}'.format(symbol), 'AM.{}'.format(symbol)]
        channels += symbol_channels
    print('Watching {} symbols.'.format(len(symbols)))
    run_ws(conn, channels)