diff --git a/docs/docs/getting-started/resources.md b/docs/docs/getting-started/resources.md index 4739d887..fc098ae7 100644 --- a/docs/docs/getting-started/resources.md +++ b/docs/docs/getting-started/resources.md @@ -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. @@ -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) - \ No newline at end of file diff --git a/examples/AdanosSentimentResearch.ipynb b/examples/AdanosSentimentResearch.ipynb new file mode 100644 index 00000000..9d7ecd74 --- /dev/null +++ b/examples/AdanosSentimentResearch.ipynb @@ -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 +}