In our last finance with Python tutorial, we created and tested our basic strategy based on fundamental company data. The only problem is that we currently only buy, but never exit companies. We need to build this in!
Revisiting the concept of our strategy, the plan is:
Buy up to 10 companies that have a PE ratio less than 11 (and PB ratio less than 2). Hold these, until the PE ratio gets above 14, or until the PB ratio gets above 2.
The way we can handle for this is to check to see if the companies we have positions with are also in our "universe," meaning that they are under 14 PE and under 2 PB. If not, then we want to go ahead and sell these companies off.
To do this, we're going to bring in a new functionality with Quantopian, called schedule_function
, which does exactly what it sounds like it does: Lets us schedule a function!
The way it works is you slap that method in your initialize
method, giving the name of the function you wish to schedule, giving it a date_rule and a time_rule. The date rule will be how frequently you want it to run. You have choices like daily, weekly, monthly, and so on. The time rule lets you govern at what time of the day you want this scheduled function to run, things like before market open, at market open, at market close, and so on.
For example, here's our new initialize
method:
def initialize(context): context.limit = 10 schedule_function(rebalance, date_rule = date_rules.every_day(), time_rule = time_rules.market_open())
In this case, we call schedule_function
to run a function called rebalance
, which will run every day, at market open.
Great, time to make our rebalance
function!
def rebalance(context, data): for stock in context.portfolio.positions: if stock not in context.fundamentals and stock in data: order_target_percent(stock, 0)
Here, we pass both context and data. Remember which does what? Context tracks your trading algorithm's state. Data tracks your universe.
In our rebalance
function, what we do is iterate through all of the stocks that are in our portfolio currently. Then, we just ask whether or not those stocks are in context.fundamentals
. If the stock isn't in our context.fundamentals
(which means it is one of the top 10 companies by market cap with below 14 PE and below 2 PB), yet we have this company in our portfolio, we want to send it to the chopping block! If this is the case, we sell the company. To do this, we use a new selling method, order_target_percent
. With this method, we can set a "target" percentage of our portfolio to be allocated to the asset we pass. Careful, you can easily leverage yourself with this method. If you buy 6 companies with a target percent of 20% of your portfolio, you'll become over-invested, or leveraged.
With these changes, the full code:
def initialize(context): context.limit = 10 schedule_function(rebalance, date_rule = date_rules.every_day(), time_rule = time_rules.market_open()) def rebalance(context, data): for stock in context.portfolio.positions: if stock not in context.fundamentals and stock in data: order_target_percent(stock, 0) # Will be called on every trade event for the securities you specify. def before_trading_start(context): context.fundamentals = get_fundamentals( query( fundamentals.valuation_ratios.pb_ratio, fundamentals.valuation_ratios.pe_ratio, ) .filter( fundamentals.valuation_ratios.pe_ratio < 14 ) .filter( fundamentals.valuation_ratios.pb_ratio < 2 ) .order_by( fundamentals.valuation.market_cap.desc() ) .limit(context.limit) ) update_universe(context.fundamentals.columns.values) def handle_data(context, data): cash = context.portfolio.cash current_positions = context.portfolio.positions for stock in data: current_position = context.portfolio.positions[stock].amount stock_price = data[stock].price plausible_investment = cash / context.limit share_amount = int(plausible_investment / stock_price) try: if stock_price < plausible_investment: if current_position == 0: if context.fundamentals[stock]['pe_ratio'] < 11: order(stock, share_amount) except Exception as e: print(str(e))
The results, depending on the dates you use:
Still nothing to write home about or apply to Goldman Sachs with, but better than when we didn't sell at all (previous returns for the same date range were -1.3%)
Now, I know it might be hard to accept, but it might be the case that sometimes we're making mistakes when we buy with our logic. To compensate for this, we can try to use stop-loss in our strategy. Stop-loss is a method used by traders to "cut their losses" at a certain point. Say you bought a company for $100, expecting it to go to $125. Instead, it just keeps dropping. With stop-loss, you can set a limit, say $89. If price falls below $89, then you want to just cut your losses. That's what we'll be covering next.