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?
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?
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.