How do I get this algorithm to make orders yet not just go crazy and over budget?

If I switch to a regular order it will spend infinitely amounts of money, but this way it doesn't seem to spend at all, any advice?

12
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
def initialize(context):
context.stocks = symbols('MSFT','AAPL','GOOG','F')
context.long_leverage = 2

def handle_data(context, data):
record(leverage = context.long_leverage)
prices = history(200,'1d','price').mean()
for stock in context.stocks:
sma = prices[stock]
price = data[stock].price
context.long_weight = context.long_leverage/len(context.stocks)
if price < sma:
order_target_percent(stock, context.long_weight)

There was a runtime error.
11 responses

Moving average strategies are a great way to start, the reason that it doesn't spend at all is because in your code, you forget to calculate an average! I'm sure its just a typo, but the line where you declare sma should be like this sma = prices[stock].mean(). I'd also like to note that when calculating your weight you are using integer division, where you probably want true division. Include from __future__ import division at the top of your script and all division operations will be true division. I've included a backtest with the improved code.

Happy Coding!

22
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
from __future__ import division

def initialize(context):
context.stocks = symbols('MSFT','AAPL','GOOG','F')
context.long_leverage = 2

def handle_data(context, data):
record(leverage = context.long_leverage)
prices = history(200,'1d','price').mean()
for stock in context.stocks:
if stock in data:
sma = prices[stock].mean()
price = data[stock].price
context.long_weight = context.long_leverage / len(context.stocks)
print(context.long_weight)
if price < sma:
order_target_percent(stock, context.long_weight)

There was a runtime error.
Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

One problem is how you calculate the moving average. The history function returns a pandas.DataFrame, which is a table with columns representing securities and rows representing timestamps (objects containing a date and time). Applying the .mean() method produces either a pandas.Series with entries indexed by securities and containing the mean for each security, ie. the 200-day simple moving average, which is what you want; or a pandas.Series with entries indexed by timestamps and containing the average price at that time of the 4 securities you chose. I don't remember how pandas applies its .mean method. You can control this by specifying the axis= keyword argument in your .mean call. Or, to be safer, do what James does and first extract the column representing your security, then calculate its mean, which can be combined into sma = prices[stock].mean(), if you first set prices = history(200,'1d','price').

The second problem is, as James mentioned, integer division. In Python and almost all other languages, 2/4 evaluates to 0. A time-honored hack is to either set one of the two operands to a floating-point value, here context.long_leverage = 2.0, or explicitly convert one, eg. float(len(context.stocks)); then the other operand is implicitly converted to a float and floating-point division applied, yielding 0.5.

Another minor thing is that you set context.long_leverage to a fixed value and never change it. It would be more informative to record your actual leverage: record(context.account.leverage).

I also see that you never sell. Some of your stocks may grow beyond your desired allocation. You might want to add at the end something like

            elif price > sma * 1.05:
order_target_percent(float(context.long_leverage)/len(context.stocks))


There is an error in the code I posted above, I forgot to remove the .mean() from the line that contains the history call. (That's what you get when you copy and paste.) Here is an amended version...

22
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
from __future__ import division

def initialize(context):
context.stocks = symbols('MSFT','AAPL','GOOG','F')
context.long_leverage = 2

def handle_data(context, data):
record(leverage = context.long_leverage)
prices = history(200,'1d','price')
for stock in context.stocks:
if stock in data:
sma = prices[stock].mean()
price = data[stock].price
context.long_weight = context.long_leverage / len(context.stocks)
print(context.long_weight)
if price < sma:
order_target_percent(stock, context.long_weight)

There was a runtime error.

Another little thing is there is no point calculating context.long_weight, which never changes, every minute in lines 14-15 of the latest version. This can be done once in initialize. Moving these lines there should speed things up a bit.

The speed gain would be negligible, Python is pretty good at division already. As Don Knuth says, "premature optimization is the root of all evil."

Another important thing: running an algorithm in minute mode with starting capital as large as \$1,000,000 and without a guard against trading when orders for the same security are outstanding is a recipe for instability. Insert and not get_open_orders(stock) before the : in the if statement.

@James: Learning good habits and making little savings can lead to big savings at little cost later. This is not a major rewrite, just moving two lines outside two loops (the loop that repeats handle_data and the for loop inside it).

Here is the algorithm with both James's suggestions and mine incorporated. When the price of a stock exceeds the 200-day simple moving average, it sells, but only enough shares to bring the value fraction of that stock in your portfolio to the same level as when you buy, ie. context.long_leverage/len(context.stocks), which (with true floating-point division) equals 0.5 here. The remaining shares are considered "winners" and allowed to continue to run, and possibly bring more profit in the future.

Notice another small device I've found useful: record(stock.symbol+"_pos", ...) in line 22, with a series name generated dynamically from the security ticker symbol. With this, you don't have to copy and paste four different record calls, or edit them each time you change your securities.

4
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
from __future__ import division

def initialize(context):
context.stocks = symbols('MSFT','AAPL','GOOG','F')
context.long_leverage = 2.0

def handle_data(context, data):
#record(leverage = context.long_leverage)
prices = history(200,'1d','price')#.mean()
for stock in context.stocks:
if stock in data and not get_open_orders(stock):
sma = prices[stock].mean()
price = data[stock].price
context.long_weight = context.long_leverage / len(context.stocks)
print("weight: {}".format(context.long_weight))
print("{} sma: {}".format(stock.symbol, sma))
#small))
if price < sma:
order_target_percent(stock, context.long_weight)
elif price > sma * 1.05:
order_target_percent(stock, context.long_weight)
record(stock.symbol+"_pos", context.portfolio.positions    [stock].amount*context.portfolio.positions[stock].last_sale_price)
record(leverage=context.account.leverage)              
There was a runtime error.

This version, when it sells a stock, sells all its shares.

Also, you never have more than three stocks, so much of your available capital is unused.

Finally, if you look at the list of trades, you will notice that it buys or sells almost every minute. (Quantopian does not include trades in a posted backtest. Clone and run it.) You would probably want it to buy once and hold until it's time to sell.

4
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
from __future__ import division

def initialize(context):
context.stocks = symbols('MSFT','AAPL','GOOG','F')
context.long_leverage = 2.0
context.long_weight = context.long_leverage / len(context.stocks)

def handle_data(context, data):
#record(leverage = context.long_leverage)
prices = history(200,'1d','price')#.mean()
for stock in context.stocks:
if stock in data and not get_open_orders(stock):
sma = prices[stock].mean()
price = data[stock].price
#context.long_weight = context.long_leverage / len(context.stocks)
#print("weight: {}".format(context.long_weight))
#print("{} sma: {}".format(stock.symbol, sma))
#small)) # apologies for autocorrector
if price < sma:
order_target_percent(stock, context.long_weight)
elif price > sma * 1.05:
order_target_percent(stock, context.long_weight*0)
record(stock.symbol+"_pos", context.portfolio.positions    [stock].amount*context.portfolio.positions[stock].last_sale_price)
record(leverage=context.account.leverage)              
There was a runtime error.

to André Pajak ,
Adding "not get_open_orders(stock)" will prevent any selling of any open orders, right? that is why, all stocks are being held with no selling.
Am I wrong?

Jeff

@Jeff - It will prevent any buying or selling as long as there are orders for the same stock that have not been fully filled.