Back to Community
Personalized Portfolio of Investments in Electronic Trading

Investing has increasingly moved from the exchange floor to complex algorithms and electronic trading platforms. Electronic trading represents more than half of the $5.3 trillion-a-day foreign-exchange market, according to a Euromoney Institutional Investor Plc survey1, as increasing competition, diminishing trade volumes and the need for low-cost execution has driven many financial firms to embrace electronic trading and reap the benefits of lower bid-ask spreads and efficient order.

Rebalancing Period: 6 Months

Credits:
1. https://www.quantopian.com/posts/diversified-portfolio-monthly-rebalance-for-live-trading
2. https://trader.motifinvesting.com/positions/sharpe-optimal--electronic-trad-U_568559#/overview

Clone Algorithm
31
Loading...
Backtest from to with initial capital
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
''' 
Investing has increasingly moved from the exchange floor to complex algorithms and electronic trading         platforms. Electronic trading represents more than half of the $5.3 trillion-a-day foreign-exchange           market, according to a Euromoney Institutional Investor Plc survey1, as increasing competition,               diminishing trade volumes and the need for low-cost execution has driven many financial firms to embrace electronic trading and reap the benefits of lower bid-ask spreads and efficient order.

61%   - MarketAxess Holdings Inc
16%   - BGC Partners, Inc
05%   - Interactive Brokers Group, Inc
12%   - Nasdaq Inc
06%  - CBOE Holdings, Inc

    NOTE: This algo is intended to run in minute-mode simulation and is compatible 
          with LIVE TRADING.
'''

########### IMPORT THE LIBRARIES USED IN THE ALGORITHM ####################################
import datetime
import pytz
import pandas as pd
from zipline.utils.tradingcalendar import get_early_closes

########### INITIALZE() IS RUN ONCE (OR IN LIVE TRADING ONCE EACH DAY BEFORE TRADING) #####
def initialize(context):
    
    # Define the instruments in the portfolio:
    context.MKTX      = symbol('MKTX')
    context.BGCP      = symbol('BGCP')
    context.IBKR      = symbol('IBKR')
    context.NDAQ      = symbol('NDAQ')
    context.CBOE      = symbol('CBOE')
    

    # Define the benchmark (used to get early close dates for reference).
    context.spy           = sid(8554)
    
    start_date = context.spy.security_start_date
    end_date   = context.spy.security_end_date
    
    # Initialize context variables the define rebalance logic:
    context.rebalance_date = None
    context.next_rebalance_Date = None
    context.rebalance_days = 180
    context.rebalance_window_start = 10
    context.rebalance_window_stop  = 15
    
    # Get the dates when the market closes early:
    context.early_closes = get_early_closes(start_date,end_date).date
    
    
########### HANDLE_DATA() IS RUN ONCE PER MINUTE #######################
def handle_data(context, data):
    
    # Get the current exchange time, in local timezone: 
    exchange_time = pd.Timestamp(get_datetime()).tz_convert('US/Eastern')
    
    # If it is rebalance day, rebalance:
    if  context.rebalance_date == None or exchange_time >= context.next_rebalance_date:
        
       # If EOD, cancel all open orders, maintains consistency with live trading.
       cancel_orders_EOD(context)
       
       # If it's 10am proceed with rebalancing, otherwise skip this minute of trading.
       if exchange_time.hour != 10:
        return 
       
       # Check if there are any existing open orders. has_orders() defined below.
       has_orders = has_open_orders(data,context)
        
       # If we are in rebalance window but there are open orders, wait til next minute
       if has_orders == True:
            log.info('Has open orders')
            return
        
       # If there are no open orders we can rebalance.
       elif has_orders == False:
           
           rebalance(context, data, exchange_time) 
           log.info('Rebalanced portfolio to target weights at %s' % exchange_time)

           # Update the current and next rebalance dates
           context.rebalance_date = exchange_time 
           context.next_rebalance_date = context.rebalance_date + \
            datetime.timedelta(days=context.rebalance_days)      
    else:
        return
    
########### CORE REBALANCE LOGIC #########################################
## THIS FUNCTION IS RUN ONLY AT REBALANCE (DAY/TIME) #####################
def rebalance(context,data,exchange_time):
    
    #Rebalance the portfolio to the predetermined target weights:
    #
    #  61%   - MarketAxess Holdings Inc
    #  16%   - BGC Partners, Inc
    #  05%   - Interactive Brokers Group, Inc
    #  12%   - Nasdaq Inc
    #  06%  - CBOE Holdings, Inc
       
    order_target_percent(context.MKTX,0.609)
    order_target_percent(context.BGCP,0.158)
    order_target_percent(context.IBKR,0.052)
    order_target_percent(context.NDAQ,0.122)
    order_target_percent(context.CBOE,0.059)

########### HELPER FUNCTIONS ##############################################
        
## IN LIVE TRADE ALL OPEN ORDERS ARE CANCELLED EOD. HANDLE EXPLICITLY WITH THIS
## HELPER FUNCTION SO THAT ALL OPEN ORDERS ARE ALSO CANCELLED AT EOD IN BACKTEST.
def cancel_orders_EOD(context):

    date = get_datetime().date()
    
    # set the closing hour, based on get_early_closes (assumes that all early closes are at 13)
    if date in context.early_closes:
        close = 13 # early closing time
    else:
        close = 16 # normal closing time
    
    loc_dt = pd.Timestamp(get_datetime()).tz_convert('US/Eastern')
    
    # if it is EOD, find open orders and cancel them, otherwise return
    if loc_dt.hour == close and loc_dt.minute == 0:
        pass
    else:
        return
    
    all_open_orders = get_open_orders()
    if all_open_orders:
        for security, oo_for_sid in all_open_orders.iteritems():
            for order_obj in oo_for_sid:
                log.info("%s: Cancelling order for %s of %s created on %s" % 
                         (get_datetime(), order_obj.amount,
                          security.symbol, order_obj.created))
                cancel_order(order_obj)    
                
## RETURNS TRUE IF THERE ARE PENDING OPEN ORDERS, OTHERWISE RETURNS FALSE
def has_open_orders(data,context):               
# Only rebalance when we have zero pending orders.
    has_orders = False
    for stk in data:
        orders = get_open_orders(stk)
        if orders:
            for oo in orders:                  
                message = 'Open order for {amount} shares in {stock}'  
                message = message.format(amount=oo.amount, stock=stk)  
            has_orders = True
    return has_orders           
    if has_orders:
        return  
There was a runtime error.
5 responses

Nice code but why would you want to bet on the future success of these companies? Isn't it said that the minimum number of companies you need in a portfolio for diversification is 20?

Nice to adapt or as on/off switch approach between bonds and equities though. Needs updating - contains a deprecated feature.

Thank you Anthony for the note. I agree to an extent that in order to achieve balanced portfolio, one must include more assets than there are in the above portfolio. In the regards, I aim to consider it as a sub portfolio of a larger portfolio which certainly would have assets more than 20. With that said, I also believe the optimal number of assets in an portfolio is an subjective matter that also depends on the targeted growth one is seeking for.

I will certainly explore more about the on/off switch approach and update my findings in the post. Also, thank you for pointing out about a deprecated, I will update the code.

Thanks. The code is super helpful. I have got very lazy about Q since I am working on other stuff. I'll post my switching algo tomorrow.

Agree about subjectivity. I guess I was seeing these stock picks as a fundamental choice rather than algorithmic. Nothing wrong with that! I just don't have a clue on the fundamentals.

A switching routine between equities and bonds cannibalising your code and others, Can be expected to under-perform in a bull equity market and outperform in a bear equity market. In view of current interest rate levels it might be better to stay at the short end of the maturity curve and use a 1 to 5 year bond ETF rather than 20.

Clone Algorithm
47
Loading...
Backtest from to with initial capital
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

########### IMPORT THE LIBRARIES USED IN THE ALGORITHM ####################################
import datetime
import pytz
import pandas as pd
from zipline.utils.tradingcalendar import get_early_closes
import talib

########### INITIALZE() IS RUN ONCE (OR IN LIVE TRADING ONCE EACH DAY BEFORE TRADING) #####
def initialize(context):
    
    # Define the instruments in the portfolio:
    context.SPY      = symbol('SPY')
    context.TLT      = symbol('TLT')
    context.IEF      = symbol('IEF')
    context.IWV      = symbol('IWV')
    context.QQQ      = symbol('QQQ')
    

    # Define the benchmark (used to get early close dates for reference).
    #context.spy           = sid(8554)
    context.canary = context.IWV
    context.risk_free = context.TLT
    
    start_date = context.SPY.security_start_date
    end_date   = context.SPY.security_end_date
    
    # Initialize context variables the define rebalance logic:
    context.rebalance_date = None
    context.next_rebalance_Date = None
    context.rebalance_days = 10
    #context.rebalance_window_start = 10
    #context.rebalance_window_stop  = 15
    
    # Get the dates when the market closes early:
    context.early_closes = get_early_closes(start_date,end_date).date
    
    
########### HANDLE_DATA() IS RUN ONCE PER MINUTE #######################
def handle_data(context, data):
    
    # Get the current exchange time, in local timezone: 
    exchange_time = pd.Timestamp(get_datetime()).tz_convert('US/Eastern')
    
    # If it is rebalance day, rebalance:
    if  context.rebalance_date == None or exchange_time >= context.next_rebalance_date:
        
       # If EOD, cancel all open orders, maintains consistency with live trading.
       cancel_orders_EOD(context)
       
       # If it's 10am proceed with rebalancing, otherwise skip this minute of trading.
       if exchange_time.hour != 10:
        return 
       
       # Check if there are any existing open orders. has_orders() defined below.
       has_orders = has_open_orders(data,context)
        
       # If we are in rebalance window but there are open orders, wait til next minute
       if has_orders == True:
            log.info('Has open orders')
            return
        
       # If there are no open orders we can rebalance.
       elif has_orders == False:
           
           rebalance(context, data, exchange_time) 
           log.info('Rebalanced portfolio to target weights at %s' % exchange_time)

           # Update the current and next rebalance dates
           context.rebalance_date = exchange_time 
           context.next_rebalance_date = context.rebalance_date + \
            datetime.timedelta(days=context.rebalance_days)      
    else:
        return
    
########### CORE REBALANCE LOGIC #########################################
## THIS FUNCTION IS RUN ONLY AT REBALANCE (DAY/TIME) #####################
def rebalance(context,data,exchange_time):
    
    #Rebalance the portfolio to the predetermined target weights:
    #
    #  61%   - MarketAxess Holdings Inc
    #  16%   - BGC Partners, Inc
    #  05%   - Interactive Brokers Group, Inc
    #  12%   - Nasdaq Inc
    #  06%  - CBOE Holdings, Inc
     Canary = data.history(context.canary, 'price', 80, '1d')
#    RiskFree = data.history(context.risk_free, 'price', 80, '1d')
#    Canary_Mom = Canary[-1]/Canary[-20]
     Canary_fast = Canary[-15:].mean()
     Canary_slow = Canary.mean()
#    RiskFree_Mom = RiskFree[-1]/RiskFree[-20]
    
     if Canary_fast > Canary_slow:    
#   if Canary_Mom > RiskFree_Mom:
        order_target_percent(context.IWV,0.75)
        #order_target_percent(context.TLT,0.00)
        order_target_percent(context.TLT,0.25)
        #order_target_percent(context.NDAQ,0.122)
        #order_target_percent(context.CBOE,0.059)
     else: 
        #order_target_percent(context.TLT,0.45)
        order_target_percent(context.IWV,0.25)
        order_target_percent(context.TLT,0.75)
        #order_target_percent(context.NDAQ,0.122)
        #order_target_percent(context.CBOE,0.059)
        pass
        

########### HELPER FUNCTIONS ##############################################
        
## IN LIVE TRADE ALL OPEN ORDERS ARE CANCELLED EOD. HANDLE EXPLICITLY WITH THIS
## HELPER FUNCTION SO THAT ALL OPEN ORDERS ARE ALSO CANCELLED AT EOD IN BACKTEST.
def cancel_orders_EOD(context):

    date = get_datetime().date()
    
    # set the closing hour, based on get_early_closes (assumes that all early closes are at 13)
    if date in context.early_closes:
        close = 13 # early closing time
    else:
        close = 16 # normal closing time
    
    loc_dt = pd.Timestamp(get_datetime()).tz_convert('US/Eastern')
    
    # if it is EOD, find open orders and cancel them, otherwise return
    if loc_dt.hour == close and loc_dt.minute == 0:
        pass
    else:
        return
    
    all_open_orders = get_open_orders()
    if all_open_orders:
        for security, oo_for_sid in all_open_orders.iteritems():
            for order_obj in oo_for_sid:
                log.info("%s: Cancelling order for %s of %s created on %s" % 
                         (get_datetime(), order_obj.amount,
                          security.symbol, order_obj.created))
                cancel_order(order_obj)    
                
## RETURNS TRUE IF THERE ARE PENDING OPEN ORDERS, OTHERWISE RETURNS FALSE
def has_open_orders(data,context):               
# Only rebalance when we have zero pending orders.
    has_orders = False
    for stk in data:
        orders = get_open_orders(stk)
        if orders:
            for oo in orders:                  
                message = 'Open order for {amount} shares in {stock}'  
                message = message.format(amount=oo.amount, stock=stk)  
            has_orders = True
    return has_orders           
    if has_orders:
        return  
There was a runtime error.

Can you pls. tell me what is the rationale of picking those Symbols in particular?