Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/docs/getting-started/resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Here's a collection of resources to get started.
- [Running Telegram signal bot](https://nbviewer.jupyter.org/github/polakowo/vectorbt/blob/master/examples/TelegramSignals.ipynb)
- [Porting RSI strategy from backtrader](https://nbviewer.jupyter.org/github/polakowo/vectorbt/blob/master/examples/PortingBTStrategy.ipynb)
- [Pairs trading (vs backtrader)](https://nbviewer.jupyter.org/github/polakowo/vectorbt/blob/master/examples/PairsTrading.ipynb)
- [Researching external stock sentiment with Adanos](https://nbviewer.jupyter.org/github/polakowo/vectorbt/blob/master/examples/AdanosSentimentResearch.ipynb)

Note: you must run the notebook to play with the widgets.

Expand Down Expand Up @@ -48,4 +49,3 @@ Note: you must run the notebook to play with the widgets.
- For questions on Numba and other parts, the best place to go to is [StackOverflow](https://stackoverflow.com/)
- If you have general questions, start a new [GitHub Discussion](https://github.com/polakowo/vectorbt/discussions)
- If you found what appears to be a bug, please [create a new issue](https://github.com/polakowo/vectorbt/issues)

192 changes: 192 additions & 0 deletions examples/AdanosSentimentResearch.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Researching external stock sentiment with Adanos and vectorbt\n",
"\n",
"This notebook shows how to use the Adanos Finance Sentiment API as an external confirmation layer in a vectorbt research workflow.\n",
"\n",
"We will:\n",
"\n",
"- fetch source-level sentiment for a basket of stocks\n",
"- build simple composites such as `average_buzz` and `bullish_avg`\n",
"- combine those values with a price trend filter from vectorbt\n",
"- rank candidates for deeper analysis\n",
"\n",
"Replace `ADANOS_API_KEY` with your own key before running the notebook.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import requests\n",
"import pandas as pd\n",
"import vectorbt as vbt\n",
"\n",
"ADANOS_API_KEY = \"YOUR_ADANOS_API_KEY\"\n",
"TICKERS = [\"TSLA\", \"NVDA\", \"AAPL\", \"MSFT\"]\n",
"LOOKBACK_DAYS = 7\n",
"\n",
"ADANOS_ENDPOINTS = {\n",
" \"reddit\": \"https://api.adanos.org/reddit/stocks/v1/stock/{ticker}\",\n",
" \"x\": \"https://api.adanos.org/x/stocks/v1/stock/{ticker}\",\n",
" \"news\": \"https://api.adanos.org/news/stocks/v1/stock/{ticker}\",\n",
" \"polymarket\": \"https://api.adanos.org/polymarket/stocks/v1/stock/{ticker}\",\n",
"}\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def classify_alignment(bullish_values):\n",
" spread = max(bullish_values) - min(bullish_values)\n",
" if spread <= 15:\n",
" return \"Aligned\"\n",
" if spread <= 30:\n",
" return \"Mixed\"\n",
" return \"Wide divergence\"\n",
"\n",
"\n",
"def fetch_source_signal(session, source, ticker):\n",
" response = session.get(\n",
" ADANOS_ENDPOINTS[source].format(ticker=ticker),\n",
" params={\"days\": LOOKBACK_DAYS},\n",
" timeout=15,\n",
" )\n",
" response.raise_for_status()\n",
" payload = response.json()\n",
"\n",
" volume = payload.get(\"trade_count\")\n",
" if volume is None:\n",
" volume = payload.get(\"mentions\", 0)\n",
"\n",
" return {\n",
" \"source\": source,\n",
" \"buzz\": float(payload.get(\"buzz_score\", 0.0)),\n",
" \"bullish_pct\": float(payload.get(\"bullish_pct\", 0.0)),\n",
" \"trend\": str(payload.get(\"trend\", \"stable\")).lower(),\n",
" \"volume\": int(volume or 0),\n",
" }\n",
"\n",
"\n",
"def build_sentiment_row(ticker):\n",
" session = requests.Session()\n",
" session.headers[\"X-API-Key\"] = ADANOS_API_KEY\n",
"\n",
" signals = []\n",
" for source in ADANOS_ENDPOINTS:\n",
" try:\n",
" signals.append(fetch_source_signal(session, source, ticker))\n",
" except requests.RequestException as exc:\n",
" print(f\"Skipping {ticker} {source}: {exc}\")\n",
"\n",
" if not signals:\n",
" return None\n",
"\n",
" buzz_values = [signal[\"buzz\"] for signal in signals]\n",
" bullish_values = [signal[\"bullish_pct\"] for signal in signals]\n",
" trends = [signal[\"trend\"] for signal in signals]\n",
"\n",
" return {\n",
" \"ticker\": ticker,\n",
" \"coverage\": len(signals),\n",
" \"average_buzz\": round(sum(buzz_values) / len(buzz_values), 1),\n",
" \"bullish_avg\": round(sum(bullish_values) / len(bullish_values), 1),\n",
" \"rising_sources\": sum(1 for trend in trends if trend == \"rising\"),\n",
" \"falling_sources\": sum(1 for trend in trends if trend == \"falling\"),\n",
" \"source_alignment\": classify_alignment(bullish_values),\n",
" }\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"rows = [build_sentiment_row(ticker) for ticker in TICKERS]\n",
"sentiment_df = pd.DataFrame([row for row in rows if row is not None]).set_index(\"ticker\")\n",
"sentiment_df.sort_values([\"average_buzz\", \"bullish_avg\"], ascending=False)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"close = vbt.YFData.download(sentiment_df.index.tolist(), start=\"2024-01-01\").get(\"Close\")\n",
"ma50 = vbt.MA.run(close, 50).ma\n",
"\n",
"price_snapshot = pd.DataFrame(\n",
" {\n",
" \"close\": close.iloc[-1],\n",
" \"ma50\": ma50.iloc[-1],\n",
" \"one_month_return_pct\": ((close.iloc[-1] / close.iloc[-21]) - 1).round(4) * 100,\n",
" }\n",
")\n",
"price_snapshot[\"above_50dma\"] = price_snapshot[\"close\"] > price_snapshot[\"ma50\"]\n",
"price_snapshot\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"research_df = sentiment_df.join(price_snapshot[[\"above_50dma\", \"one_month_return_pct\"]])\n",
"research_df = research_df.sort_values(\n",
" [\"above_50dma\", \"average_buzz\", \"bullish_avg\"],\n",
" ascending=[False, False, False],\n",
")\n",
"research_df\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## How to use this output\n",
"\n",
"A simple workflow is:\n",
"\n",
"- focus first on symbols with `above_50dma = True`\n",
"- prefer higher `average_buzz` and `bullish_avg`\n",
"- be careful with `Wide divergence` across sources\n",
"- treat this as a ranking layer before deeper backtests or live strategy changes\n",
"\n",
"This keeps Adanos in the role it fits best inside vectorbt: an external research input that helps prioritize which setups deserve attention.\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}