Back to Community
Track Orders

An example that tracks orders / fills; track_orders() should be largely plug-n-play.
Edit: See below for code and notes.

The output is improved since this was first posted. The original ...

2013-07-17 track_orders:107 INFO Sell -86 TSLA now 120.3 target 164.86  
2013-07-26 track_orders:107 INFO Buy 102 STX at 41.05  
2013-07-29 track_orders:107 INFO  Bot 102 STX at 40.3  
2013-07-29 track_orders:107 INFO Buy 53 STX at 40.3  
2013-07-30 track_orders:107 INFO  Bot 53 STX at 41.25  
2013-07-30 track_orders:107 INFO Buy 25 STX at 41.25  
2013-07-31 track_orders:107 INFO  Bot 25 STX at 40.9  
2013-07-31 track_orders:107 INFO Sell -180 STX now 40.9 target 55.75  
2013-08-27 track_orders:107 INFO  Sold -86 TSLA at 167.0  
9 responses

Gary,

Wow, this is awesome. I think a lot of people will find this super useful. I've added a version that tracks stops as well:

Clone Algorithm
95
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 54999666f2a41c73ef283649
There was a runtime error.
Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

Goodness, hopefully this can be moved out of people's algo code and into the quantopian core!

Thanks Seong, interesting thought Simon, suggest in initialize() a log_orders(stuff here) call implemented on Quantopian for as soon as trades happen.

Edit: Removed some content about stops

Last updated 2017-09-30

Updates
- Faster.
- Handles Stop and Limit orders.
- Specify start and/or optional stop dates to hone in on a particular area while not overwhelming the logging window.
- Precedes each line with the minute of the day (more readable than the log line timestamp).
- Option to add order id's on each line.
- Option to add cash on each line.
- Shows partial fills like Bot (meaning bought) 50/63 EDV at 80.50 or Sold -250/-285 XIV at 15.32

About how to run this: Be sure its schedule_function is last, after any others.

def initialize(context, data):  
    [your things]

    for i in range(1, 391):  
        schedule_function(track_orders, date_rules.every_day(), time_rules.market_open(minutes=i))

def track_orders(context, data):  
    '''  Show orders when made and filled.  
           Info: https://www.quantopian.com/posts/track-orders  
    '''  
    c = context  
    if 'trac' not in c:  
        c.t_options = {           # __________    O P T I O N S    __________  
            'log_neg_cash': 1,    # Show cash only when negative.  
            'log_cash'    : 1,    # Show cash values in logging window or not.  
            'log_ids'     : 1,    # Include order id's in logging window or not.  
            'log_unfilled': 1,    # When orders are unfilled. (stop & limit excluded).  
        }    # Move these to initialize() for better efficiency.  
        c.trac = {}  
        c.t_dates  = {  # To not overwhelm the log window, start/stop dates can be entered.  
            'active': 0,  
            'start' : [],   # Start dates, option like ['2007-05-07', '2010-04-26']  
            'stop'  : []    # Stop  dates, option like ['2008-02-13', '2010-11-15']  
        }  
    from pytz import timezone     # Python only does once, makes this portable.  
                                  #   Move to top of algo for better efficiency.  
    # If 'start' or 'stop' lists have something in them, triggers ...  
    if c.t_dates['start'] or c.t_dates['stop']:  
        date = str(get_datetime().date())  
        if   date in c.t_dates['start']:    # See if there's a match to start  
            c.t_dates['active'] = 1  
        elif date in c.t_dates['stop']:     #   ... or to stop  
            c.t_dates['active'] = 0  
    else: c.t_dates['active'] = 1           # Set to active b/c no conditions.  
    if c.t_dates['active'] == 0: return     # Skip if not active.  
    def _minute():   # To preface each line with the minute of the day.  
        bar_dt = get_datetime().astimezone(timezone('US/Eastern'))  
        return (bar_dt.hour * 60) + bar_dt.minute - 570 # (-570 = 9:31a)  
    def _trac(to_log):      # So all logging comes from the same line number,  
        log.info(' {}   {}'.format(str(_minute()).rjust(3), to_log))  # for vertical alignment in the logging window.

    for oid in c.trac.copy():               # Existing known orders  
      o = get_order(oid)  
      if o.dt == o.created: continue        # No chance of fill yet.  
      cash = ''  
      prc  = data.current(o.sid, 'price') if data.can_trade(o.sid) else c.portfolio.positions[o.sid].last_sale_price  
      if (c.t_options['log_neg_cash'] and c.portfolio.cash < 0) or c.t_options['log_cash']:  
        cash = 'cash {}'.format(int(c.portfolio.cash))  
      if o.status == 2:                      # Canceled  
        do = 'Buy' if o.amount > 0 else 'Sell' ; style = ''  
        if o.stop:  
          style = ' stop {}'.format(o.stop)  
          if o.limit: style = ' stop {} limit {}'.format(o.stop, o.limit)  
        elif o.limit: style = ' limit {}'.format(o.limit)  
        _trac('  Canceled {} {} {}{} at {}   {}  {}'.format(do, o.amount,  
           o.sid.symbol, style, prc, cash, o.id[-4:] if c.t_options['log_ids'] else ''))  
        del c.trac[o.id]  
      elif o.filled:                             # Filled at least some.  
        filled = '{}'.format(o.amount)  
        filled_amt = 0  
        if o.status == 1:            # Complete  
          if 0 < c.trac[o.id] < o.amount:  
            filled   = 'all {}/{}'.format(o.filled - c.trac[o.id], o.amount)  
          filled_amt = o.filled  
        else:                                    # c.trac[o.id] value is previously filled total  
          filled_amt = o.filled - c.trac[o.id]   # filled this time, can be 0  
          c.trac[o.id] = o.filled                # save fill value for increments math  
          filled = '{}/{}'.format(filled_amt, o.amount)  
        if filled_amt:  
          now = ' ({})'.format(c.portfolio.positions[o.sid].amount) if c.portfolio.positions[o.sid].amount else ' _'  
          pnl = ''  # for the trade only  
          amt = c.portfolio.positions[o.sid].amount ; style = ''  
          if (amt - o.filled) * o.filled < 0:    # Profit-taking scenario including short-buyback  
            cb = c.portfolio.positions[o.sid].cost_basis  
            if cb:  
              pnl  = -filled_amt * (prc - cb)  
              sign = '+' if pnl > 0 else '-'  
              pnl  = '  ({}{})'.format(sign, '%.0f' % abs(pnl))  
          if o.stop:  
            style = ' stop {}'.format(o.stop)  
            if o.limit: style = ' stop () limit {}'.format(o.stop, o.limit)  
          elif o.limit: style = ' limit {}'.format(o.limit)  
          if o.filled == o.amount: del c.trac[o.id]  
          _trac('   {} {} {}{} at {}{}{}'.format(  
            'Bot' if o.amount > 0 else 'Sold', filled, o.sid.symbol, now,  
            '%.2f' % prc, pnl, style).ljust(52) + '  {}  {}'.format(cash, o.id[-4:] if c.t_options['log_ids'] else ''))  
      elif c.t_options['log_unfilled'] and not (o.stop or o.limit):  
        _trac('      {} {}{} unfilled  {}'.format(o.sid.symbol, o.amount,  
         ' limit' if o.limit else '', o.id[-4:] if c.t_options['log_ids'] else ''))

    oo = get_open_orders().values()  
    if not oo: return                       # Handle new orders  
    cash = ''  
    if (c.t_options['log_neg_cash'] and c.portfolio.cash < 0) or c.t_options['log_cash']:  
      cash = 'cash {}'.format(int(c.portfolio.cash))  
    for oo_list in oo:  
      for o in oo_list:  
        if o.id in c.trac: continue         # Only new orders beyond this point  
        prc = data.current(o.sid, 'price') if data.can_trade(o.sid) else c.portfolio.positions[o.sid].last_sale_price  
        c.trac[o.id] = 0 ; style = ''  
        now  = ' ({})'.format(c.portfolio.positions[o.sid].amount) if c.portfolio.positions[o.sid].amount else ' _'  
        if o.stop:  
          style = ' stop {}'.format(o.stop)  
          if o.limit: style = ' stop {} limit {}'.format(o.stop, o.limit)  
        elif o.limit: style = ' limit {}'.format(o.limit)  
        _trac('{} {} {}{} at {}{}'.format('Buy' if o.amount > 0 else 'Sell',  
          o.amount, o.sid.symbol, now, '%.2f' % prc, style).ljust(52) + '  {}  {}'.format(cash, o.id[-4:] if c.t_options['log_ids'] else ''))  

There appears to be a bug in your code Blue.
Error SyntaxError: unexpected character after line continuation character

for oid in c.trac.copy():
c.trac[o.id]['cb'] = c.portfolio.positions[o.sid].cost_basis \ <<< On this line continuation character
if c.portfolio.positions[o.sid].cost_basis else c.trac[o.id]['cb']

That's a revelation, the forum code adds two spaces at the end of almost every line instead of displaying code in its original form. You can remove the spaces after backslash in your code or copy/paste again, I switched them over to long lines above.

Thanks Blue. Amazing... such a $#!% load of work has gone into that, it is much appreciated!

Check out this post for more explanations from Blue on how track_order works
https://www.quantopian.com/posts/get-open-orders-on-a-sub-minutely-level#59503680de5db60010c41591

First off, let me say how much I appreciate you putting this together.

My question is: how did you find out what the different status numbers meant?

Such as o.status== 1 or o.status==2