Does Financier has sentiments (Next)?
In first episode Does Financier has sentiments ?. This is not about a new love story but… | by Stéphane Chalon | Apr, 2025 | Medium there was a bad conclusion…
- “I was hoping to find that Sentiment_score from Alpha Vantage could be a mean to predict Stock Price evolution but it seems to be the opposite way; Stock Price evolution is triggering Sentiment_score. This is like News used to compute Sentiment_score are coming after Stock Price change has occurred…”
The proposal here is to dig into News sources and categories to determine if conclusion above remains valid when going to details
The same dataset than used in [1] is considered. Still TSLA sounds to be an interesting Ticker and good Time window starting in November after elections, covering post-end January after CEO behavior and showing 04/22 a bad EPS Surprise (-34.89% Earnings)
Alpha Vantage API offers for Sentiment_score number of related fields: title, url, time_published, authors, summary, banner_image, source, category_within_source, source_domain, topics, overall_sentiment_score, overall_sentiment_label, ticker_sentiment.
- source in : [‘Benzinga’, ‘Motley Fool’, ‘Zacks Commentary’, ‘CNBC’, ‘South China Morning Post’, ‘Decrypt.co’, ‘GlobeNewswire’, ‘Cointelegraph’, ‘Financial Times’, ‘Business Insider’]
- category_within_source in : [‘n/a’, ’News’, ‘Markets’, ‘Trading’, ‘General’, ‘Top News’, ‘Companies’, ‘Business’, ‘RSS’, ‘Economy’, ‘Investing’, ‘Top Stories’, ‘Finance’, ‘Money’, ‘Mergers’, ‘Earnings’]
Below is distribution of News for source, category_within_source
Let us compute two matrix of Granger Causality (GC) tests for different subset considering filter on source and category_within_source.
- McauseSentimentsEffectDaily : matrix of GC for sources x categories for cause ‘av_sentiment_score-return’, effect ‘daily-return’
- McauseDailyEffectSentiments : matrix of GC for sources x categories for cause ‘daily-return’, effect ‘av_sentiment_score-return’
# Function to apply
def gc_function(Category, Source):
df = news_df.copy()
df = df[(df['category_within_source'] == Category) & (df['source'] == Source)]
if df.empty:
return np.nan, np.nan
else:
# Compute mean sentiment score per date and Group News by date
#
ddf = df.groupby('date')[['sentiment_score']].mean().reset_index()
ddf.columns = ['date', 'av_sentiment_score']
# Merge in ddf Sentiments analysis and stock prices
# ---------- Merge ddf with stock_df ----------
merged_ddf = pd.merge(ddf, stock_df, on='date', how='left')
merged_ddf = merged_ddf.fillna(0)
# --- Filter null close values (week-end) ----------------------
merged_ddf = merged_ddf[merged_ddf['close'] >0]
# Adding daily_return, sentiment_return
#
merged_ddf['daily-return'] = merged_ddf['close'].pct_change()
merged_ddf['av_sentiment_score-return'] = merged_ddf['av_sentiment_score'].pct_change()
# Granger causality test matrix
#
gcdf = merged_ddf
gcdf.set_index('date', inplace=True)
# Drop rows with NaN (like first daily-return)
gcdf.dropna(inplace=True)
# Apply test
selected = ['av_sentiment_score-return', 'daily-return']
print(f'### source: {filterSource}, category: {filterCategory}')
pval_matrix = granger_matrix(gcdf[selected])
# 1/ cause 'av_sentiment_score-return', effect 'daily-return'
# 2/ cause 'daily-return', effect 'av_sentiment_score-return'
print(f'source {filterSource}, category: {filterCategory} cause sentimentscore-return {pval_matrix["daily-return"]["av_sentiment_score-return"]}')
print(f'source {filterSource}, category: {filterCategory} cause daily-return {pval_matrix["av_sentiment_score-return"]["daily-return"]}')
return pval_matrix["daily-return"]["av_sentiment_score-return"], pval_matrix["av_sentiment_score-return"]["daily-return"]
# Build the matrix
McauseSentimentsEffectDaily = []
McauseDailyEffectSentiments = []
for filterSource in filterSources:
rowCauseSentimentsEffectDaily = []
rowCauseDailyEffectSentiments = []
for filterCategory in filtersCategories:
# 1/ cause 'av_sentiment_score-return', effect 'daily-return'
# 2/ cause 'daily-return', effect 'av_sentiment_score-return'
gc_causeSentimentsEffectDaily, gc_causeDailyEffectSentiments = gc_function(filterCategory, filterSource)
rowCauseSentimentsEffectDaily.append(gc_causeSentimentsEffectDaily)
rowCauseDailyEffectSentiments.append(gc_causeDailyEffectSentiments)
McauseSentimentsEffectDaily.append(rowCauseSentimentsEffectDaily)
McauseDailyEffectSentiments.append(rowCauseDailyEffectSentiments)
1st chart below is informative since it is showing already shown cases where cause is Daily-return and Affected variable is Sentiment_score. This is conclusion reached in previous article where Sentiment_score is computed after stock price has changed. The News are explaining the stock price and not predicting an evolution to come
2nd chart below is interesting one since it is showing cases where cause is Sentiment_score-return and Affected variable is Daily-return. This is case where Sentiment_score is computed before stock price hange.
Get Stéphane Chalon’s stories in your inbox
Join Medium for free to get updates from this writer.
We see two cases where Granger causality test is giving a p-value < 0.05.
- source Benzinga, category_within_source News : p-value=0.04 < 0.05
- source Financial Times, category_within_source Markets : p-value=0.01 << 0.05
Conclusion and next steps:
- I was hoping to find that Sentiment_score from Alpha Vantage could be a mean to predict Stock Price evolution.
- The detailed analysis is showing that this may be possible under specific conditions depending on source and category_within_source.
[2] Python scripts and figures in GitHub — Sentiment/sentimentsScoreShortv2.py at main · stchalon/Sentiment and Sentiment/SentimentsScoreShortLoopv4.py at main · stchalon/Sentiment
[3] API Documentation | Alpha Vantage — NEWS_SENTIMENT function










