Back to Community
Sentiment Trading Algorithm

Dear Community,

First of all, I would like to say that I am an absolute beginner when it comes to backtesting my trading strategies. Nevertheless, I would be very happy about feedback and suggestions for improvement - because I believe that some errors have crept into my code.

Trading algorithm
I use two signals, the FinSents Sentiment signals and my own sentiment signals based on a sentiment analysis of newspaper articles. These should now be used to make long or short decisions. I only use three companies for my portfolio - this should be extended though.

Current Problems
I have a few questions about the weighting for each company: If I set the weighting to 1.0 or -1.0 for each company, then the return is much higher than if I make an equal weighting, for example, 0.33 or -0.33 for each company in the case of three companies. Unfortunately, I do not fully understand the weighing mechanism, as it is not just only one signal used for every company, but two signals for some companies. Does the weighting even play a role for the signal used in the algorithm or can there be multiple weightings for one company?
Another question that is not quite clear to me is how I can best and easiest implement the long and short strategy for every company. Does this really have to be done individually for each company, or is there a better function for this?

At the moment, according to my algorithm, the return is far too high (which would be nice, of course), but I don't think that's realistic. I would be very happy if you could point out my mistakes in my code because I think that such an algorithm could come to very good results if it would be implemented correctly.

I hope someone can help me with my problem.

Clone Algorithm
29
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
from operator import itemgetter

def initialize(context):
    context.ubs = sid(48129)
    context.abb = sid(22574)
    context.nvs = sid(21536)
    #context.ade = sid(15618)
    context.cs = sid(23058)
    #set_benchmark(sid(27026))
    context.investment_size = (context.portfolio.cash / 10.0)
    context.stop_loss_pct = 0.995
    
    schedule_function(SentimentTrading, 
                      date_rules.week_start(days_offset=0),
                      time_rules.market_open())  
    
    # Loading NZZ Sentiment Signals for UBS Group
    fetch_csv(  
        'https://dl.dropboxusercontent.com/s/hiii0fdduds6ti7/UBS_SentimentSignals.txt?dl=0',  
        delim_whitespace=True,  
        index_col=None,  
        date_column='date',  
        symbol='UBS',  
        date_format='%Y-%m-%d') 

    # Loading Sentiment Signals for UBS Group
    fetch_csv(  
        'https://dl.dropboxusercontent.com/s/2dcx8vfltnq5w8k/NS1-UBSN_VX.csv?dl=0',  
        delim_whitespace=False,  
        index_col=None,  
        date_column='Date',  
        symbol='UBS',  
        date_format='%Y-%m-%d')  

    # Loading NZZ Sentiment Signals for Novartis
    fetch_csv(  
        'https://dl.dropboxusercontent.com/s/jyngffa6adw1qmt/Novartis_SentimentSignals.txt?dl=0',  
        delim_whitespace=True,  
        index_col=None,  
        date_column='Date',  
        symbol='Novartis',  
        date_format='%Y-%m-%d') 

    # Loading Sentiment Signals for Novartis
    fetch_csv(  
        'https://dl.dropboxusercontent.com/s/lh89d4j1ju4dbnd/NS1-NOVN_VX.csv?dl=0',  
        delim_whitespace=False,  
        index_col=None,  
        date_column='Date',  
        symbol='Novartis',  
        date_format='%Y-%m-%d')
        
    
    # Loading Sentiment Signals for ABB
    fetch_csv(  
        'https://dl.dropboxusercontent.com/s/lu5w6cdilaah12i/NS1-ABB_IN.csv?dl=0',  
        delim_whitespace=False,  
        index_col=None,  
        date_column='Date',  
        symbol='ABB',  
        date_format='%Y-%m-%d')

    # Loading Sentiment Signals for Credit Suisse
    #fetch_csv(  
    #    'https://dl.dropboxusercontent.com/s/001haumsfd5htz1/NS1-CSGN_VX.csv?dl=0',  
    #    delim_whitespace=False,  
    #    index_col=None,  
    #    date_column='Date',  
    #    symbol='CS',  
    #    date_format='%Y-%m-%d')

    # Loading Sentiment Signals for Adecco
    #fetch_csv(  
    #    'https://dl.dropboxusercontent.com/s/ip5ou0y34edh2vo/NS1-ADENE_SW.csv?dl=0',  
    #    delim_whitespace=False,  
    #    index_col=None,  
    #    date_column='Date',  
    #    symbol='Adecco',  
    #    date_format='%Y-%m-%d')      


def handle_data(context,data):
    record(leverage=context.account.leverage)
        
def SentimentTrading(context,data):
    
    open_orders = get_open_orders()
        
    # Sentiment Signal of UBS using NZZ articles
    sentiment = data['UBS']['sentiment']  
    sentiment_normal = data['UBS']['sentiment_normal']  
     
    if (sentiment_normal > 1.5):
        if context.ubs not in open_orders:
            order_target_percent(context.ubs, 1.0)
                                           
    elif (sentiment_normal <= -2.5):
        if context.ubs not in open_orders:
            order_target_percent(context.ubs, -1.0)

    # Sentiment Signal of UBS using FinSents Database
    FinSentUBS = data['UBS']['Sentiment']  
    FinSentHighUBS = data['UBS']['Sentiment High']     
    
    if (FinSentUBS > 0.5):
        if context.ubs not in open_orders:
            order_target_percent(context.ubs, 1.0)
                                           
    elif (FinSentUBS <= -0.5):
        if context.ubs not in open_orders:
            order_target_percent(context.ubs, -1.0)
    
    # Sentiment Signal of Novartis using NZZ articles
    sentiment = data['Novartis']['Sentiment']  
    sentiment_normal = data['Novartis']['SentimentNormal']  
    sentiment_standard = data['Novartis']['SentimentStandard']  
     
    if (sentiment_standard > 1.5):
        if context.nvs not in open_orders:
            order_target_percent(context.nvs, 1.0)
                                           
    elif (sentiment_standard <= -2.5):
        if context.nvs not in open_orders:
            order_target_percent(context.nvs, -1.0)
  
    # Sentiment Signal of Novartis using FinSents Database        
    FinSentNovartis = data['Novartis']['Sentiment']  
    FinSentHighNovartis = data['Novartis']['Sentiment High']     
    
    if (FinSentNovartis > 0.5):
        if context.nvs not in open_orders:
            order_target_percent(context.nvs, 1.0)
                                           
    elif (FinSentNovartis <= -0.5):
        if context.nvs not in open_orders:
            order_target_percent(context.nvs, -1.0)

    # Sentiment Signal of ABB using FinSents Database        
    FinSentABB = data['ABB']['Sentiment']  
    FinSentHighABB = data['ABB']['Sentiment High']     
    
    if (FinSentABB > 0.5):
        if context.abb not in open_orders:
            order_target_percent(context.abb, 1.0)
                                           
    elif (FinSentABB <= -0.5):
        if context.abb not in open_orders:
            order_target_percent(context.abb, -1.0)
            
    # Sentiment Signal of Credit Suisse using FinSents Database        
    #FinSentCS = data['CS']['Sentiment']  
    #FinSentHighCS = data['CS']['Sentiment High']     
    
    #if (FinSentCS > 0.0):
    #    if context.cs not in open_orders:
    #        order_target_percent(context.cs, 1.0)
                                           
    #elif (FinSentCS <= -0.0):
    #    if context.cs not in open_orders:
    #        order_target_percent(context.cs, -1.0)
            
            
    # Sentiment Signal of Adeco using FinSents Database        
    #FinSentADE = data['Adecco']['Sentiment']  
    #FinSentHighADE = data['Adecco']['Sentiment High']     
    
    #if (FinSentADE > 0.0):
    #    if context.ade not in open_orders:
    #        order_target_percent(context.ade, 1.0)
                                           
    #elif (FinSentADE <= -0.0):
    #    if context.ade not in open_orders:
    #        order_target_percent(context.ade, -1.0)
There was a runtime error.
4 responses

Slightly adjusted algorithm. I would be really pleased if someone could point out some improvements I could make.

Clone Algorithm
29
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
from operator import itemgetter

def initialize(context):
    context.nvs = sid(21536)
    context.ubs = sid(48129)
    context.abb = sid(22574)
    #context.cs = sid(23058)
    #set_benchmark(sid(27026))
    context.investment_size = (context.portfolio.cash / 10.0)
    context.stop_loss_pct = 0.995
    
    schedule_function(SentimentTrading, 
                      date_rules.week_start(days_offset=0),
                      time_rules.market_open())  
    
    # Loading NZZ Sentiment Signals for UBS Group
    fetch_csv(  
        'https://dl.dropboxusercontent.com/s/hiii0fdduds6ti7/UBS_SentimentSignals.txt?dl=0',  
        delim_whitespace=True,  
        index_col=None,  
        date_column='date',  
        symbol='UBS',  
        date_format='%Y-%m-%d') 

    # Loading Sentiment Signals for UBS Group
    fetch_csv(  
        'https://dl.dropboxusercontent.com/s/2dcx8vfltnq5w8k/NS1-UBSN_VX.csv?dl=0',  
        delim_whitespace=False,  
        index_col=None,  
        date_column='Date',  
        symbol='UBS',  
        date_format='%Y-%m-%d')  

    # Loading NZZ Sentiment Signals for Novartis
    fetch_csv(  
        'https://dl.dropboxusercontent.com/s/jyngffa6adw1qmt/Novartis_SentimentSignals.txt?dl=0',  
        delim_whitespace=True,  
        index_col=None,  
        date_column='Date',  
        symbol='Novartis',  
        date_format='%Y-%m-%d') 

    # Loading Sentiment Signals for Novartis
    fetch_csv(  
        'https://dl.dropboxusercontent.com/s/lh89d4j1ju4dbnd/NS1-NOVN_VX.csv?dl=0',  
        delim_whitespace=False,  
        index_col=None,  
        date_column='Date',  
        symbol='Novartis',  
        date_format='%Y-%m-%d')
    
    # Loading Sentiment Signals for ABB
    fetch_csv(  
        'https://dl.dropboxusercontent.com/s/lu5w6cdilaah12i/NS1-ABB_IN.csv?dl=0',  
        delim_whitespace=False,  
        index_col=None,  
        date_column='Date',  
        symbol='ABB',  
        date_format='%Y-%m-%d')   

def handle_data(context,data):
    record(leverage=context.account.leverage)
        
def SentimentTrading(context,data):
    
    open_orders = get_open_orders()
        
    # Sentiment Signal of UBS using NZZ articles
    sentiment = data['UBS']['sentiment']  
    sentiment_normal = data['UBS']['sentiment_normal']  
     
    if (sentiment_normal > 1.5):
        if context.ubs not in open_orders:
            order_target_percent(context.ubs, 0.5)
                                           
    elif (sentiment_normal <= -2.5):
        if context.ubs not in open_orders:
            order_target_percent(context.ubs, -0.5)

    # Sentiment Signal of UBS using FinSents Database
    FinSentUBS = data['UBS']['Sentiment']  
    FinSentHighUBS = data['UBS']['Sentiment High']     
    
    if (FinSentUBS > 0.5):
        if context.ubs not in open_orders:
            order_target_percent(context.ubs, 0.5)
                                           
    elif (FinSentUBS <= -0.5):
        if context.ubs not in open_orders:
            order_target_percent(context.ubs, -0.5)
    
    # Sentiment Signal of Novartis using NZZ articles
    sentiment = data['Novartis']['Sentiment']  
    sentiment_normal = data['Novartis']['SentimentNormal']  
    sentiment_standard = data['Novartis']['SentimentStandard']  
     
    if (sentiment_standard > 1.5):
        if context.nvs not in open_orders:
            order_target_percent(context.nvs, 0.5)
                                           
    elif (sentiment_standard <= -2.5):
        if context.nvs not in open_orders:
            order_target_percent(context.nvs, -0.5)
  
    # Sentiment Signal of Novartis using FinSents Database        
    FinSentNovartis = data['Novartis']['Sentiment']  
    FinSentHighNovartis = data['Novartis']['Sentiment High']     
    
    if (FinSentNovartis > 0.5):
        if context.nvs not in open_orders:
            order_target_percent(context.nvs, 0.5)
                                           
    elif (FinSentNovartis <= -0.5):
        if context.nvs not in open_orders:
            order_target_percent(context.nvs, -0.5)

    # Sentiment Signal of ABB using FinSents Database        
    FinSentABB = data['ABB']['Sentiment']  
    FinSentHighABB = data['ABB']['Sentiment High']     
    
    if (FinSentABB > 0.5):
        if context.abb not in open_orders:
            order_target_percent(context.abb, 0.5)
                                           
    elif (FinSentABB <= -0.5):
        if context.abb not in open_orders:
            order_target_percent(context.abb, -0.5)
            
There was a runtime error.

Hi Jannick,

I took a look at the performance of your strategy from January to December 2017. Here are a few suggestions that could improve its performance/tradeability:

  1. Since this strategy only trades 3 stocks, its position concentration is extremely high. This is risky! I would recommend looking into trading a greater basket of stocks (for reference, we generally look for position concentrations around 5% or so).
  2. This strategy's positions over 2017 tend to be mostly long positions. Ideally, we'd want roughly equal exposure to long and short positions since we want to bet on the relative values of assets compared to the other assets in our universe.
  3. It looks like the leverage is a little high. This strategy trades with significantly greater than 1.1x leverage for most of 2017, but we look for algorithms with between 0.8-1.1x gross leverage.

Further explanation and examples for all leverage, exposure, and position concentration can be found in our Contest tutorial.

On a more practical level, you can streamline your algorithm using our new Self Serve Data feature and the Optimize API. The Self Serve Data feature is meant to provide a better alternative to Fetcher; you can read more about it here and see an example of it in action here. Since it looks like you have already developed an alpha factor, I would also recommend using the Optimize API's MaximizeAlpha objective to take care of your ordering for you (where your alpha factor would be sentiment, sentiment_standard, sentiment_normal, or some combination of the three).

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.

When you use order_target_percent() 1.0 = 100%, 0.5 = 50%, -1.0 = -100% (ie, short). Ideally the absolute value of all your weights added together should equal 1.0. If you go over 1.0, you're using borrowed money.

You'll want to use more stocks. You'll need more than 20 if you want to enter it into the contest. Also, 3 isn't going to give you a statistically viable enough sample. If you can generate signals for hundreds or even 1000 stocks and it consistently performs well, then you know you have a promising signal. A signal that works well on 3 stocks, well that could just be chance (and likely is).

Thank you very much for your responses. They helped me a lot!

I will, therefore, analyse further stocks and include them in the portfolio, as the concentration is too high. I wanted to adjust the leverage and include a max_leverage, but it doesn't seem to work (as well as the stop_loss, etc.). Furthermore, I tried to include commissions according to the procedure described in the docs. However, this does not seem to work either. Does anyone know why?

Thank you very much for your help!

Best,

Clone Algorithm
29
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
def initialize(context):
    set_commission(
        us_equities=commission.PerShare(cost=0.001, min_trade_cost=0),
        us_futures=commission.PerContract(cost=1, exchange_fee=0.85,
                                          min_trade_cost=0))                                          
    #context.investment_size = (context.portfolio.cash / 10.0)
    context.stop_loss_pct = 0.995
    context.max_leverage = 1.0
    context.max_pos_size = 0.33
    context.max_turnover = 0.975
    
    schedule_function(SentimentTrading, 
                      date_rules.week_start(days_offset=0),
                      time_rules.market_open())  
    
    # Loading NZZ Sentiment Signals for UBS Group
    fetch_csv(  
        'https://dl.dropboxusercontent.com/s/hiii0fdduds6ti7/UBS_SentimentSignals.txt?dl=0', 
        delim_whitespace=True,  
        index_col=None,  
        date_column='date',  
        symbol='UBS',  
        date_format='%Y-%m-%d') 
    
    # Loading Sentiment Signals for UBS Group
    fetch_csv(  
        'https://dl.dropboxusercontent.com/s/2dcx8vfltnq5w8k/NS1-UBSN_VX.csv?dl=0',  
        delim_whitespace=False,  
        index_col=None,  
        date_column='Date',  
        symbol='UBS',  
        date_format='%Y-%m-%d')  

    # Loading NZZ Sentiment Signals for Novartis
    fetch_csv(  
        'https://dl.dropboxusercontent.com/s/jyngffa6adw1qmt/Novartis_SentimentSignals.txt?dl=0',  
        delim_whitespace=True,  
        index_col=None,  
        date_column='Date',  
        symbol='Novartis',  
        date_format='%Y-%m-%d') 

    # Loading Sentiment Signals for Novartis
    fetch_csv(  
        'https://dl.dropboxusercontent.com/s/lh89d4j1ju4dbnd/NS1-NOVN_VX.csv?dl=0',  
        delim_whitespace=False,  
        index_col=None,  
        date_column='Date',  
        symbol='Novartis',  
        date_format='%Y-%m-%d')
    
    # Loading Sentiment Signals for ABB
    fetch_csv(  
        'https://dl.dropboxusercontent.com/s/lu5w6cdilaah12i/NS1-ABB_IN.csv?dl=0',  
        delim_whitespace=False,  
        index_col=None,  
        date_column='Date',  
        symbol='ABB',  
        date_format='%Y-%m-%d')  
 
def before_trading_start(context, data):
    context.nvs = sid(21536)
    context.ubs = sid(48129)
    context.abb = sid(22574)
    #context.cs = sid(23058)

def handle_data(context,data):
    record(lever=context.account.leverage,
           exposure=context.account.net_leverage,
           num_pos=len(context.portfolio.positions))
 
def SentimentTrading(context,data):
    
    open_orders = get_open_orders()
        
    # Sentiment Signal of UBS using NZZ articles
    sentiment = data['UBS']['sentiment']  
    sentiment_normal = data['UBS']['sentiment_normal']  
     
    if (sentiment_normal > 1.5):
        if context.ubs not in open_orders:
            order_target_percent(context.ubs, 0.33)
                                           
    elif (sentiment_normal <= -2.5):
        if context.ubs not in open_orders:
            order_target_percent(context.ubs, -0.33)

    # Sentiment Signal of UBS using FinSents Database
    FinSentUBS = data['UBS']['Sentiment']  
    FinSentHighUBS = data['UBS']['Sentiment High']     
    
    if (FinSentUBS > 0.5):
        if context.ubs not in open_orders:
            order_target_percent(context.ubs, 0.33)
                                           
    elif (FinSentUBS <= -0.5):
        if context.ubs not in open_orders:
            order_target_percent(context.ubs, -0.33)
    
    # Sentiment Signal of Novartis using NZZ articles
    sentiment = data['Novartis']['Sentiment']  
    sentiment_normal = data['Novartis']['SentimentNormal']  
    sentiment_standard = data['Novartis']['SentimentStandard']  
     
    if (sentiment_standard > 1.5):
        if context.nvs not in open_orders:
            order_target_percent(context.nvs, 0.33)
                                           
    elif (sentiment_standard <= -2.5):
        if context.nvs not in open_orders:
            order_target_percent(context.nvs, -0.33)
  
    # Sentiment Signal of Novartis using FinSents Database        
    FinSentNovartis = data['Novartis']['Sentiment']  
    FinSentHighNovartis = data['Novartis']['Sentiment High']     
    
    if (FinSentNovartis > 0.5):
        if context.nvs not in open_orders:
            order_target_percent(context.nvs, 0.33)
                                           
    elif (FinSentNovartis <= -0.5):
        if context.nvs not in open_orders:
            order_target_percent(context.nvs, -0.33)

    # Sentiment Signal of ABB using FinSents Database        
    FinSentABB = data['ABB']['Sentiment']  
    FinSentHighABB = data['ABB']['Sentiment High']     
    
    if (FinSentABB > 0.5):
        if context.abb not in open_orders:
            order_target_percent(context.abb, 0.33)
                                           
    elif (FinSentABB <= -0.5):
        if context.abb not in open_orders:
            order_target_percent(context.abb, -0.33)
There was a runtime error.