Back to Community
7D SMA of Sentiment as an overbullish or overbearish indicator

We look at the 7D SMA of the News Sentiment on Apple stock and use it as a over-bullish or over-bearish indicator

If the 7D SMA is > to 5,5 (validated empirically), but it starts to decline, we sell. If the 7D SMA is < 5 (also validated empirically), but it starts to rise, we buy. News sentiment data are generated by our InfoTrie FinSentS engine. They rank from 1 (very bearish) to 10 (very bullish). We generate scores on Equities, FX, commodities ...
Note that this daily scores represent average of sentiment scores of articles mentioning Apple in business and financial news. 6 months scores can be downloaded for free on http://portal.finsents.com
Post on our blog : http://www.infotrie.com/blog/capm-sentiment-and-oscillators/item/sentiment-analysis-as-a-contradictory-indicator-aapl-in-2013
This is a very basic scenario. I will soon post more advanced ones.

Clone Algorithm
26
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
# We look at the 7D SMA of Apple stock and use it as a overbullish or overbearish indicator
# If the 7D SMA is > to 5,5 (validated empirically), but it starts to decline, we sell
# If the 7D SMA is < 5  (also validated empirically), but it starts to rise, we buy
# Use fetcher to get data. News sentiment data generated by my InfoTrie FinSentS engine.
# Note that this daily scores represent average of sentiment scores of Articles mentioning
# Apple in Business and Financial News.
# 6 months scores can be downloaded for free on www.finsents.com
# Post on our blog : http://www.infotrie.com/blog/capm-sentiment-and-oscillators/item/sentiment-analysis-as-a-contradictory-indicator-aapl-in-2013
# Check www.infotrie.com and www.finsents.com for more details.

import numpy as np
import datetime
import pandas as pd
from datetime import timedelta

window_roll = 7 # window length for rolling stats

prev_sma = 0
prev_ema = 0 
traded_price = 0
    
def initialize(context):
        
    # This is the search query we are using, this is tied to the csv file.
    context.query = 'Sentiment'   
    fetch_csv('http://data.infotrie.com/Apple Sentiment 2013 - 4 Q.csv',
              date_format='%Y-%m-%d',
              date_column='Date',
              headers={'User-Agent': 'Blah'},
              symbol='Sentiment',)
    context.order_size = 1000
    context.sec_id = 24
    context.security = sid(24) # Apple    
    context.stocks = [sid(24)]   
    context.sentiments = np.zeros([365])    
    context.count = 0
    
def handle_data(context, data):
    c = context
    global prev_sma
    global prev_ema
    global traded_price

    #log.info('prev_sma')
    #log.info(prev_sma)
    
    # Skip Saturdays and Sundays (we give signals on WE). 
    # We could refine algo and process WE's info
    current_day = get_datetime()  
    if ((current_day.day == 5) or (current_day.day == 6)):  
        #log.info(current_day)
        return
    
    if c.query not in data[c.query]:
        #log.info('Empty Return')
        return   
    
    # Extract Sentiment socres of search query.
    DailySentiment = data[c.query][c.query]
      
    for i, stock in enumerate(context.sentiments):
        context.sentiments[context.count] = DailySentiment

    sma = pd.rolling_mean(context.sentiments,window_roll)[context.count]
    ema = pd.ewma(context.sentiments,window_roll)[context.count]
    context.count += 1
    
    record(EMA=float(ema))
    record(SMA=float(sma))
    
  #  log.info('score')
  #  log.info(DailySentiment)
  #  log.info(context.sentiments)
  #  log.info('sma')
  #  log.info(sma)
  #  log.info(ema)
    amount = c.portfolio['positions'][c.sec_id].amount
    
  #  current_shares = context.portfolio.positions[stock].amount
    
    # Not looking at prices to take profit or to cover ! Only look at sentiment,
    # this of course can be enhanced
    if ((prev_sma < sma) and (amount < 0)): # and (sma > 5.5)
           #log.info('cover short')
           order(c.security, -amount)
  
    if ((prev_sma > sma) and (amount > 0)): # and (sma < 5)
            #log.info('cover long')
            order(c.security, -amount)        
    
    #  enter only when slope of SMA is inverting    
    #  sma < 5 prices are too low and should correct   
    #  sma > 5.5 prices are too high and should correct
    if (sma < 5) and (prev_sma < sma):
        order(c.security, c.order_size)
        #log.info('go long')
        traded_price = data[sid(24)].price
    else:
       if (sma > 5.5) and (prev_sma > sma):
           order(c.security, -c.order_size)  
           #log.info('go short')
           traded_price = data[sid(24)].price
    
    prev_sma = sma
    prev_ema = ema
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.
5 responses

Implemented a slightly more aggressive version of the algo. By checking previous day 7D sma of Sentiment it gives better results

    if ((sma  5.5) or (prev_sma > 5.5)) and (prev_sma > sma):  
           order(c.security, -c.order_size)  
           #log.info('go short')  
           traded_price = data[sid(24)].price  
Clone Algorithm
26
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
# We look at the 7D SMA of Apple stock and use it as a overbullish or overbearish indicator
# If the 7D SMA is > to 5,5 (validated empirically), but it starts to decline, we sell
# If the 7D SMA is < 5  (also validated empirically), but it starts to rise, we buy
# Use fetcher to get data. News sentiment data generated by my InfoTrie FinSentS engine.
# Note that this daily scores represent average of sentiment scores of Articles mentioning
# Apple in Business and Financial News.
# 6 months scores can be downloaded for free on www.finsents.com
# Post on our blog : http://www.infotrie.com/blog/capm-sentiment-and-oscillators/item/sentiment-analysis-as-a-contradictory-indicator-aapl-in-2013
# Check www.infotrie.com and www.finsents.com for more details.

import numpy as np
import datetime
import pandas as pd
from datetime import timedelta

window_roll = 7 # window length for rolling stats

prev_sma = 0
prev_ema = 0 
traded_price = 0
    
def initialize(context):
        
    # This is the search query we are using, this is tied to the csv file.
    context.query = 'Sentiment'   
    fetch_csv('http://data.infotrie.com/Apple Sentiment 2013 - 4 Q.csv',
              date_format='%Y-%m-%d',
              date_column='Date',
              headers={'User-Agent': 'Blah'},
              symbol='Sentiment',)
    context.order_size = 1000
    context.sec_id = 24
    context.security = sid(24) # Apple    
    context.stocks = [sid(24)]   
    context.sentiments = np.zeros([365])    
    context.count = 0
    
def handle_data(context, data):
    c = context
    global prev_sma
    global prev_ema
    global traded_price

    #log.info('prev_sma')
    #log.info(prev_sma)
    
    # Skip Saturdays and Sundays (we give signals on WE). 
    # We could refine algo and process WE's info
    current_day = get_datetime()  
    if ((current_day.day == 5) or (current_day.day == 6)):  
        #log.info(current_day)
        return
    
    if c.query not in data[c.query]:
        #log.info('Empty Return')
        return   
    
    # Extract Sentiment socres of search query.
    DailySentiment = data[c.query][c.query]
      
    for i, stock in enumerate(context.sentiments):
        context.sentiments[context.count] = DailySentiment

    sma = pd.rolling_mean(context.sentiments,window_roll)[context.count]
    ema = pd.ewma(context.sentiments,window_roll)[context.count]
    context.count += 1
    
    record(EMA=float(ema))
    record(SMA=float(sma))
    
  #  log.info('score')
  #  log.info(DailySentiment)
  #  log.info(context.sentiments)
  #  log.info('sma')
  #  log.info(sma)
  #  log.info(ema)
    amount = c.portfolio['positions'][c.sec_id].amount
    
  #  current_shares = context.portfolio.positions[stock].amount
    
    # Not looking at prices to take profit or to cover ! Only look at sentiment,
    # this of course can be enhanced
    if ((prev_sma < sma) and (amount < 0)): # and (sma > 5.5)
           #log.info('cover short')
           order(c.security, -amount)
  
    if ((prev_sma > sma) and (amount > 0)): # and (sma < 5)
            #log.info('cover long')
            order(c.security, -amount)        
    
    #  enter only when slope of SMA is inverting    
    #  sma < 5 prices are too low and should correct   
    #  sma > 5.5 prices are too high and should correct
    if ((sma < 5) or (prev_sma < 5)) and (prev_sma < sma):
        order(c.security, c.order_size)
        #log.info('go long')
        traded_price = data[sid(24)].price
    else:
       if ((sma > 5.5) or (prev_sma > 5.5)) and (prev_sma > sma):
           order(c.security, -c.order_size)  
           #log.info('go short')
           traded_price = data[sid(24)].price
    
    prev_sma = sma
    prev_ema = ema
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.

Corrected a few bugs and added a few checks on the way we cover

Clone Algorithm
26
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
# We look at the 7D SMA of Apple stock and use it as a overbullish or overbearish indicator
# If the 7D SMA is > to 5,5 (validated empirically), but it starts to decline, we sell
# If the 7D SMA is < 5  (also validated empirically), but it starts to rise, we buy
# Use fetcher to get data. News sentiment data generated by my InfoTrie FinSentS engine.
# Note that this daily scores represent average of sentiment scores of Articles mentioning
# Apple in Business and Financial News.
# 6 months scores can be downloaded for free on www.finsents.com
# Post on our blog : http://www.infotrie.com/blog/capm-sentiment-and-oscillators/item/sentiment-analysis-as-a-contradictory-indicator-aapl-in-2013
# Check www.infotrie.com and www.finsents.com for more details.

import numpy as np
import datetime as dt
import pandas as pd
from datetime import timedelta

window_roll = 7 # window length for rolling stats
window_roll_slow = 15 # window length for rolling stats
prev_sma = 0
prev_ema = 0 
#traded_price = 0
#Weighted_Price = 0
    
def initialize(context):
        
    # This is the search query we are using, this is tied to the csv file.
    context.query = 'Sentiment'   
    fetch_csv('http://data.infotrie.com/Apple Sentiment 2013 - 4 Q.csv',
              date_format='%Y-%m-%d',
              date_column='Date',
              headers={'User-Agent': 'Blah'},
              symbol='Sentiment',)
    context.sec_id = 24
    context.security = sid(24) # Apple    
    context.stocks = [sid(24)]   
    context.sentiments = np.zeros([365])    
    context.count = 0
    context.aapl = sid(24)
    context.max_notional = 1000000.1
    context.min_notional = -1000000.0
    #context.order_size = 2000

    
def handle_data(context, data):
    c = context
    global prev_sma
    global prev_ema

    amount = c.portfolio['positions'][c.sec_id].amount
  #  log.info('amount')
   # log.info(amount)
    
    #Define Order size for new trades
    if c.query not in data[c.query]:
        return   
    if (amount < 0):
        c.order_size = -(c.min_notional - (amount * data[c.security].price)) / data[c.security].price
    else:
         c.order_size = (c.max_notional - (amount * data[c.security].price)) / data[c.security].price
        
    # Extract Sentiment scores of search query.
    DailySentiment = data[c.query][c.query]
      
    for i, stock in enumerate(c.sentiments):
        c.sentiments[c.count] = DailySentiment

    sma = pd.rolling_mean(c.sentiments,window_roll)[c.count]
    sma_slow = pd.rolling_mean(c.sentiments,window_roll_slow)[c.count]
    ema = pd.ewma(c.sentiments,window_roll)[c.count]
    c.count += 1
    
    record(EMA=float(ema))
    record(SMA=float(sma))
    record(SMA_Slow=float(sma_slow))
    
    # Skip Saturdays and Sundays (we give signals on WE). 
    # We could refine algo and process WE's info
    current_day = get_datetime()  
    if ((current_day.weekday() == 5) or (current_day.weekday() == 6)):
        return
    
 
    cost_basis = c.portfolio.positions[context.aapl].cost_basis
    
    traded_price = data[c.security].price
    
    
    # Cover if prices go in opposite direction than sentiment - Only activate if set to 4% there
    if ((traded_price >= (cost_basis * 1.05)) and (amount < 0)): 
           log.info('cover short price')
           order(c.security, -amount)
  
    if ((traded_price <= (cost_basis * 0.95)) and (amount > 0)):
            log.info('cover long price')
            order(c.security, -amount)
    
    # Not looking at prices to take profit or to cover ! Only look at sentiment,
    # this of course can be enhanced
    # Cover/Take Profit if SMA change direction with condition on slow sma
    if ((prev_sma < sma) and (amount < 0) and (sma_slow > sma)):
           log.info('cover short 1')         
           order(c.security, -amount)
    if ((prev_sma > sma) and (amount > 0) and (sma_slow < sma)):
           log.info('cover long 1')
           order(c.security, -amount)

    # Cover/Take profit if SMA near inversion tresholds
    if ((amount < 0) and (sma < 4.75)):
           log.info('cover short 2')
           order(c.security, -amount)
    if ((amount > 0) and (sma > 5.5)):
            log.info('cover long 2')
            order(c.security, -amount)

    #  enter only when slope of SMA is inverting    
    #  sma < 5 prices are too low and should correct   
    #  sma > 5.5 prices are too high and should correct
    if ((sma < 4.75) or (prev_sma < 4.75)) and (prev_sma < sma):
        order(c.security, c.order_size)
        log.info('go long')
    else:
       if ((sma > 5.5) or (prev_sma > 5.5)) and (prev_sma > sma):
           order(c.security, -c.order_size)  
           log.info('go short')
               
    prev_sma = sma
    prev_ema = ema
    
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.

Corrected typos

Clone Algorithm
10
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
# We look at the 7D SMA of Apple stock and use it as a overbullish or overbearish indicator
# If the 7D SMA is > to 5,5 (validated empirically), but it starts to decline, we sell
# If the 7D SMA is < 4.75  (also validated empirically), but it starts to rise, we buy
# Use fetcher to get data. News sentiment data generated by my InfoTrie FinSentS engine.
# Note that this daily scores represent average of sentiment scores of Articles mentioning
# Apple in Business and Financial News.
# 6 months scores can be downloaded for free on www.finsents.com
# Post on our blog : http://www.infotrie.com/blog/capm-sentiment-and-oscillators/item/sentiment-analysis-as-a-contradictory-indicator-aapl-in-2013
# Check www.infotrie.com and www.finsents.com for more details.

import numpy as np
import datetime as dt
import pandas as pd
from datetime import timedelta

window_roll = 7 # window length for rolling stats
window_roll_slow = 15 # window length for rolling stats
prev_sma = 0
prev_ema = 0 
    
def initialize(context):
        
    # This is the search query we are using, this is tied to the csv file.
    context.query = 'Sentiment'   
    fetch_csv('http://data.infotrie.com/Apple Sentiment 2013 - 4 Q.csv',
              date_format='%Y-%m-%d',
              date_column='Date',
              headers={'User-Agent': 'Blah'},
              symbol='Sentiment',)
    context.security = sid(24) # Apple
    context.sentiments = np.zeros([365])    
    context.count = 0
    context.max_notional = 1000000.1
    context.min_notional = -1000000.0
    #context.order_size = 2000

    
def handle_data(context, data):
    c = context
    global prev_sma
    global prev_ema

    amount = c.portfolio['positions'][c.security].amount
  #  log.info('amount')
   # log.info(amount)
    
    #Define Order size for new trades
    if c.query not in data[c.query]:
        return   
    if (amount < 0):
        c.order_size = -(c.min_notional - (amount * data[c.security].price)) / data[c.security].price
    else:
         c.order_size = (c.max_notional - (amount * data[c.security].price)) / data[c.security].price
        
    # Extract Sentiment scores of search query.
    DailySentiment = data[c.query][c.query]
      
    for i, stock in enumerate(c.sentiments):
        c.sentiments[c.count] = DailySentiment

    sma = pd.rolling_mean(c.sentiments,window_roll)[c.count]
    sma_slow = pd.rolling_mean(c.sentiments,window_roll_slow)[c.count]
    ema = pd.ewma(c.sentiments,window_roll)[c.count]
    ema_slow = pd.ewma(c.sentiments,window_roll_slow)[c.count]
    c.count += 1
    
    #record(EMA=float(ema))
    record(SMA=float(sma))
    record(SMA_Slow=float(sma_slow))
    #record(EMA_Slow=float(ema_slow))
    
    # Skip Saturdays and Sundays (we give signals on WE). 
    # We could refine algo and process WE's info
    current_day = get_datetime()  
    if ((current_day.weekday() == 5) or (current_day.weekday() == 6)):
        return    
 
    cost_basis = c.portfolio.positions[c.security].cost_basis    
    traded_price = data[c.security].price
    
    
    # Cover if prices go in opposite direction than sentiment - Only activate if set to 4% there
    if ((traded_price >= (cost_basis * 1.05)) and (amount < 0)): 
           log.info('cover short price')
           order(c.security, -amount)
  
    if ((traded_price <= (cost_basis * 0.95)) and (amount > 0)):
            log.info('cover long price')
            order(c.security, -amount)
    
    # Not looking at prices to take profit or to cover ! Only look at sentiment,
    # this of course can be enhanced
    # Cover/Take Profit if SMA change direction with condition on slow sma
    if ((prev_sma < sma) and (amount < 0) and (sma_slow > sma)):
           log.info('cover short 1')         
           order(c.security, -amount)
    if ((prev_sma > sma) and (amount > 0) and (sma_slow < sma)):
           log.info('cover long 1')
           order(c.security, -amount)

    # Cover/Take profit if SMA near inversion tresholds
    if ((amount < 0) and (sma < 4.75)):
           log.info('cover short 2')
           order(c.security, -amount)
    if ((amount > 0) and (sma > 5.5)):
            log.info('cover long 2')
            order(c.security, -amount)

    #  enter only when slope of SMA is inverting    
    #  sma < 4.75 prices are too low and should correct   
    #  sma > 5.5 prices are too high and should correct
    if ((sma < 4.75) or (prev_sma < 4.75)) and (prev_sma < sma):
        order(c.security, c.order_size)
        log.info('go long')
    else:
       if ((sma > 5.5) or (prev_sma > 5.5)) and (prev_sma > sma):
           order(c.security, -c.order_size)  
           log.info('go short')
               
    prev_sma = sma
    prev_ema = ema
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.

Works as well with an EMA, even though need to change thresholds to trigger buy/sell

Clone Algorithm
4
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
# We look at the 7D SMA of Apple stock and use it as a overbullish or overbearish indicator
# If the 7D SMA is > to 5,5 (validated empirically), but it starts to decline, we sell
# If the 7D SMA is < 5  (also validated empirically), but it starts to rise, we buy
# Use fetcher to get data. News sentiment data generated by my InfoTrie FinSentS engine.
# Note that this daily scores represent average of sentiment scores of Articles mentioning
# Apple in Business and Financial News.
# 6 months scores can be downloaded for free on www.finsents.com
# Post on our blog : http://www.infotrie.com/blog/capm-sentiment-and-oscillators/item/sentiment-analysis-as-a-contradictory-indicator-aapl-in-2013
# Check www.infotrie.com and www.finsents.com for more details.

import numpy as np
import datetime as dt
import pandas as pd
from datetime import timedelta

window_roll = 7 # window length for rolling stats
window_roll_slow = 15 # window length for rolling stats
prev_sma = 0
prev_ema = 0 
    
def initialize(context):
        
    # This is the search query we are using, this is tied to the csv file.
    context.query = 'Sentiment'   
    fetch_csv('http://data.infotrie.com/Apple Sentiment 2013 - 4 Q.csv',
              date_format='%Y-%m-%d',
              date_column='Date',
              headers={'User-Agent': 'Blah'},
              symbol='Sentiment',)
    context.security = sid(24) # Apple      
    context.sentiments = np.zeros([365])    
    context.count = 0
    context.max_notional = 1000000.1
    context.min_notional = -1000000.0
    #context.order_size = 2000

    
def handle_data(context, data):
    c = context
    global prev_sma
    global prev_ema

    amount = c.portfolio['positions'][c.security].amount
  #  log.info('amount')
   # log.info(amount)
    
    #Define Order size for new trades
    if c.query not in data[c.query]:
        return   
    if (amount < 0):
        c.order_size = -(c.min_notional - (amount * data[c.security].price)) / data[c.security].price
    else:
         c.order_size = (c.max_notional - (amount * data[c.security].price)) / data[c.security].price
        
    # Extract Sentiment scores of search query.
    DailySentiment = data[c.query][c.query]
      
    for i, stock in enumerate(c.sentiments):
        c.sentiments[c.count] = DailySentiment

    sma = pd.rolling_mean(c.sentiments,window_roll)[c.count]
    sma_slow = pd.rolling_mean(c.sentiments,window_roll_slow)[c.count]
    ema = pd.ewma(c.sentiments,window_roll)[c.count]
    ema_slow = pd.ewma(c.sentiments,window_roll_slow)[c.count]
    c.count += 1
    
    record(EMA=float(ema))
   # record(SMA=float(sma))
   # record(SMA_Slow=float(sma_slow))
   # record(EMA_Slow=float(ema_slow))
    
    # Skip Saturdays and Sundays (we give signals on WE). 
    # We could refine algo and process WE's info
    current_day = get_datetime()  
    if ((current_day.weekday() == 5) or (current_day.weekday() == 6)):
        return
    
 
    cost_basis = c.portfolio.positions[c.security].cost_basis
    
    traded_price = data[c.security].price
    
    
    # Cover if prices go in opposite direction than sentiment - Only activate if set to 4% there
    if ((traded_price >= (cost_basis * 1.05)) and (amount < 0)): 
           log.info('cover short price')
           order(c.security, -amount)
  
    if ((traded_price <= (cost_basis * 0.95)) and (amount > 0)):
            log.info('cover long price')
            order(c.security, -amount)
    
    # Not looking at prices to take profit or to cover ! Only look at sentiment,
    # this of course can be enhanced
    # Cover/Take Profit if SMA change direction with condition on slow sma
    if ((prev_ema < ema) and (amount < 0) and (ema_slow > ema)):
           log.info('cover short 1')         
           order(c.security, -amount)
    if ((prev_ema > ema) and (amount > 0) and (ema_slow < ema)):
           log.info('cover long 1')
           order(c.security, -amount)

    # Cover/Take profit if SMA near inversion tresholds
    if ((amount < 0) and (ema < 5.1)):
           log.info('cover short 2')
           order(c.security, -amount)
    if ((amount > 0) and (ema > 5.35)):
            log.info('cover long 2')
            order(c.security, -amount)

    #  enter only when slope of SMA is inverting    
    #  sma < 5 prices are too low and should correct   
    #  sma > 5.5 prices are too high and should correct
    if ((ema < 5.1) or (prev_ema < 5.1)) and (prev_ema < ema):
        order(c.security, c.order_size)
        log.info('go long')
    else:
       if ((ema > 5.35) or (prev_ema > 5.35)) and (prev_ema > ema):
           order(c.security, -c.order_size)  
           log.info('go short')
               
    prev_sma = sma
    prev_ema = ema
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.

Similar example on MSFT, results are less good there: the stock took more than 40% during that period and I can only reach a maximum of 27-28% by tweaking my thresholds. Still there are multiple interesting signals detected and tradable.

Clone Algorithm
5
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
# We look at the 7D SMA of MSFT stock and use it as a overbullish or overbearish indicator
# If the 7D SMA is > to 6,55 (validated empirically), but it starts to decline, we sell
# If the 7D SMA is < 5.8  (also validated empirically), but it starts to rise, we buy
# Use fetcher to get data. News sentiment data generated by my InfoTrie FinSentS engine.
# Note that this daily scores represent average of sentiment scores of Articles mentioning
# Apple in Business and Financial News.
# 6 months scores can be downloaded for free on www.finsents.com
# Post on our blog : http://www.infotrie.com/blog/capm-sentiment-and-oscillators/item/sentiment-analysis-as-a-contradictory-indicator-aapl-in-2013
# Check www.infotrie.com and www.finsents.com for more details.

import numpy as np
import datetime as dt
import pandas as pd
from datetime import timedelta

window_roll = 7 # window length for rolling stats
window_roll_slow = 15 # window length for rolling stats
prev_sma = 0
prev_ema = 0 
    
def initialize(context):
        
    # This is the search query we are using, this is tied to the csv file.
    context.query = 'Sentiment'   
    fetch_csv('http://data.infotrie.com/MSFT Sentiment 2013 - 4 Q.csv',
              date_format='%Y-%m-%d',
              date_column='Date',
              headers={'User-Agent': 'Blah'},
              symbol='Sentiment',)
    context.security = sid(5061) # MSFT     
    context.sentiments = np.zeros([365])    
    context.count = 0
    context.max_notional = 1000000.1
    context.min_notional = -1000000.0
    #context.order_size = 2000

    
def handle_data(context, data):
    c = context
    global prev_sma
    global prev_ema

    amount = c.portfolio['positions'][c.security].amount
  #  log.info('amount')
   # log.info(amount)
    
    #Define Order size for new trades
    if c.query not in data[c.query]:
        return   
    if (amount < 0):
        c.order_size = -(c.min_notional - (amount * data[c.security].price)) / data[c.security].price
    else:
         c.order_size = (c.max_notional - (amount * data[c.security].price)) / data[c.security].price
        
    # Extract Sentiment scores of search query.
    DailySentiment = data[c.query][c.query]
      
    for i, stock in enumerate(c.sentiments):
        c.sentiments[c.count] = DailySentiment

    sma = pd.rolling_mean(c.sentiments,window_roll)[c.count]
    sma_slow = pd.rolling_mean(c.sentiments,window_roll_slow)[c.count]
    ema = pd.ewma(c.sentiments,window_roll)[c.count]
    ema_slow = pd.ewma(c.sentiments,window_roll_slow)[c.count]
    c.count += 1
    
   # log.info('Logs')
   # log.info(DailySentiment)
   #log.info(sma)
    #log.info(ema_slow)
   # log.info(data[c.security].price)
    
   #record(EMA=float(ema))
    record(SMA=float(sma))
    record(SMA_Slow=float(sma_slow))
   # record(EMA_Slow=float(ema_slow))
    
    # Skip Saturdays and Sundays (we give signals on WE). 
    # We could refine algo and process WE's info
    current_day = get_datetime()  
    if ((current_day.weekday() == 5) or (current_day.weekday() == 6)):
        return
    
 
    cost_basis = c.portfolio.positions[c.security].cost_basis
    
    traded_price = data[c.security].price
    
    
    # Cover if prices go in opposite direction than sentiment - Only activate if set to 4% there
    if ((traded_price >= (cost_basis * 1.05)) and (amount < 0)): 
           log.info('cover short price')
           order(c.security, -amount)
  
    if ((traded_price <= (cost_basis * 0.95)) and (amount > 0)):
            log.info('cover long price')
            order(c.security, -amount)
    
    # Not looking at prices to take profit or to cover ! Only look at sentiment,
    # this of course can be enhanced
    # Cover/Take Profit if SMA change direction with condition on slow sma
    if ((prev_sma < sma) and (amount < 0) and (sma_slow > sma)):
           log.info('cover short 1')         
           order(c.security, -amount)
    if ((prev_sma > sma) and (amount > 0) and (sma_slow < sma)):
           log.info('cover long 1')
           order(c.security, -amount)

    # Cover/Take profit if SMA near inversion tresholds
    if ((amount < 0) and (sma < 5.8)):
           log.info('cover short 2')
           order(c.security, -amount)
    if ((amount > 0) and (sma > 6.55)):
            log.info('cover long 2')
            order(c.security, -amount)

    #  enter only when slope of SMA is inverting    
    #  sma < 5 prices are too low and should correct   
    #  sma > 5.5 prices are too high and should correct
    if ((sma < 5.8) or (prev_sma < 5.8)) and (prev_sma < sma): # and (sma_slow > sma):
        order(c.security, c.order_size)
        log.info('go long')
    else:
       if ((sma > 6.55) or (prev_sma > 6.55)) and (prev_sma > sma): # and (sma_slow < sma):
           order(c.security, -c.order_size)  
           log.info('go short')
               
    prev_sma = sma
    prev_ema = ema
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.