Back to Community
Young quant in need of assistance

Hello, I am an aspiring quant (I'm eighteen years old) with limited Python knowledge. I am struggling to make my system do certain things and behave as expected.

Note: My system uses moving average crossovers as buy and sell triggers. Should be pretty simple, but I am having the following problems:

1) Logging the price at which my system buys a stock. (Useful for future debugging I hope.) Also logging (price x number of shares purchased) (does Quantopian call this amount?) would be helpful.
2) Preventing my system from using leverage! Included with this, I would like to log my remaining cash at the end of each transaction.
3) Using the built-in portfolio object instead of the custom dictionary I created. (portfol[])
4) Making my code more user friendly/professional/efficient for future expansion. Also, removing useless code.

If anyone notices any major flaws in my logic/approach/code, please notify me.

Note: I realize that this code is extremely primitive, as I am trying to grasp the basics of Quantopain by constructing a simple algorithm and then expanding from there. Any other general tips are would be much appreciated. Thanks!

def initialize(context):  
    set_symbol_lookup_date('2012-01-01')  
    set_universe(universe.DollarVolumeUniverse(floor_percentile=99.0, ceiling_percentile=100.0))

    context.max_notional = 10000.1  
    context.min_notional = -10000.0  
    context.init_margin=1.00 #Set initial margin requirement  
    context.pct_invested_threshold = 0.95  
def handle_data(context, data):  
    buying_power = context.account.buying_power  
    for stock in data:  
        current_price = data[stock].price  
        moving_avg_20 = data[stock].mavg(20)  
        moving_avg_50 = data[stock].mavg(50)  
        moving_avg_200 = data[stock].mavg(200)

# BUYING:  
        mymoney = context.portfolio.cash  
        if moving_avg_50 > moving_avg_200 and mymoney > current_price and mymoney > 0 and buying_power > 0:  
            cash = context.portfolio.cash  
            context.buyorder_id = order_target_percent(stock, .10)  
            buy_order = get_order(context.buyorder_id)  
            tradeday = data[stock].datetime  
            log.info("Buying %s shares of %s on %s. Cash = %s" % ((buy_order.amount), (stock.symbol), (tradeday.strftime('%m/%d/%y')), (cash)))  
            global portfol  
            portfol = []  
            portfol.append(stock)  
# SELLING:  
        if moving_avg_50 < moving_avg_200 and stock in portfol:  
            order_target(stock, 0)  
            tradeday = data[stock].datetime  
            log.info("Selling %s on %s" % ((stock.symbol), (tradeday.strftime('%m/%d/%y'))))  
            portfol.remove(stock)  
10 responses

Hey Young Quant. Love to see younger people taking part in Quantopian. I am also new and I'm having similar issues with my algos. Any help would be appreciated. Sorry if my English is bad.

Awesome! Glad to see I'm not the only one struggling with these issues.

Hey Young,
I'm new too, 19 years old, so take this with a grain of salt...

1) You can use the record function to track leverage. This will graph your leverage for you " record('Leverage', context.account.leverage)"

2) I think you're over leveraging because for each stock that meets your buying conditions you allocate 10% of portfolio value to it. Often times there are 40 or 50 of these stocks that you end up buying, all with 10% of your portfolio value. This is why your leverage often stays at 4 or 5, giving you those massive performance swings.

Also, Buying power is set to infinity in the backtests, so buying_power > 0 is always true. As to why cash > 0 seems to always be true I'll have to investigate. I'd guess that when it is set, it uses the initial value it was set to each time, without updating. But I'm not sure. I'll get back to this.

It looks like cash isn't updating until the function ends. This is the code I used to test it.

def initialize(context):  
    context.stocks = symbols('SPY', 'TQQQ')  
    #Only here to run once a day for debugging  
    schedule_function(day, date_rules.every_day(), time_rules.market_open(minutes=1), half_days=True)  

def day(context, data):  
    cash = context.portfolio.cash  #The amount of cash  
    print "Initial cash is {cash} ".format(cash = cash)  
    for stock in context.stocks:  
        print "Cash is {cash} before ordering {sec} ".format(sec = stock.symbol, cash = cash)  
        order_target_percent(stock, 1.0)  
        print "Cash is {cash} after ordering {sec} ".format(sec = stock.symbol, cash = cash)  
    print ""  #New line to make it readable  

def handle_data(context, data):  
    pass  

The log output is:

2015-01-05PRINTInitial cash is 1000000.0
2015-01-05PRINTCash is 1000000.0 before ordering SPY
2015-01-05PRINTCash is 1000000.0 after ordering SPY
2015-01-05PRINTCash is 1000000.0 before ordering TQQQ
2015-01-05PRINTCash is 1000000.0 after ordering TQQQ
2015-01-05PRINT
2015-01-06PRINTInitial cash is -1001417.03002
2015-01-06PRINTCash is -1001417.03002 before ordering SPY
2015-01-06PRINTCash is -1001417.03002 after ordering SPY
2015-01-06PRINTCash is -1001417.03002 before ordering TQQQ
2015-01-06PRINTCash is -1001417.03002 after ordering TQQQ

Yes, that's right, all of the variables and positions are updated at the end of handle_data(). They are not updated in the middle of a call. YQ for your other questions.

Logging the price at which my system buys a stock

If you do "data[stock].price" this will give you the price when your order was submitted. T get the full price, take a look at this thread: https://www.quantopian.com/posts/fill-price-of-market-orders. I'd also recommend to use the debugger to understand your code and order placements.

Preventing my system from using leverage

You're on the right track in your code by using "order_target". This will minimize the leverage in your algo. One of the rough edges of this function is it doesn't consider the status of open orders when making it's calculations. It only considers filled orders for the target. I would add this line to handle_data():

if get_open_orders():  
  log.info("There are open orders, exiting")  
   return  

Using the built-in portfolio object instead of the custom dictionary I created

You can use "context.portfolio.positions" https://www.quantopian.com/help#api-portfolio

And in case you haven't seen it yet, take a look at the tutorial videos. Good luck!

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.

Thank you Calvin and Alisa for your replies.

Calvin, the leverage recording works beautifully, thanks.

As far as order_target_percent, shouldn't the system only order 10% worth of the remaining portfolio cash? In that case, I don't understand how the system would go into debt since with every purchase there is less cash available for spending, and the 10% amount for buying a new stock would continue decrease, eventually reaching zero. Alisa says that the system is still incurring debt because the function "doesn't consider the status of open orders." I added her code to fix this, but the algo is still going into debt.

Calvin, your cash testing was very interesting. I recall doing some similar testing and getting the same (seemingly random) negative number after the cash updated. Any ideas as to why the cash is going from 1000000.0 to -1001417.03002? So far I have still been unable to use the cash function properly. Alisa, how do list (or cycle through them in a for loop or something similar) the stocks that are in context.portfolio.positions?

Here is my updated code. I currently have a strange system of logging to avoid the limits that Quantopian has. If anyone has a better way, please tell me. Anyways, on line 44, I attempt to log the price at which the stock was bought, but my log outputs show 1000000.0 for every purchased stock.

def initialize(context):  
    set_symbol_lookup_date('2012-01-01')  
    set_universe(universe.DollarVolumeUniverse(floor_percentile=99.0, ceiling_percentile=100.0))

    context.max_notional = 10000.1  
    context.min_notional = -10000.0  
    context.init_margin=1.00 #Set initial margin requirement  
    context.pct_invested_threshold = 0.95

def handle_data(context, data):  
    if get_open_orders():  
        log.info("There are open orders, exiting")  
        return  
    record('Leverage', context.account.leverage)  
    global portfol  
    portfol = []  
    #portfol = context.portfolio.positions  
    global buy_stock_list  
    stock_buy_list = []  
    global stock_buy_list_2  
    stock_buy_list_2 = []  
    buying_power = context.account.buying_power  
    for stock in data:  
        current_price = data[stock].price  
        moving_avg_20 = data[stock].mavg(20)  
        moving_avg_50 = data[stock].mavg(50)  
        moving_avg_200 = data[stock].mavg(200)

# BUYING:  
        mymoney = context.portfolio.cash  
        if moving_avg_50 > moving_avg_200 and mymoney > current_price and mymoney > 0 and buying_power > 0:  
            cash = context.portfolio.cash  
            context.buyorder_id = order_target_percent(stock, .10)  
            buy_order = get_order(context.buyorder_id)  
            tradeday = data[stock].datetime  
            if len(stock_buy_list) < 10:  
                stock_buy_list.append(str("Buying %s shares of %s on %s. Cash = %s" % ((buy_order.amount), (stock.symbol), (tradeday.strftime('%m/%d/%y')), (cash))))  
            else:  
                stock_buy_list_2.append(str("Buying %s shares of %s on %s for %s. Cash = %s" % ((buy_order.amount), (stock.symbol), (tradeday.strftime('%m/%d/%y')), (cash), (data[stock].price))))  
            portfol.append(stock)  
            #portfol.update(stock)

# SELLING:  
        if moving_avg_50 < moving_avg_200 and stock in portfol:  
            order_target(stock, 0)  
            tradeday = data[stock].datetime  
            log.info("Selling %s on %s" % ((stock.symbol), (tradeday.strftime('%m/%d/%y'))))  
            portfol.remove(stock)  
            log.info(portfol)  
    if stock_buy_list != []:  
        log.info(stock_buy_list)  
    if stock_buy_list_2 != []:  
        log.info(stock_buy_list_2)  

Thanks again for the responses thus far!

Young Q., Give this a try. The key is to use determine new entries and then combine them with existing positions. Then rebalance using the combined total.

Clone Algorithm
10
Loading...
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
Leverage = .95

def initialize(context):  
    set_symbol_lookup_date('2012-01-01')  
    set_universe(universe.DollarVolumeUniverse(floor_percentile=99.0, ceiling_percentile=100.0))
    
    schedule_function(HandleExits)
    schedule_function(HandleEntries)
    
def handle_data(context, data):  
    positions = context.portfolio.positions
    record(Leverage      = context.account.leverage)
    record(OpenPositions = sum([1 for sid in positions if positions[sid].amount > 0]))
    
def HandleEntries(context, data):  
    positions = context.portfolio.positions
    openLongPositions = [sid for sid in positions if positions[sid].amount > 0]
    
    eligible = []
    for stock in data:  
        current_price  = data[stock].price  
        moving_avg_20  = data[stock].mavg(20)  
        moving_avg_50  = data[stock].mavg(50)  
        moving_avg_200 = data[stock].mavg(200)

        if moving_avg_50 > moving_avg_200 and moving_avg_20 > current_price:
            if (stock not in openLongPositions and not get_open_orders(stock)):
                eligible.append(stock)

    eligible = eligible + openLongPositions
    eligibleCount = float(len(eligible))
    for stock in eligible:
        order_target_percent(stock, (1.0 / eligibleCount) * Leverage)  
       
def HandleExits(context, data):  
    for stock in data:  
        moving_avg_50 = data[stock].mavg(50)  
        moving_avg_200 = data[stock].mavg(200)
        if (moving_avg_50 < moving_avg_200):  
            order_target_percent(stock, 0.0)


There was a runtime error.

@Alisa, come on, get your guys to bring back the "record()" legend, would ya?

Thanks Market Tech for the useful code. I am wondering, however, why backtesting your algo is much slower than backtesting my algos. Is there a way to increase efficiency? Also, what would you or anyone else recommend for logging the date, amount, and ticker for every transaction without hitting the logging limit?

Yeah the slowness of Market Tech's code was weird. Yes I agree. Code for logging with the post above would be helpful