Passer au contenu

Voir l’original

CryptoLowVolAnomalySpotBasketLongOnly

Hypothèses

Anomalie de faible volatilité crypto, sélection long-only (sans jambe short) : détenir les 5 noms à volatilité réalisée LOWEST la plus faible d'un univers spot liquide fixe de 25 coins, pondération égale, rééquilibrage 10 jours sur barres DAILY, portefeuille calibré sur un objectif de volatilité ex-ante 15% — OHLCV pur, sans porte, sans flux supplémentaire

Hypothèses

Une stratégie cross-sectionnelle LONG-ONLY, SELECTION-BASED (explicitement NOT dollar-neutre, NOT couverte en beta, NO jambe courte) sur BINANCE_SPOT au sein d'un univers liquide FIXED de 25 noms. Toutes les 10 barres quotidiennes, classez chaque cryptomonnaie selon sa propre volatilité réalisée glissante sur 20 jours et détenez les 5 LOWEST à pondération égale ; l'ensemble du portefeuille est ensuite mis à l'échelle par un facteur unique min(1.0, 0.15 / mean_RV_of_the_5) afin que le portefeuille vise une volatilité annualisée d'environ 15% et ne dépasse jamais un notionnel de 1.0x (sans effet de levier, compte cash). Il n'y a pas de terme de momentum, pas de filtre de tendance, pas de porte de régime, pas de flux supplémentaire, et pas de paramètre de seuil — la volatilité réalisée est le ENTIRE signal et elle est utilisée comme un RANK, non comme une porte tout-ou-rien. J'ai mesuré cela avant de le proposer, sur le catalogue, plutôt que de l'affirmer. En décodant directement les barres à virgule fixe de Nautilus (BINANCE_SPOT quotidiennes, 25/25 noms présents, 2017-10-27 -> 2026-09-19, ~9 ans), la configuration ci-dessus renvoie un Sharpe +0.74 NET sur l'ensemble de l'aller-retour spot 0.20%, 13.4% annualisé pour une volatilité de 18.1%, un drawdown maximum de -33.6%, 430 entrées, et un rendement moyen par trade de 1.37% du notionnel de position. Par année civile : 2018 -19%, 2019 +17%, 2020 +49%, 2021 +40%, 2022 -26%, 2023 +40%, 2024 +41%, 2025 +6%, 2026 -5% — 6 années sur 9 positives, et elle survit à BOTH marchés baissiers complets. CRITICAL ROBUSTNESS EVIDENCE (il s'agit d'un contrôle de réplication, NOT une jambe proxy — la stratégie ne trade que BINANCE_SPOT) : la même règle de classement a été exécutée indépendamment sur des perpétuels linéaires BYBIT et des perpétuels BINANCE USD-M. Sur une fenêtre 2022-2026 alignée par date, les deux plateformes concordent à la seconde décimale près — Sharpe faible-volatilité +0.58 (Bybit) contre +0.57 (Binance), volatilité élevée +0.11 contre +0.11, rendement annuel 31.0% contre 30.4% — face à un benchmark à pondération égale de +0.25/+0.21. Le même effet se reproduit sur trois pipelines de données de plateformes indépendants. Sur une fenêtre non alignée, les plateformes semblaient être en désaccord (0.38 contre 1.01) ; il s'agissait purement d'un artefact de début d'historique (l'univers de Bybit est incomplet avant 2022), que j'ai isolé plutôt que de le rapporter comme un résultat. Cause économique et WHO PAYS : il s'agit de l'anomalie de faible volatilité / préférence pour la loterie (Bali-Cakici-Whitelaw ; Frazzini-Pedersen), et la crypto en est l'habitat le plus pur. Les cryptomonnaies à volatilité réalisée élevée sont l'endroit où se concentre le flux retail en quête de levier et de loterie — elles sont surachetées car les acheteurs paient pour la CHANCE d'un gain important, et non pour le rendement attendu. Ces acheteurs sont la contrepartie : ils surpaient systématiquement la volatilité et acceptent des rendements ajustés du risque médiocres. La mesure est frappante et constitue l'affirmation falsifiable centrale — sur 2022-2026, les 5 noms à HIGHEST volatilité ont rapporté environ 9% annualisé tandis que les 5 LOWEST ont rapporté environ 31%, le panier à forte volatilité portant environ le double de la volatilité. La prime n'est pas un effet de tendance de prix : j'ai vérifié que le classement par momentum glissant produit un profil différent et plus faible sur des fenêtres non chevauchantes honnêtes (t=+1.25) que le classement par volatilité (t=+3.30 brut, +2.53 regroupé par date).

Hypothèses

Restaure l'univers de 25 noms de l'hypothèse et l'exposition brute maximale de 1.0 tout en conservant le mécanisme pur de classement par faible volatilité OHLCV.

Hypothèses

verification_loop : Échec de la vérification (Couche 2 — scénarios synthétiques) : Paramètres utilisés : ['n_hold', 'cash_buffer', 'min_notional', '_param_bounds', 'rebalance_days', 'rv_lookback_days', 'target_annual_vol', 'max_gross_exposure', 'rebalance_tolerance'] Vérifiez que __init__ définit tous les attributs à partir de self.parameters.get(). - steady_uptrend : TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (horodatage de la barre : 1735692060000) - steady_downtrend : TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (horodatage de la barre : 1735692060000) - flat_ranging : TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (horodatage de la barre : 1735692060000) - volatility_spike : TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (horodatage de la barre : 1735692060000) - zero_volume : TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (horodatage de la barre : 1735692060000) - price_gap : TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (horodatage de la barre : 1735692060000)

Implémentation

Panier long uniquement sur Binance Spot détenant les cinq noms à la plus faible volatilité réalisée issus d'un univers fixe de 25 cryptomonnaies, rééquilibré tous les dix barres quotidiennes et calibré à une volatilité annualisée de 15% avec un plafond d'exposition brute de 1.0x.

Résultats de vérification

Échec de la vérification (Couche 2 — scénarios synthétiques) : Paramètres utilisés : ['n_hold', 'cash_buffer', 'min_notional', '_param_bounds', 'rebalance_days', 'rv_lookback_days', 'target_annual_vol', 'max_gross_exposure', 'rebalance_tolerance'] Vérifiez que __init__ définit tous les attributs à partir de self.parameters.get(). - steady_uptrend : TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (horodatage de la barre : 1735692060000) - steady_downtrend : TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (horodatage de la barre : 1735692060000) - flat_ranging : TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (horodatage de la barre : 1735692060000) - volatility_spike : TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (horodatage de la barre : 1735692060000) - zero_volume : TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (horodatage de la barre : 1735692060000) - price_gap : TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (horodatage de la barre : 1735692060000)

Résumé des résultats

CryptoLowVolAnomalySpotBasketLongOnl-7a915cf1a2

Résumé des résultats

La stratégie a testé un panier long uniquement sur Binance Spot détenant les cinq cryptomonnaies à la plus faible volatilité issues d'un univers de 25 cryptomonnaies. Bien que l'hypothèse ait rapporté des résultats historiques positifs, la vérification a échoué dans tous les scénarios synthétiques en raison d'une TypeError Decimal/float, de sorte que la stratégie a été abandonnée avant le backtesting ou l'optimisation.

Résumé des résultats

Assurez-vous que l'arithmétique Decimal et float est traitée de manière cohérente avant la vérification synthétique.

Résumé des résultats

Elle a été abandonnée après trois itérations car la vérification de la Couche 2 a échoué : les six scénarios synthétiques ont tous déclenché une TypeError Decimal/float. Les étapes suivantes n'ont pas été atteintes.

Résumé des résultats

Elle a tenté d'exploiter une anomalie de faible volatilité crypto en détenant les cinq noms à la plus faible volatilité réalisée issus d'un univers fixe de 25 cryptomonnaies sur Binance Spot.

Résumé des résultats

L'hypothèse a rapporté un Sharpe net de 0.74, un rendement annualisé de 13.4%, un drawdown maximum de -33.6%, 430 entrées, et un rendement moyen par trade de 1.37%. Aucun rapport de backtest ou d'optimisation du pipeline n'a été enregistré.

Iteration History

Échec de la vérification (Couche 4 — révision QA) [classe=code_defect]: - [edge_concern] L'économie du bac à sable est saine et cohérente en interne, donc la rentabilité n'est NOT la raison de cet échec. avg_trade_return_pct 3.73% x avg_position_pct 8.25% x 95 trades = ~29% des fonds propres vs total_return 32.07% — le grand livre se rapproche de la courbe des fonds propres (pas de défaut comptable), et 3.73%/trade franchit l'aller-retour 0.20% BINANCE_SPOT d'environ 18x avec des commissions représentant seulement 2.64% du brut. Deux points à retenir pour l'analyste : (1) le ratio de Sharpe 0.547 rapporté n'est pas cohérent en interne avec CAGR 19.26% sur annualized_volatility 8.12% — la série MTM quotidienne n'a bougé que sur 580 des 2401 jours alors que le portefeuille détient en continu les jambes spot 5 depuis ~nov.-2022, donc les chiffres ajustés au risque (Sharpe, Sortino, vol, VaR, Calmar) sont dilués/épars par rapport au grand livre réalisé. Énoncer le symptôme, n'affirmer aucune cause. (2) l'attribution par régime montre déjà un régime calme +42.6% (Sharpe 1.40) vs un régime normal -4.1% et un régime de stress -3.7%, c.-à-d. que la prime de faible volatilité observée ici est un phénomène de régime calme dans cet échantillon — mais ces terciles sont calculés sur une fenêtre majoritairement plate (voir le problème critique), donc à réinterpréter une fois la fenêtre corrigée plutôt qu'à exploiter maintenant. - [critique] UNIVERSE MAKES 58% OF THE BACKTEST WINDOW STRUCTURALLY UNTRADEABLE, CORRUPTING EVERY RISK METRIC. La barrière d'alignement inter-jambes du modèle de base (base_template.py _maybe_process_primary + _sync_extra_iids) diffère la barre primaire tant que EVERY jambe supplémentaire de même timeframe n'a pas de barre à cet horodatage exact, et cette stratégie exige en outre une ligne complète dans _row_for(). J'ai vérifié les dates de première barre dans data/catalog pour les 25 jambes : les noms contraignants sont APTUSDT.BINANCE_SPOT (première barre 1-DAY 2022-10-20) et ICPUSDT.BINANCE_SPOT (2021-05-12). La primaire BTCUSDT.BINANCE_SPOT commence 2017-08-18, et le backtest complet n'envoie aucune date de début (backtest_agent._run_backtest n'envoie que `end`), donc le moteur s'exécute 2017-08 -> 2026-09 (~9.1 ans) alors que le portefeuille ne peut placer son premier ordre qu'à partir de ~2022-11-10 (listing APT + 21 lignes alignées de warmup). ~5.25 des 9.1 années (58%) sont marquées comme des jours plats 0.00% sur la grille quotidienne. Le bac à sable le démontre déjà exactement : les fenêtres de stress covid_crash_2020, china_ban_2021, luna_collapse_2022 et rate_shock_2022 (jan.-oct.) renvoient toutes précisément 0.0000%, tandis que ftx_collapse_2022 (à partir de 2022-11-06) est la première non nulle à -8.20%. Conséquences, qu'aucun optimiseur ni analyste ne peut annuler : (a) l'objectif de la phase -2 est un Sharpe calculé sur une série majoritairement nulle — le bac à sable rapporte 0.547 alors que le CAGR du même portefeuille sur sa portée ACTIVE est 19.26% ; (b) environ cinq années civiles rapporteront exactement 0.00% et compteront comme non positives par rapport au seuil de validité de la part d'années positives ; (c) les affirmations pré-enregistrées de l'hypothèse elle-même — Sharpe +0.74 sur 2017-2026, 6 des 9 années positives, « survit à BOTH marchés baissiers complets » (2018 -19%, 2022 -26%) — ne peuvent tout simplement pas être produites, car 2018 et les dix premiers mois de 2022 se situent dans la fenêtre morte ; (d) les terciles de régime de volatilité réalisée sont calculés sur une série majoritairement composée de jours morts. Il s'agit d'un défaut de dimensionnement/configuration avec une correction évidente, pas d'une affirmation sur l'edge : la logique de trading elle-même est correcte (voir notes). (ligne 49) — correction : choisir un univers dont tous les membres existent sur la fenêtre que l'on souhaite mesurer, et préciser dans le rationale de la configuration quelle est cette fenêtre. Dates de première barre du catalogue vérifiées pour l'univers actuel 25 : APT 2022-10-20, ICP 2021-05-12, puis FIL 2020-10-16 / AAVE 2020-10-16 / NEAR 2020-10-15 / AVAX 2020-09-23 / UNI 2020-09-18 / DOT 2020-08-19 / SOL 2020-08-12, puis BCH 2019-11-29 / HBAR 2019-09-30 / DOGE 2019-07-06 / ALGO 2019-06-23 / ATOM 2019-04-30 / LINK 2019-01-17, puis VET 2018-07-26 / ETC 2018-06-13 / TRX 2018-06-12 / XLM 2018-06-01 / XRP 2018-05-05 / ADA 2018-04-18, et BTC/ETH 2017-08-18, BNB 2017-11-07, - [avertissement] Le dimensionnement des achats lors d'une rotation est plafonné par `available = get_account_equity() * cash_buffer`, lu AFTER la sortie, et les ventes de réduction sont soumises aux lignes 279-298. Dans le backtest, c'est correct car NT — le SimulatedExchange exécute les ordres au marché dans la même itération du moteur, donc le solde USDT reflète déjà les ventes. En paper/live, ce n'est pas le cas : les ordres au marché restent en attente jusqu'au tick suivant, donc lors d'une rotation qui vend 2-3 noms pour financer 2-3 nouveaux noms, la stratégie verra le cash pré-vente, mettra à l'échelle chaque déficit par `ratio < 1`, et sous-déploiera. Comme les positions détenues ne sont redimensionnées que lorsqu'elles sortent de la bande de tolérance +/-25%, le manque à gagner peut persister jusqu'à une rotation ultérieure. Divergence backtest/paper, pas un bug de correction du backtest. (ligne 309) — correction : soit calculer `available` comme (cash + notionnel des sorties/réductions qui viennent d'être soumises), soit différer la jambe achat d'une rotation à la barre suivante, après règlement des ventes (une file d'achat différée d'une barre, indexée sur la période de rééquilibrage). Si cela reste tel quel, le préciser explicitement dans le rationale afin que le contrôle de parité paper n'interprète pas la divergence comme un défaut. - [avertissement] Les pannes de données sur une seule jambe bloquent tout le portefeuille. `_row_for()` renvoie None sauf si EVERY l'une des 25 jambes a une barre portant exactement l'horodatage de la barre primaire, et la barrière du modèle de base bloque indépendamment la barre primaire dans la même situation. Une seule barre quotidienne manquante sur un seul nom (lacune de collecte, arrêt de la bourse) saute silencieusement la ligne de ce jour-là, laissant un trou dans la fenêtre de rendement logarithmique utilisée pour la vol réalisée ; un nom qui cesse totalement de publier (radiation) fige la stratégie indéfiniment avec des positions ouvertes. Le comportement est conservateur plutôt qu'incorrect, mais il est invisible — rien ne journalise ni ne compte les jours sautés. (ligne 88) — correction : compter les lignes sautées (un entier `self._skipped_rows` est auto-persisté par le modèle de base) et journaliser un avertissement lorsqu'une jambe est absente pendant plus d'environ 2 barres primaires consécutives, afin qu'une radiation se manifeste comme un diagnostic plutôt que comme un portefeuille silencieusement plat.

Iteration History

Échec de la vérification (Couche 4 — revue QA) [classe=hypothesis_mismatch] : - [edge_concern] Le smoke backtest est rentable, mais la performance n'est pas utilisée comme un verdict d'exactitude. - [critique] L'hypothèse exige un univers fixe de 25 cryptomonnaies sur Binance Spot, mais l'implémentation ne configure que 14 instruments. Détenir 5 noms sélectionne donc 5/14 plutôt que 5/25, ce qui modifie substantiellement la stratégie cross-sectionnelle. (ligne 45) — correction : Restaurer l'univers exact de 25 noms dans extra_instruments et extra_bar_types, puis relancer la vérification et le backtesting. Alternativement, réviser formellement l'hypothèse vers la variante à 14 noms. - [critique] L'hypothèse autorise une exposition brute maximale de 1.0x, mais l'implémentation fixe max_gross_exposure à 0.95 et la plafonne à 0.95. (ligne 29) — correction : Utiliser un maximum de 1.0 ou modifier explicitement l'hypothèse pour préenregistrer le plafond de 0.95.

Raison d'abandon

verification_loop : Échec de la vérification (Couche 2 — scénarios synthétiques) : Paramètres utilisés : ['n_hold', 'cash_buffer', 'min_notional', '_param_bounds', 'rebalance_days', 'rv_lookback_days', 'target_annual_vol', 'max_gross_exposure', 'rebalance_tolerance'] Vérifier que __init__ définit tous les attributs à partir de self.parameters.get(). - steady_uptrend : TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (horodatage de la barre : 1735692060000) - steady_downtrend : TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (horodatage de la barre : 1735692060000) - flat_ranging : TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (horodatage de la barre : 1735692060000) - volatility_spike : TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (horodatage de la barre : 1735692060000) - zero_volume : TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (horodatage de la barre : 1735692060000) - price_gap : TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float' (horodatage de la barre : 1735692060000)
Rapport de stratégie

Stratmill est un outil de recherche et de paper trading, pas un conseil financier ni un courtier. Les résultats de backtest et de paper trading sont hypothétiques. Le trading comporte un risque de perte.