First off... in order to base the sells upon some data in the pipeline, then the securities need to be in the pipeline. Eliminate the screen from the pipe. You don't want to filter out data for stocks you currently hold. Second, make a factor (I called it 'buys') that returns true for any stocks you want to buy (this was the initial pipe screen). That way one can simply filter on this factor to get a list of stocks to buy. Something like this:
"these are the stocks we want to buy"
buys = mktcap.top(20, mask=is_tradeable)
return Pipeline(
columns={
'is_tradeable': is_tradeable,
'under_100': under_100,
'over_5': over_5,
'the_cross': the_cross,
'high_dollar_volume': high_dollar_volume,
'buys': buys,
'sma_5': sma_5,
'sma_48': sma_48,
'mktcap': mktcap,
},
# don't add a screen here. we want to lso return data for any stocks we currently hold
# screen=mktcap.top(20, mask=is_tradeable)
)
Now, simply get the pipe data output. Create two lists of stocks, the buys and the sells, using any logic one wishes. As an example:
context.output = pipeline_output('my_pipeline')
# Get a list of the stocks we want to buy and any we want to sell
# Sort by any pipeline column one wishes.
# Note that there may be less than TARGET_STOCKS returned by the pipe
# so the total stocks may not always be equal to TARGET_STOCKS
context.buys = (context.output.
query('buys == True').
sort_values('mktcap').
head(TARGET_STOCKS - len(context.portfolio.positions)).
index.tolist())
context.sells = context.output.query('sma_5 < sma_48 and buys == False').index.tolist()
Notice this also limits the length of the number of buys to just the difference between what is held and some target value (in this case 20). One will therefore never hold more than this target value.
Maybe that will give you some ideas on how to proceed. I've made these changes to the attached algorithm.
Good luck.