Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
How to Code percent profit target and dates.

How would I close a position when either a x percent profit target is hit or x number of days goes by, whichever comes first?

1 response

First off, you will need to capture two pieces of data - the cost basis of your positions, and the number of days a position is held.

The cost basis is stored in context.portfolio.positions[stock].cost_basis (where 'stock' is an equity object in your current holdings). Retrieve this value each day. DO NOT store it between days. Otherwise the value won't be split adjusted.

The days held isn't stored any place and needs to be generated in your code. This could potentially be done by saving the order date and using date calculations. Don't go there. Keeping track of weekends and holidays etc is awful. The easiest is to simply increment a 'days_held' value each day in the 'before_trading_start' method. This method conveniently only runs on trading days and therefore is a good place to count just the trading days held.

I like storing all the equity related data in a single dataframe. First get any desired historical data using a pipeline. Then, simply add rows for any held securities not already there, and add columns for any other data you wish to track. In this case, I'd add columns for 'cost_basis' and 'days_held'.

Assuming you already have set up your pipeline definition, The following would add those rows and columns.

def before_trading_start(context, data):  
    '''  
    Run pipeline_output to get the latest data for each security.  
    The data is returned in a 2D pandas dataframe. Rows are the security objects.  
    Columns are what was defined in the pipeline definition.  
    '''  
    # Get the stocks we want to consider and any associated data  
    output = pipeline_output('my_pipe')  


    # Add other columns to the dataframe for storing qty of shares held, etc  
    output = output.assign(  
        have_position = False,  
        held_share_cost = 0,  
        days_held = 0,  
        )

    # Add data for stocks which are in portfolio  
    for stock in context.portfolio.positions:  
        # The .set_value method adds the stock to the dataframe if not already there  
        output.set_value(stock, 'held_share_cost', context.portfolio.positions[stock].cost_basis)  
        output.set_value(stock, 'days_held', context.output.get_value(stock, 'days_held') + 1)  


    # Overwrite the old dataframe with the new one  
    context.output = output

Then, assuming you have some logic to open orders, you would close those positions after either a fixed number of days or after some target gain. Maybe something like this.

def close_orders(context, data):  
    '''  
    Close anything held longer than MAX_HOLD_DAYS and try to sell everything else at a profit  
    '''  
    # Place an order to close everything held more than MAX_HOLD_DAYS  
    stocks_to_close = (context.output.  
                      query('days_held >= @MAX_HOLD_DAYS').  
                      index.tolist())

    for stock in stocks_to_close:  
        if data.can_trade(stock):  
            order_target(stock, 0)

    # Try to sell everything else at a profit  
    for stock in context.portfolio.positions:  
        if (stock not in stocks_to_close) and data.can_trade(stock):  
            # This is a long only algo so increase the target price by the target gain  
            purchase_price = context.output.get_value(stock, 'held_share_cost')  
            target_sell_price = purchase_price * (1 + TARGET_GAIN)  
            order_target(stock, 0, style=LimitOrder(target_sell_price))  

Attached is an algorithm with some simple open rules with this in action. It gives an idea how to implement "either a x percent profit target is hit or x number of days goes by, whichever comes first". Note that it uses limit orders to capture a target gain. It could also be modified to simply sell anytime after that target was reached. Just depends upon the desired effect.