Bracket Order Code Example with alpaca-py Library

I struggled to find adequate documentation on bracket orders with the new and updated alpaca-py library, so I figured I’d post the below example for someone struggling to find an example in the future.

Imports

from alpaca.trading.client import TradingClient
from alpaca.trading.requests import MarketOrderRequest, TakeProfitRequest, StopLossRequest
from alpaca.trading.enums import OrderSide, TimeInForce, QueryOrderStatus, OrderClass

Keys

API_KEY = "<YOUR KEY>"
SECRET_KEY = "<YOUR SECRET>"

Variables

trading_client = TradingClient(API_KEY, SECRET_KEY, paper=True)

Functions

def main():

Request a Bracket market order (calling Bracket market order function)

market_order_data = bracket_order(
        "AAPL", 1, 100, 200, OrderSide.BUY, TimeInForce.GTC
    )

Submit market order

submit_order(market_order_data)

Bracket market order function

def bracket_order(ticker, size, stop, take_profit, order_side, tif):
    stop_loss_request = StopLossRequest(
    stop_price=stop
    )
    take_profit_request = TakeProfitRequest(
    limit_price=take_profit
    )
    market_order_data = MarketOrderRequest(
        symbol=ticker,
        qty=size,
        side=order_side,
        time_in_force=tif,
        order_class = OrderClass.BRACKET,
        stop_loss=stop_loss_request,
        take_profit=take_profit_request
    )
    return market_order_data

Submit an order and print the returned object function

def submit_order(order_data):
    order = trading_client.submit_order(order_data)
    for property_name, value in order:
        print(f'"{property_name}": {value}')

Calling main() when running script directly

if __name__ == "__main__":
    main()

Happy trading!
P

2 Likes

Thank you! Do you think it is possible to have a limit order and when this is filled a market on close order is submitted to exit on the close?

Yes, and if you simply wanted to close everything before close end of week, you could do something like this. To close it each day just remove and next_open() >= 63:

from alpaca.trading.client import TradingClient
from alpaca.trading.requests import (
    MarketOrderRequest,
    StopOrderRequest,
    LimitOrderRequest,
    StopLimitOrderRequest,
    TrailingStopOrderRequest,
    GetOrdersRequest,
    GetOrderByIdRequest,
    ReplaceOrderRequest,
    TakeProfitRequest,
    StopLossRequest,
    ClosePositionRequest,
    GetAssetsRequest,
    GetPortfolioHistoryRequest,
    CancelOrderResponse,
    GetCalendarRequest
)
from alpaca.trading.enums import (
    OrderSide,
    TimeInForce,
    AssetClass,
    QueryOrderStatus,
    OrderClass,
)
from alpaca.data.historical import StockHistoricalDataClient
from datetime import datetime
import time
from pytz import timezone
import sys
import os
tz = timezone("EST")

def main():

def main():

while not trading_client.get_clock().is_open:  # Set to not
        print(f"{datetime.now(tz)} - Market Closed, keep checking")
        time.sleep(60) #60
        continue
    else:
        print(f"{datetime.now(tz)} - Closing any unwanted open orders and positions")
        print(trading_client.close_all_positions(cancel_orders=True))
        time.sleep(5)
        print(f"{datetime.now(tz)} - Executing trades")
        market_order_data = bracket_order("AAPL", 1, 100, 200, OrderSide.BUY, TimeInForce.GTC)
        print(submit_order(market_order_data))
        while trading_client.get_clock().is_open and market_closing() > 12 and next_open() >= 63:
            print(f"{datetime.now(tz)} - Market not closing soon and week not ending")
            time.sleep(60) #60
            continue
        else:
            print(f"{datetime.now(tz)} - Closing all open orders and trades to end the week")
            trading_client.close_all_positions(cancel_orders=True)
            while trading_client.get_clock().is_open:
                print(f"{datetime.now(tz)} - Waiting for market to close for the week to exit program")
                time.sleep(60) #60
                continue
            else:
                sys.exit(f"{datetime.now(tz)} - Exiting to restart program")
# Market order function
def market_order(ticker, size, order_side, tif):
    market_order_data = MarketOrderRequest(
        symbol=ticker,
        qty=size,
        side=order_side,
        time_in_force=tif,
    )
    return market_order_data
# Stop order function
def stop_order(ticker, size, order_side, tif, stop):
    stop_order_data = StopOrderRequest(
        symbol=ticker, qty=size, side=order_side, time_in_force=tif, stop_price=stop
    )
    return stop_order_data
# Submit an order and print the returned object function
def submit_order(order_data):
    order = trading_client.submit_order(order_data)
    for property_name, value in order:
        print(f'"{property_name}": {value}')
# Cancel_all open orders and print each of them function
def cancel_all():
    positions = trading_client.cancel_orders()
    for position in positions:
        for property_name, value in position:
            print(f'"{property_name}": {value}')
def market_closing():
    est = datetime.now(tz).timestamp()
    market_close = trading_client.get_clock().next_close.timestamp()
    time_to_close = int((market_close - est) / 60)  # Minutes to next close
    return time_to_close
    


def next_open():
    market_close = trading_client.get_clock().next_close.timestamp()
    market_open = trading_client.get_clock().next_open.timestamp()
    time_to_next_open = int((market_open - market_close) / 3600)  # Hours to next open
    return time_to_next_open
if __name__ == "__main__":
    main()
1 Like

Wow thank you! I am hoping they could include some trigger mechanism in the future like IBKR has it in their order logic.

The documentation is faulty and not updated. Wondering if I should put more money into Alpaca.

Yes, the docs on the website are outdated, and they do have updated docs, it’s just not full of context or examples. I’ve been successful using this: Alpaca-py

I plugged this into PyCharm, and it still says its incomplete. please if you can explain why bracket orders need to be separates into the different function and why can’t they just be put into one function like a market order. for original post. I’m really trying and struggling to understand bracket orders, so any help is very appreciated.

@AASAAQWE The alpaca-py SDK can be a bit daunting at first with all the imports it requires, but I can perhaps help. Specifically, you asked

why bracket orders need to be separated into the different functions and why can’t they just be put into one function like a market order?

All it takes to submit a bracket order is a single statement. One doesn’t need to wrap it in a function unless it makes sense in your logic to do it that way. The entry (or parent) order can either be a regular market order or a limit order. To turn it into a bracket order (which is really a group of three separate orders) simply add the following to the order request

  order_class = OrderClass.BRACKET,
  stop_loss=StopLossRequest(stop_price=300.00),
  take_profit=TakeProfitRequest(limit_price=400.00)

That specifies 1) that you want a BRACKET order and 2) the associated stop loss order and 3) the associated take profit limit order. You don’t need to specify the time in force, or the side, the symbol, or the qty for the ‘leg’ orders. Those values are copied from the original parent order.

So the coplete code to submit a bracket order (buying 1 share of SPY) would look like this

# install the alpaca-py SDK if needed
!pip install -q alpaca-py

from alpaca.trading.client import TradingClient
from alpaca.trading.requests import MarketOrderRequest, LimitOrderRequest, StopLossRequest, TakeProfitRequest
from alpaca.trading.enums import OrderSide, OrderClass, TimeInForce

ALPACA_API_KEY = 'xxxxx'
ALPACA_API_SECRET_KEY = 'xxxxx'

client = TradingClient(api_key=ALPACA_API_KEY, secret_key=ALPACA_API_SECRET_KEY)
 
# Bracket order with market order as initial 'parent' order
market_bracket_order = client.submit_order(MarketOrderRequest(
          symbol='SPY',
          qty=1,
          side=OrderSide.BUY,
          limit_price=350.00,
          time_in_force=TimeInForce.DAY,
          order_class = OrderClass.BRACKET,
          stop_loss=StopLossRequest(stop_price=300.00),
          take_profit=TakeProfitRequest(limit_price=500.00)
          ))
# Bracket order with limit order as initial order
limit_bracket_order = client.submit_order(LimitOrderRequest(
          symbol='SPY',
          qty=1,
          side=OrderSide.BUY,
          limit_price=350.00,
          time_in_force=TimeInForce.DAY,
          order_class = OrderClass.BRACKET,
          stop_loss=StopLossRequest(stop_price=300.00),
          take_profit=TakeProfitRequest(limit_price=500.00)
          ))

Of course a functioning algo would have more code than this, and it is good practice to put the submit_order methods in a try-except statement (in case it fails for some reason) but this is basically all that’s needed.

Excellent answer Dan! What to do with limit_bracket_order but with a trailing stop loss and/or take-profit? Is stop_loss and take_profit parameter compatible with any request type, eg trailing requests as well?
Thank you @Dan_Whitnable_Alpaca