Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Elapsed time, run info and logging variables when backtest is done

Code to show run information at beginning and end of the run plus elapsed time and/or intermittently (optionally).
You can also output your own variables at the end of the run, for example.

Options:
tz -- Your own time zone if you prefer
log_elapsed -- Number of trading days to produce this output (besides at end)
Add log.info() of any of your own variables at the end of run_info()

Example output:

2015-01-02 12:59 run_info:23 INFO 2015-01-02 to 2017-01-20  $1000000  2017-01-22 00:23 US/Eastern  
2015-07-02 12:59 run_info:32 INFO Elapsed 0 hr 21.2 min  
2015-12-31 12:59 run_info:32 INFO Elapsed 0 hr 42.9 min  
2016-07-01 12:59 run_info:32 INFO Elapsed 1 hr  3.7 min  
2016-12-30 12:59 run_info:32 INFO Elapsed 1 hr 24.1 min  
2017-01-20 12:59 run_info:30 INFO 2015-01-02 to 2017-01-20  $1000000  2017-01-22 00:23 US/Eastern  
2017-01-20 12:59 run_info:32 INFO Elapsed 1 hr 45 min  
def initialize(context):  
    schedule_function(run_info, date_rules.every_day(), time_rules.market_close())

def run_info(context, data):  
    import time  
    from datetime import datetime  
    from pytz import timezone    # Python will only do once, makes this portable.  
                                 #   Move to top of algo for better efficiency.  
    c = context  # Brevity is the soul of wit -- Shakespeare [for efficiency, readability]  
    if 'elapsed' not in c:       # Store some variables in context.elapsed  
        date_end  = get_environment('end').date()  
        tz        = 'US/Eastern' # Change to your timezone optionally  
        c.elapsed = {  
            'days'       : 0.0,  
            'date_end'   : date_end,  
            'begin'      : time.time(),  # For run time  
            'log_elapsed': 126,          # Log it every x days, 1yr is 252  
            'timezone'   : tz,  
            'run_str'    : '{} to {}  ${}  {} {}'.format(  
                get_environment('start').date(), date_end,  
                int(c.portfolio.starting_cash),  
                datetime.now(timezone(tz)).strftime("%Y-%m-%d %H:%M"), tz  
            )  
        }  
        log.info(c.elapsed['run_str'])

    c.elapsed['days'] += 1  
    date = get_datetime().date()  
    if not (c.elapsed['days'] % c.elapsed['log_elapsed'] == 0 or date == c.elapsed['date_end']):  
        return  
    if date == c.elapsed['date_end']:  
        log.info(c.elapsed['run_str']) # Run specifics again for copy/paste  
    elapsed = (time.time() - c.elapsed['begin']) / 60  # minutes  
    log.info('Elapsed {} hr {} min'.format(int(elapsed / 60), '%.1f' % (elapsed % 60)))

    # Add your own log.info() output here if you wish  
2 responses

Thanks Blue, that's very nice. I'd suggest not to rename context to c: admittedly it is shorter, but having two names for the same thing can be confusing, and the name "c" does not suggest what "c" is, whereas "context" does. I'd suggest to name your variables descriptively, within reason.

Another for merely elapsed time at the end of the backtest along with wall clock time. List of time zones.

2018-10-04 12:59 at_end:122 INFO Elapsed: 1 hr 3.1 min 2018-10-05 22:29 US/Eastern

import time  
from datetime import datetime as dt  
from pytz     import timezone as tz

def at_end(context, data):  
    if get_datetime().date() != context.end_date:  # only proceed if last day of backtest  
        return

    tzone = 'US/Eastern'  
    elapsed = (time.time() - context.time_start) / 60   # Minutes  
    log.info('Elapsed: {} hr  {} min   {} {}\n'.format(  
        int(elapsed / 60), '%.1f' % (elapsed % 60),  
        dt.now(tz(tzone)).strftime('%Y-%m-%d %H:%M'), tzone))  
    # Elapsed: 1 hr  3.1 min   2018-10-05 22:29 US/Eastern

def initialize(context):  
    context.time_start = time.time()  # seconds since 1970  
    context.end_date   = get_environment('end').date()  
    schedule_function(at_end, date_rules.every_day(), time_rules.market_close())  

Replying to Gili about renaming context to c, I first saw that from a Quantopian representative and had the same reaction but decided to try it out. Quickly became accustomed, faster development and easier reading.