diff --git a/project-templates/python/custom-indicator/main.py b/project-templates/python/custom-indicator/main.py index 140da03788..fac808ba2f 100644 --- a/project-templates/python/custom-indicator/main.py +++ b/project-templates/python/custom-indicator/main.py @@ -9,7 +9,7 @@ def initialize(self) -> None: self.set_start_date(2024, 9, 1) self.set_end_date(2024, 12, 31) # Request daily SPY data to feed the indicators to generate trade signals and trade. - self._spy = self.add_equity("SPY", data_normalization_mode=DataNormalizationMode.RAW) + self._spy = self.add_equity("SPY") # Create a custom money flow index to generate a trade signal. self._custom_mfi = CustomMoneyFlowIndex(20) # Warm up for immediate usage of indicators. diff --git a/project-templates/python/custom-indicator/research.ipynb b/project-templates/python/custom-indicator/research.ipynb new file mode 100644 index 0000000000..df5a7f959e --- /dev/null +++ b/project-templates/python/custom-indicator/research.ipynb @@ -0,0 +1,199 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ci-logo", + "metadata": {}, + "source": [ + "![QuantConnect Logo](https://cdn.quantconnect.com/web/i/icon.png)\n", + "
\n", + "\n", + "## Custom Indicator Research\n", + "\n", + "This notebook implements a custom Money Flow Index and builds value and signal series from daily SPY history." + ] + }, + { + "cell_type": "markdown", + "id": "ci-setup-md", + "metadata": {}, + "source": [ + "### Set Up QuantBook\n", + "\n", + "Create a daily SPY subscription for the custom indicator updates." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "ci-setup", + "metadata": {}, + "outputs": [], + "source": [ + "qb = QuantBook()\n", + "qb.set_start_date(2024, 12, 31)\n", + "qb.settings.seed_initial_prices = True\n", + "equity = qb.add_equity(\"SPY\")" + ] + }, + { + "cell_type": "markdown", + "id": "ci-class-md", + "metadata": {}, + "source": [ + "### Define Indicator\n", + "\n", + "Implement a [custom Money Flow Index Indicator](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/custom-indicators)." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "ci-class", + "metadata": {}, + "outputs": [], + "source": [ + "class CustomMoneyFlowIndex(PythonIndicator):\n", + "\n", + " def __init__(self, period: int) -> None:\n", + " super().__init__()\n", + " self.value = 0\n", + " self._previous_typical_price = 0\n", + " self._negative_money_flow: RollingWindow[float] = RollingWindow(period)\n", + " self._positive_money_flow: RollingWindow[float] = RollingWindow(period)\n", + "\n", + " def update(self, input: TradeBar) -> bool:\n", + " # Estimate the money flow by averaging the price multiplied by volume.\n", + " typical_price = (input.high + input.low + input.close) / 3\n", + " money_flow = typical_price * input.volume\n", + " # Classify the flow as positive or negative relative to the previous bar.\n", + " self._negative_money_flow.add(money_flow if typical_price < self._previous_typical_price else 0)\n", + " self._positive_money_flow.add(money_flow if typical_price > self._previous_typical_price else 0)\n", + " self._previous_typical_price = typical_price\n", + " positive_money_flow_sum = sum(self._positive_money_flow)\n", + " total_money_flow = positive_money_flow_sum + sum(self._negative_money_flow)\n", + " # Set the value to the positive money flow ratio.\n", + " self.value = 100\n", + " if total_money_flow != 0:\n", + " self.value *= positive_money_flow_sum / total_money_flow\n", + " return self._positive_money_flow.is_ready" + ] + }, + { + "cell_type": "markdown", + "id": "ci-build-md", + "metadata": {}, + "source": [ + "### Build Time Series\n", + "\n", + "Feed daily TradeBar history through the indicator and store its value for each bar." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "ci-build", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2024-01-31 16:00:00 67.098719\n", + "2024-02-01 16:00:00 66.933356\n", + "2024-02-02 16:00:00 72.315045\n", + "2024-02-05 16:00:00 72.500965\n", + "2024-02-06 16:00:00 72.214645\n", + " ... \n", + "2024-12-23 16:00:00 56.324222\n", + "2024-12-24 13:00:00 55.565956\n", + "2024-12-26 16:00:00 55.006566\n", + "2024-12-27 16:00:00 53.274876\n", + "2024-12-30 16:00:00 48.933252\n", + "Name: mfi, Length: 231, dtype: float64" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mfi = CustomMoneyFlowIndex(20)\n", + "mfi_by_date = {\n", + " bar.end_time: mfi.value\n", + " for bar in qb.history[TradeBar](equity, 250, Resolution.DAILY)\n", + " if mfi.update(bar)\n", + "}\n", + "\n", + "indicator_values = pd.Series(mfi_by_date, name=\"mfi\")\n", + "indicator_values" + ] + }, + { + "cell_type": "markdown", + "id": "ci-signal-md", + "metadata": {}, + "source": [ + "### Signal Series\n", + "\n", + "Derive long and short signals based on the money flow indicator." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "ci-signal", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2024-01-31 16:00:00 1\n", + "2024-02-01 16:00:00 1\n", + "2024-02-02 16:00:00 1\n", + "2024-02-05 16:00:00 1\n", + "2024-02-06 16:00:00 1\n", + " ..\n", + "2024-12-23 16:00:00 1\n", + "2024-12-24 13:00:00 1\n", + "2024-12-26 16:00:00 1\n", + "2024-12-27 16:00:00 1\n", + "2024-12-30 16:00:00 -1\n", + "Name: signal, Length: 231, dtype: int64" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Go long when demand outweighs supply, otherwise go short.\n", + "signal = pd.Series(-1, index=indicator_values.index, name=\"signal\")\n", + "signal[indicator_values > 50] = 1\n", + "signal" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Foundation-Py-Default", + "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.11.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}