To recap, we're interested in using sentiment analysis from Sentdex to include into our algorithmic trading strategy.
Since Quantopian limits the amount of companies in our universe, first we need to get a list of ~200 companies that we want to trade. To do this, we're going to reference the top 200 companies in terms of sentiment volume that is collected. To get that, we can reference the Sentdex sentiment analysis API again, heading to: Top 200 Companies by sentiment volume
This list allows us to copy and paste it into a parameter to cycle through within our script, called context.stocks:
def initialize(context): context.investment_size = (context.portfolio.cash / 10.0) context.stop_loss_pct = 0.995 set_symbol_lookup_date('2012-10-01') fetch_csv('http://sentdex.com/api/finance/sentiment-signals/sample/', pre_func = preview) context.stocks = symbols('AAPL', 'MCD', 'FB', 'GME', 'INTC', 'SBUX', 'T', 'MGM', 'SHLD', 'NKE', 'NFLX', 'PFE', 'GS', 'TGT', 'NOK', 'SNE', 'TXN', 'JNJ', 'KO', 'VZ', 'XOM', 'WMT', 'MCO', 'TWTR', 'URBN', 'MCP', 'MSFT', 'HD', 'KSS', 'AMZN', 'S', 'BA', 'F', 'JPM', 'QCOM', 'TSLA', 'YHOO', 'BBRY', 'GM', 'IBM', 'C', 'ZNGA', 'BAC', 'DIS', 'SCHW', 'UA', 'CSCO', 'ORCL', 'SYMC', 'WFC', 'TM', 'EBAY', 'SCHL', 'MS', 'NDAQ', 'TIF', 'AIG', 'DAL', 'JCP', 'MRK', 'CA', 'SIRI', 'AMD', 'CVX', 'FSLR', 'LMT', 'P', 'CBS', 'TWX', 'PEP', 'LNKD', 'CMG', 'NVDA', 'BBY', 'TWC', 'M', 'RHT', 'ACN', 'CRM', 'PETS', 'CELG', 'BLK', 'GD', 'DOW', 'YUM', 'GE', 'MA', 'DTV', 'DDD', 'CAT', 'FDX', 'GRPN', 'ACE', 'BK', 'GILD', 'V', 'DUK', 'FFIV', 'WFM', 'CVS', 'UNH', 'LUV', 'CBG', 'AFL', 'CHK', 'BRCM', 'HPQ', 'LULU', 'ATVI', 'RTN', 'EMC', 'NOC', 'MAR', 'X', 'BMY', 'LOW', 'COST', 'HON', 'SPLS', 'BKS', 'AA', 'AXP', 'AMGN', 'GPS', 'MDT', 'LLY', 'CME', 'MON', 'WWWW', 'MU', 'DG', 'TRIP', 'HAL', 'COH', 'WYNN', 'PCLN', 'HTZ', 'CLF', 'DD', 'ACI', 'FCX', 'AON', 'GMCR', 'CSX', 'ADBE', 'PRU', 'PG', 'MYL', 'STT', 'PPG', 'EXPE', 'KORS', 'JNPR', 'UTX', 'HOT', 'SNDK', 'CCL', 'DRI', 'BIIB', 'MHFI', 'BBT', 'APA', 'A', 'TDC', 'ANF', 'MTB', 'PPL', 'ABT', 'GNW', 'KMI', 'MET', 'FE', 'DVA', 'ETFC', 'GLW', 'NRG', 'INTU', 'KR', 'ARNA', 'VALE', 'MSI', 'EOG', 'AET', 'MAT', 'HST', 'COP', 'MO', 'IVZ', 'HUM', 'NUE', 'CI')
Next up, we need to write the trading logic that will trade based on sentiment signals:
def handle_data(context, data): cash = context.portfolio.cash try: for s in data: if 'sentiment_signal' in data[s]: sentiment = data[s]['sentiment_signal'] current_position = context.portfolio.positions[s].amount current_price = data[s].price if (sentiment > 5) and (current_position == 0): if cash > context.investment_size: order_value(s, context.investment_size, style=StopOrder(current_price * context.stop_loss_pct)) cash -= context.investment_size elif (sentiment <= -1) and (current_position > 0): order_target(s,0) except Exception as e: print(str(e))
In here, we cycle through each of the companies in our data, which is each of the 200 companies we specified to context.stocks. From here, we want to see if "sentiment_signal" is contained for that stock's data. What is happening here? The way the fetch_csv method works, is it will grab the CSV, which is organized by date. The handle_data method runs through day by day, so what is happening behind the scenes is the handle_data method is looking at all returns for that specific date. Then, we iterate through all of our plausible stocks from context.stocks, asking if there is a sentiment_signal, today, for each.
If there is a sentiment analysis signal for that company for that day, then we might be interested in investing, so the next step we take is to reference our current position in that company, to see if we're already invested. Next, we grab the current price of that company.
Next, we're ready to consider our strategy. To keep things simple, we're going to look for stocks with a sentiment signal rating of 6 for buying into them, and then look for stocks with a sentiment signal of -3 to short them. You can feel free to play with these values, but this is what we'll be using for now. So, we'll ask if either of these are the case, and, if so, we're going to place a buy.
Ignoring shorting for now, we need to consider how we will actually exit a company. We will incorporate a stop-loss, but this is obviously not enough. So, what we're going to do is require that sentiment is less than or equal to -1, or basically meaning less than 0. If that is the case, and we more than 0 positions (1 or more), then we want to sell all of our shares.
Running the full code, which should be:
def preview(df): log.info(df.head()) return df def initialize(context): context.investment_size = (context.portfolio.cash / 10.0) context.stop_loss_pct = 0.995 set_symbol_lookup_date('2012-10-01') fetch_csv('http://sentdex.com/api/finance/sentiment-signals/sample/', pre_func = preview) context.stocks = symbols('AAPL', 'MCD', 'FB', 'GME', 'INTC', 'SBUX', 'T', 'MGM', 'SHLD', 'NKE', 'NFLX', 'PFE', 'GS', 'TGT', 'NOK', 'SNE', 'TXN', 'JNJ', 'KO', 'VZ', 'XOM', 'WMT', 'MCO', 'TWTR', 'URBN', 'MCP', 'MSFT', 'HD', 'KSS', 'AMZN', 'S', 'BA', 'F', 'JPM', 'QCOM', 'TSLA', 'YHOO', 'BBRY', 'GM', 'IBM', 'C', 'ZNGA', 'BAC', 'DIS', 'SCHW', 'UA', 'CSCO', 'ORCL', 'SYMC', 'WFC', 'TM', 'EBAY', 'SCHL', 'MS', 'NDAQ', 'TIF', 'AIG', 'DAL', 'JCP', 'MRK', 'CA', 'SIRI', 'AMD', 'CVX', 'FSLR', 'LMT', 'P', 'CBS', 'TWX', 'PEP', 'LNKD', 'CMG', 'NVDA', 'BBY', 'TWC', 'M', 'RHT', 'ACN', 'CRM', 'PETS', 'CELG', 'BLK', 'GD', 'DOW', 'YUM', 'GE', 'MA', 'DTV', 'DDD', 'CAT', 'FDX', 'GRPN', 'ACE', 'BK', 'GILD', 'V', 'DUK', 'FFIV', 'WFM', 'CVS', 'UNH', 'LUV', 'CBG', 'AFL', 'CHK', 'BRCM', 'HPQ', 'LULU', 'ATVI', 'RTN', 'EMC', 'NOC', 'MAR', 'X', 'BMY', 'LOW', 'COST', 'HON', 'SPLS', 'BKS', 'AA', 'AXP', 'AMGN', 'GPS', 'MDT', 'LLY', 'CME', 'MON', 'WWWW', 'MU', 'DG', 'TRIP', 'HAL', 'COH', 'WYNN', 'PCLN', 'HTZ', 'CLF', 'DD', 'ACI', 'FCX', 'AON', 'GMCR', 'CSX', 'ADBE', 'PRU', 'PG', 'MYL', 'STT', 'PPG', 'EXPE', 'KORS', 'JNPR', 'UTX', 'HOT', 'SNDK', 'CCL', 'DRI', 'BIIB', 'MHFI', 'BBT', 'APA', 'A', 'TDC', 'ANF', 'MTB', 'PPL', 'ABT', 'GNW', 'KMI', 'MET', 'FE', 'DVA', 'ETFC', 'GLW', 'NRG', 'INTU', 'KR', 'ARNA', 'VALE', 'MSI', 'EOG', 'AET', 'MAT', 'HST', 'COP', 'MO', 'IVZ', 'HUM', 'NUE', 'CI') # Will be called on every trade event for the securities you specify. def handle_data(context, data): cash = context.portfolio.cash try: for s in data: if 'sentiment_signal' in data[s]: sentiment = data[s]['sentiment_signal'] current_position = context.portfolio.positions[s].amount current_price = data[s].price if (sentiment > 5) and (current_position == 0): if cash > context.investment_size: order_value(s, context.investment_size, style=StopOrder(current_price * context.stop_loss_pct)) cash -= context.investment_size elif (sentiment <= -1) and (current_position > 0): order_target(s,0) except Exception as e: print(str(e))
The results, choosing Oct 20 2012 to June 15 2015:
Very exciting results for me, to say the least! Beta is too high for Quantopian, but the Sharpe Ratio is on a great track. One way we can get Beta down as shown before? Shorting! Right now, we're only willing to go long, so it is only naturual that the Beta is close to a perfect 1.
In the next tutorial, we'll include shorting, as well as a bit of a safeguard for shorting.