Back to Community
Selling stock after time

Can anyone tell me how to sell a stock exactly 10 trading days after it was bought?

specific to my code?

Already, ive tried it with a daycount variable in handle data, but it isn't working

Clone Algorithm
11
Loading...
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
"""
This is a template algorithm on Quantopian for you to adapt and fill in.
"""
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.factors import AverageDollarVolume, CustomFactor
from quantopian.pipeline.filters.morningstar import Q1500US

import numpy as np

 



   
def initialize(context):  
    schedule_function(my_rebalance, date_rules.every_day(), time_rules.market_open(hours=1)) 
    context.daycount=0
   
    
    attach_pipeline(make_pipeline(), 'my_pipeline')
    
    # Don't need the following - it's already done in previous line
    #   pipe = Pipeline()
  

    context.aapl = sid(24)
    context.spy = sid(8554)
    context.googl = sid(46631)
    context.agn = sid(205)
    context.infn = sid(33979) 
    
    context.startcash=context.portfolio.cash
    


def handle_data(context,data):  
    context.daycount+=1
    # Generally use the schedule function to schedule your trading
    # Don't put that logic in 'handle_data' since that get's executed EVERY minute
    # Only IF you want to trade every minute use 'handle_data'
    # Moved the code that was here to 'my_rebalance'
    
    pass
  
    
def make_pipeline():
    """
    A function to create our dynamic stock selector (pipeline). Documentation on
    pipeline can be found here: https://www.quantopian.com/help#pipeline-title
    """
    
    # Base universe set to the Q500US
    base_universe = Q1500US()

    # Factor of yesterday's close price.
    yesterday_close = USEquityPricing.close.latest
    
    # Custom factors for the max and min of previous prices (default is past 3 days)
    min_close = MinClose(mask=base_universe)
    max_close = MaxClose(mask=base_universe)
     
    pipe = Pipeline(
        screen = base_universe,
        columns = {
            'close': yesterday_close,
            'min_close': min_close,
            'max_close': max_close,
        }
    )
    return pipe
 
    
def before_trading_start(context, data):
    """
    Called every day before market open.
    """
    # I MAY NEED THIS 
    # Yes, you need this. This get's the data you defined in the pipeline definition
    # and put's it into a dataframe called 'context.output'
    # You can then use that data anywhere else in your code
    context.output = pipeline_output('my_pipeline')
  
    # These are the securities that we are interested in trading each day.
    # COULD NEED THIS 
    # You are right again, you may need this depending upon your logic
    # The list of securities returned in the pipeline is just the index
    # so you can always get the list that way. Explcitly making a list like this 
    # may make it easier?
    context.security_list = context.output.index
    
    # Dont run the pipeline again. It doesn't hurt. It's just not necessary.
    # so delete this line .... results = pipeline_output('my_pipeline')
    
    # You can do a print to help debug but probably remove at some time
    
        
def my_assign_weights(context, data):
    """
    Assign weights to securities that we want to order.
    """
    pass
 
    
def my_rebalance(context,data):
    """
    Execute orders according to our schedule_function() timing. 
    """
   
    # If you want to use the following code for all the stocks in Q500US instead of this fixed list of stocks
    # use context.security_list. That is where you put the list of all astocks returned by the pipeline
        # stocks = symbols('AAPL','SPY','AAPL','GOOG','AGN','INFN','prx','m', 'a','aa','aac','aan','aat', 'aap','aav','ab','abb','l','lad','ladr')
    
    openorders=get_open_orders()
    stocks = context.security_list
    orders=0
    currentcash= 3.*context.portfolio.cash
    
    for stock in stocks: 
        last_close = context.output.get_value(stock, 'close')
        min_close = context.output.get_value (stock, 'min_close')
        max_close = context.output.get_value (stock, 'max_close')
        number_of_open_positions= len(openorders)
        buyprice= context.portfolio.positions[stock].cost_basis        
        curprice=  data.current(stock, 'price')
        # for stuff last_close >= max_close 
        #for stock in stocks:
        #percentgain= and currentcash>50000

        
        
        open_orders = get_open_orders()  
        if data.can_trade(stock) & (stock not in open_orders):  
            #Since parent if-statement will be inside for loop (as stated above)  
            if context.account.leverage > 2.99: 
                if stock in context.portfolio.positions:
                    order_target_percent(stock,0) 
                continue
            buy = currentcash > .05 * context.portfolio.portfolio_value
            if (stock not in context.portfolio.positions):  
                if (buy and last_close < min_close): 
                    currentcash -= context.portfolio.portfolio_value * .05
                    order_target_percent(stock, 0.05)  
                    orders += 1
            elif context.daycount>= 43200: 
                order_target_percent(stock, 0)   
                orders += 1
                context.daycount=0
                
        if orders > 200 : break
        #sell logic (last_close >= max_close or (buyprice!=0 and (buyprice-curprice)/buyprice<=-.05) or buyprice!=0 and (buyprice-curprice)/buyprice>=.5)    
        
    log.debug('Bought ' + str(orders) )
    record(leverage=context.account.leverage) 
    record(numberofstocks=len(context.portfolio.positions)/10)
"""        
    if data.can_trade(stock):
        if (last_close < min_close):
            if get_open_orders():
                return
            else:
                order_target_percent(stock, 0.5)
        elif (last_close >= max_close or (buyprice!=0 and (buyprice-curprice)/buyprice<=-.05) and leverage<2.95): 
                if get_open_orders():
                    return
                else:
                    order_target_percent(stock, 0)
"""
 
def my_record_vars(context, data):
    """
    Plot variables at the end of each day.
    """
    pass

   
# ************* CustomFactors ***********************

class MinClose(CustomFactor):
    '''
    Returns the minimum close price over the past n days EXCLUDING last days price
    '''
    # Define inputs
    inputs = [USEquityPricing.close]
    
    # Set window_length to whatever number of days to lookback.
    # in the case where no window_length is given the default will be 3 days.
    # This can also be set/over-ridden as shown below:
    # my_min_10 = MinClose(window_length = 10)
    
    window_length = 3 
    
    def compute(self, today, assets, out, close):
        # The input 'close' is a Numpy array with close prices as axis 0 (total rows = window length)
        # and axis 1 are all the assets (ie stocks and securities).
        # Here we use the built in Numpy method to return the min in each row. 
        # The nanmin method is used because it excludes nans unlike the standard min
        prices_excluding_last = close[0:-1]
        out[:] = np.nanmin(prices_excluding_last, axis=0)
    
class MaxClose(CustomFactor):
    '''
    Returns the maximum close price over the past n days EXCLUDING last days price
    '''
    # Define inputs
    inputs = [USEquityPricing.close]
    
    # Set window_length to whatever number of days to lookback.
    # in the case where no window_length is given the default will be 3 days.
    # This can also be set/over-ridden as shown below:
    # my_min_10 = MaxClose(window_length = 10)
    
    window_length = 3 
    
    def compute(self, today, assets, out, close):
        # The input 'close' is a Numpy array with close prices as axis 0 (total rows = window length)
        # and axis 1 are all the assets (ie stocks and securities).
        # Here we use the built in Numpy method to return the max in each row. 
        # The nanmax method is used because it excludes nans unlike the standard max
        prices_excluding_last = close[0:-1]
        out[:] = np.nanmax(prices_excluding_last, axis=0)
    '''x=get_open_orders()
    hist= data.history(stock, 'price', 30, '1d') 
        
    if  hist[-1] < hist[0:-2].min() and data.can_trade(stock) and context.account.leverage<.95:  
            order_target_percent(stock, 0.05)  
    elif hist[-1] >= hist[0:-2].max()  and data.can_trade(stock):
            order_target(stock, 0)'''
    
There was a runtime error.
13 responses

Hi Charles,

the 'handle_data' function runs every minute, so you don't want to use that function to increment a variable designed to keep a count of your holding period daily. what I find to be best for day counts is to add an empty dictionary in the 'initialize' function:

def initialize(context):  
    context.holding_time = {}  

Then, whenever I make a new purchase, I add the stock purchased and 0 days to the dictionary. In the example below, I am iterating through an example buy list, and adding each stock and 0 days of ownership to the dictionary:

# example order

for stock in example_buy_list: # iterate through buy list  
    order(stock, 1) # make order  
    context.holding_time[stock] = 0 # add the stock and 0 days to the dictionary  

From there you can schedule a function to run every morning to both increment the holding period, and liquidate stocks that have hit the amount of days you want to trigger a liquidation:

def check_duration(context, data):  
    for stock in context.holding_time.keys(): # iterate through all stocks in the holding time dictionary  
        context.holding_time[stock] = context.holding_time[stock] + 1 # increment all stocks by 1 day  
    for stock in context.portfolio.positions: # iterate through all stocks in the portfolio  
        if context.holding_time[stock] >= 10: # check if stock has hit 10 days  
            order_target_percent(stock, 0) # liquidate the stock  

Awesome! It works!

Alright, can you help me again?

I've been fiddling with trying to have a brief 3 day hiatus from buying after the sellout, but i can't make it work

any suggestions?

Yea no problem. First add this to your imports:

from datetime import datetime, timedelta  

In the 'initialize' function, add this empty dictionary:

context.Last_Trade_Date = {}  

Then, wherever you have logic to sell out of positions, add the following line after the order:

current_bar = get_datetime('UTC')  
context.Last_Trade_Date[stock] = current_bar  

Then schedule a new function to run once a day with this code:

def Check_Last_Trade_Date(context, data):  
    current_bar = get_datetime('UTC')  
    context.Trade_Ban = {}  
    for stock in context.Last_Trade_Date:  
        if current_bar - timedelta(days=3) < context.Last_Trade_Date[stock]:  
            context.Trade_Ban[stock] = True  
        else:  
            context.Trade_Ban[stock] = False  

Alter the timedelta in the function above to dictate how many days you want the trade ban to last.

Final step is to go to every instance in your algo where you are entering new positions, and use an if statement to make sure the stock is not in the trade ban...psuedo code below:

if context.Trade_Ban[stock] == False:  
    # order stock  
elif context.Trade_Ban[stock] == True:  
    # do not order stock

hmmmm

I implemented it, but when i add the "if context.Trade_Ban[stock] == False: " into the buy logic, i got a key error on Trad_Ban?

*Trade_Ban

  open_orders = get_open_orders()  
        if data.can_trade(stock) & (stock not in open_orders):  
            buy = currentcash > .05 * context.portfolio.portfolio_value  
            if (stock not in context.portfolio.positions):  
                if (buy and last_close < min_close and context.Trade_Ban[stocks] == False):  
                        currentcash -= context.portfolio.portfolio_value * .05  
                        order_target_percent(stock, 0.05)  
                        orders += 1  
                        context.holding_time[stock] = 0  

Oh, stocks that were not previously traded will throw that error. Try to fix with the following:

if stock not in context.Trade_Ban.keys():  
    # order stock  
elif stock in context.Trade_Ban.keys():  
    if context.Trade_Ban[stock] == False:  
        # order stock  
    elif context.Trade_Ban[stock] == True:  
        # do not order stock  

Here is how I changed apposite to your suggestion:

open_orders = get_open_orders()  
        if data.can_trade(stock) & (stock not in open_orders):  
            buy = currentcash > .05 * context.portfolio.portfolio_value  
            if (stock not in context.portfolio.positions):  
                if (buy and last_close < min_close and stock not in context.Trade_Ban.keys()):  
                        currentcash -= context.portfolio.portfolio_value * .05  
                        order_target_percent(stock, 0.05)  
                        orders += 1  
                        context.holding_time[stock] = 0  
                elif stock in context.Trade_Ban.keys():  
                    if context.Trade_Ban[stock] == False:  
                        currentcash -= context.portfolio.portfolio_value * .05  
                        order_target_percent(stock, 0.05)  
                        orders += 1  
                        context.holding_time[stock] = 0  
                    elif context.Trade_Ban[stock] == True:  
                        pass  

I still get a key error on trade ban...

You will just need to find a way to get around the initial edge case where a stock was not previously populated to the Trade_Ban dictionary.

Charles,

Just thought of a better fix...will post in a few hours.

Could use https://www.quantopian.com/posts/waits-simple-per-security-waiting-period-any-number-of-days.
Set waits_max to 11, sell if waits[stock] is 10. The only two lines you need to worry about are marked, in this case would be setting the wait counter for a stock and skipping sell until its time has come.

That method should work as well. One fix is to just populate the context.Trade_Ban dictionary one time in the "before_trading_start" function:

# run this after the context.security_list is obtained  
dummy_count = 0  
if dummy_count == 0:  
    for stock in context.security_list:  
        context.Trade_Ban[stock] = 10 # use some dummy value greater than intended trade ban term  
    dummy_count += 1 # increment to stop this code from running on subsequent days

Kay, Frank

here's what I changed:

def before_trading_start(context, data):  
    """  
    Called every day before market open.  
    """  
    context.output = pipeline_output('my_pipeline')  

    context.security_list = context.output.index  
    # run this after the context.security_list is obtained  
    dummy_count = 0  
    if dummy_count == 0:  
        for stock in context.security_list:  
            context.Trade_Ban[stock] = 10 # use some dummy value greater than intended trade ban term  
            dummy_count += 1 # increment to stop this code from running on subsequent days  

still getting that key error :)