In this Python with Finance tutorial, we're going to be building on the last tutorial to actually add the trading logic to our algorithm. So far we have the following code:
def initialize(context): context.security = symbol('SPY') def handle_data(context, data): MA1 = data[context.security].mavg(50) MA2 = data[context.security].mavg(200) current_price = data[context.security].price current_positions = context.portfolio.positions[symbol('SPY')].amount cash = context.portfolio.cash
Our initialize
method is setting our stock universe to the SPY (S&P 500 index ETF), and that's all. Next, our handle_data
method is defining our moving average information first, then we are collecting information on the stock's current price, our current positions in the stock, and then finally how much cash we currently have for investing.
Now that we have all of this necessary information, we're ready to build actual trading logic that will trade the crossover, only if we do not already currently have that position in place, and if we can actually afford a position in that company.
Notice all the "ifs" there? Sounds like a task for the Python if statement.
To start:
if (MA1 > MA2) and current_positions == 0: number_of_shares = int(cash/current_price) order(context.security, number_of_shares) log.info("Buying shares")
The above block of code establishes our logic to purchase shares. The new method here is order
. This is the most basic form of placing orders with Quantopian. We are passing two parameters here, the first is what security are we actually ordering here, and the second is how many. If you made this a negative number, then we'd either sell shares if we had some, or we would engage in a short. There are many other ordering mechanisms built into Quantopian, like ordering a percent of a portfolio, or ordering a dollar amount of shares, and so on. We will be covering those as time goes on.
Now that we have the buying block create, we need to create the selling block:
elif (MA1 < MA2) and current_positions != 0: order_target(context.security, 0) log.info("Selling shares")
Here, we see a new form of ordering called order_target
. This allows us to order a "target" amount of shares of a company. In this case, we made our target 0, which means if we were shorting the company, we'd exit those shorts. If we had positions in the company, which we would in this case, we sell all of the positions.
Finally, the last line we'll put in this handle_data
method is:
record(MA1 = MA1, MA2 = MA2, Price= current_price)
The record
method allows us to track up to 5 custom attributes to our algorithm. In this case, we track both moving averages and the price. Where you see MA1 = MA1, what is happening here is first we give the label as MA1, and then we say what this MA1 label is representing, which is our MA1 variable. Now, we're ready to run out code. Go ahead and click "run full back test."
Full code up to now should be:
def initialize(context): context.security = symbol('SPY') def handle_data(context, data): MA1 = data[context.security].mavg(50) MA2 = data[context.security].mavg(200) current_price = data[context.security].price current_positions = context.portfolio.positions[symbol('SPY')].amount cash = context.portfolio.cash if (MA1 > MA2) and current_positions == 0: number_of_shares = int(cash/current_price) order(context.security, number_of_shares) log.info("Buying shares") elif (MA1 < MA2) and current_positions != 0: order_target(context.security, 0) log.info("Selling shares") record(MA1 = MA1, MA2 = MA2, Price= current_price)
The results should look something like:
Here, we can see the results of our basic strategy. We didn't beat the market, but we made some money. We can see all of the places where we placed trades in the bottom chart. We also can see the custom data that we traced in the 2nd row graph. You can click on the transaction details tab to see all of the trades you placed exactly, and you can click on the daily positions and gains tab to see a table of your positions each day and your performance.