Does anyone really like using Alpaca?

Tradier looks pretty great, especially for options trading. Anyone else have experience with Tradier?

does tradier charge commission? For the people suggesting IB, you must be rich, I tried to start with IB but they will charge you on every step of the way.

Just as many here, after reviewing this platform for some time, I don’t find it able to compete with even a calculator. Reason being that both have the same customer support, and the calculator would not lose you money, but Alpaca is just not trustworthy, their data does not represent reality and it’s simply unbelievably slow.

On the upside, the API is OK.

I’m considering ditching bracket order calls and building the logic in the bot to just post a market buy or sell if my stop or take profit thresholds triggers hit. Not ideal.

I have asked Aplaca to confirm if my integration pattern is correct, but after reading this, seems like we all have similar issues. Specifically with all order types, other than a maker buy or sell.

From consuming the stream, processing the signal and placing the order it takes around 50 seconds. I’m also considering an alternative to the web socket stream. I did a relatively quick POC for consuming a web hook alert from Trade View, and I can be alerted a lot faster when a signal hits. I need to think this through from a scalability perspective though.

@l.kerrigan87 You may find a good approach is to submit One-Triggers-Other (OTO) orders with a take profit limit order as the second leg. As you mentioned, have your algo watch for some stop loss or take profit triggers. If they trigger, then update the limit price.

You stated “From consuming the stream, processing the signal and placing the order it takes around 50 seconds”. That shouldn’t take that long. As an example here is my current algo, I don’t stream data but simply use the APIs to fetch data every minute, then update the limit prices if needed. It generally takes less than a second.

def update_limit_prices():
  # get current positions, orders, and minute bar data
  positions = get_positions_df()
  
  if not positions.empty:
    orders = get_open_limit_orders()

    if not orders.empty:
      minute_bars = get_current_market_bars(orders.index, lookback='7Min')

      if not minute_bars.empty:

        # there are positions and open limit orders (we assume they match)
        # add columns for values we will need
        orders['current_price'] = np.around(positions.current_price.astype('float'), decimals=2)
        orders['entry_price'] = positions.avg_entry_price.astype('float')
        orders['limit_price'] = orders.limit_price.astype('float')

        orders['long_take_profit_price'] = np.around(orders.entry_price * (1 + TAKE_PROFIT_PCT), decimals=2)
        orders['short_take_profit_price'] = np.around(orders.entry_price * (1 - TAKE_PROFIT_PCT), decimals=2)
        
        orders['long_stop_price'] = np.around(orders.entry_price * (1 - STOP_LOSS_PCT), decimals=2)
        orders['short_stop_price'] = np.around(orders.entry_price * (1 + STOP_LOSS_PCT), decimals=2)
      
        # calculate a 'smoothed price'
        orders['mean_vwap'] = minute_bars.groupby('symbol').vwap.mean()

        # set limit to current price if smoothed price < stop price for lomgs. Opposite for a short position.
        orders['long_limit'] = orders.current_price.where(orders.mean_vwap < orders.long_stop_price, orders.long_take_profit_price)
        orders['short_limit'] = orders.current_price.where(orders.mean_vwap > orders.short_stop_price, orders.short_take_profit_price)
  
        # set new_limit price based upon sell or buy (ie long or short)
        orders['new_limit'] = orders.long_limit.where(orders.side=="sell", orders.short_limit)

        # replace any orders where target_limit doesn't equal actual limit
        orders_to_replace = orders.query('limit_price != new_limit')

        for order in orders_to_replace.itertuples():
            replace_order(order.id, limit_price=order.new_limit)
            
  return

Updating the limit prices for 100 orders (not all may need updating) typically takes about 0.6 seconds but goes up to about 1 second if there are a lot of orders to update. Note the spike at the end of the day. There is often about 50 open orders left and the processing time spikes to about 2.5 seconds to close all of them. Below is the log of the times to run for a typical day (I run as a Google cloud function and these are their metrics). Times are US Central Time.

Hi @Dan_Whitnable_Alpaca , thanks for this. I greatly improved the transactional speed so thanks for your benchmark. I am now looking to implement your suggestion for OCO and OTO orders.

Using the SDK, which request class should we call? Do we just call the LimitOrderRequest, and set the order_class variable to OCO?

from alpaca.trading.requests import MarketOrderRequest, LimitOrderRequest, StopOrderRequest, TrailingStopOrderRequest