diff --git a/project-templates/python/alternative-data-universe-coingeckouniverse/research.ipynb b/project-templates/python/alternative-data-universe-coingeckouniverse/research.ipynb
new file mode 100644
index 0000000000..c77984f27e
--- /dev/null
+++ b/project-templates/python/alternative-data-universe-coingeckouniverse/research.ipynb
@@ -0,0 +1,179 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "a1b2c3d4",
+ "metadata": {},
+ "source": [
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e5f6a7b8",
+ "metadata": {},
+ "source": [
+ "## CoinGecko Market Cap Research\n",
+ "\n",
+ "This notebook studies whether cryptocurrency market cap helps explain future returns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c9d0e1f2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "qb = QuantBook()\n",
+ "# Anchor the research clock to the start of 2026 for a reproducible history window.\n",
+ "qb.set_start_date(2026, 1, 1)\n",
+ "# Daily bars will have an end_time that matches the following midnight.\n",
+ "qb.settings.daily_precise_end_time = False\n",
+ "# Trade crypto in USD on Coinbase.\n",
+ "qb.set_account_currency(\"USD\")\n",
+ "market = Market.COINBASE\n",
+ "# Collect the Coinbase pairs quoted in the account currency.\n",
+ "market_pairs = [\n",
+ " x.key.symbol\n",
+ " for x in qb.symbol_properties_database.get_symbol_properties_list(market)\n",
+ " if x.value.quote_currency == qb.account_currency\n",
+ "]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a3b4c5d6",
+ "metadata": {},
+ "source": [
+ "### Build a CoinGecko Universe\n",
+ "\n",
+ "Select the 10 largest Coinbase USD coins by market cap, then inspect the returned universe history."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e7f8a9b0",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def select_assets(data: List[CoinGecko]) -> List[Symbol]:\n",
+ " # Filter coins that are quoted in the account currency and available on the market.\n",
+ " tradable = [d for d in data if d.coin and (d.coin + qb.account_currency) in market_pairs]\n",
+ " # Sort by market cap and return the top 10 as tradable Symbols.\n",
+ " return [f.create_symbol(market, qb.account_currency)\n",
+ " for f in sorted(tradable, key=lambda f: f.market_cap)[-10:]]\n",
+ "\n",
+ "# Add the CoinGecko universe.\n",
+ "universe = qb.add_universe(CoinGeckoUniverse, \"CoinGeckoUniverse\", Resolution.DAILY, select_assets)\n",
+ "# Request universe history of the last 90 days.\n",
+ "universe_history = qb.universe_history(universe, qb.time - timedelta(90), qb.time - timedelta(1), flatten=True).rename_axis(['time', 'symbol']).drop(columns=['time'])\n",
+ "# Print the returned shape and columns.\n",
+ "print(f\"Shape: {universe_history.shape}\")\n",
+ "print(f\"Columns: {list(universe_history.columns)}\")\n",
+ "universe_history.head()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c1d2e3f4",
+ "metadata": {},
+ "source": [
+ "### Universe Diagnostics\n",
+ "\n",
+ "Check how many assets pass the filter each day and summarize the factors."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a5b6c7d8",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Count selected assets by day.\n",
+ "universe_size = universe_history.reset_index().groupby(['time', 'symbol']).size().groupby('time').size()\n",
+ "print(f\"Universe days: {len(universe_size)}\")\n",
+ "# Store the selected symbol list.\n",
+ "unique_assets = list(universe_history.index.levels[1].unique())\n",
+ "print(f\"Mean universe size per day: {universe_size.mean():.1f}\")\n",
+ "print('')\n",
+ "print(universe_history.marketcap.describe().map('{:0.3f}'.format))\n",
+ "universe_size.plot(title='Daily Universe Size', ylabel='Universe Size');"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e9f0a1b2",
+ "metadata": {},
+ "source": [
+ "### Daily Universe Prices\n",
+ "\n",
+ "Fetch daily price history for every symbol that appears in the universe."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c3d4e5f6",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Extract unique assets\n",
+ "symbols = list(universe_history.index.get_level_values(1).unique())\n",
+ "# Fetch daily historical price metrics using the earliest timestamp available in the index.\n",
+ "history = qb.history(symbols, universe_history.index[0][0] - timedelta(1), qb.time, Resolution.DAILY)\n",
+ "history"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a7b8c9d0",
+ "metadata": {},
+ "source": [
+ "### Align Market Cap And Returns\n",
+ "\n",
+ "Build a joined table of market cap and future returns."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "3d56c7cd",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Align the factor with the return from the next open to the following open.\n",
+ "dataset = (\n",
+ " universe_history.reset_index().groupby(['time', 'symbol']).agg(marketcap=('marketcap', 'max'))\n",
+ " .join(history.open.unstack('symbol').sort_index().pct_change(2, fill_method=None).shift(-2).stack().rename('futurereturn'), how='inner')\n",
+ ")\n",
+ "\n",
+ "dataset.head()"
+ ]
+ }
+ ],
+ "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
+}