Skip to Content

Micro-Structure de Marché

Isoler les Nœuds Gaussiens (POC / NLV) en SQL PostgreSQL Natif
October 27, 2026 by
Administrator

1. Le Contexte & La Problématique


Dans le cadre du développement de notre infrastructure de screening quantitatif sous Odoo, notre objectif était de détecter les points d'inflexion critiques et les zones de non-liquidation (Low Volume Nodes / No-Liquidity Volume) sur des actions américaines traversant des phases de capitulation ou de forte saisonnalité (ex: Blue Bird - BLBD).

Le défi était double :

  • Éliminer les usines à gaz logicielles : Éviter de charger la mémoire de l'application avec des boucles Python ou des bibliothèques de traitement de signal lourdes (type scipy.signal ou régressions continues par moindres carrés).
  • Gommer le bruit du marché : Les micro-variations de prix au centime près créent de faux creux de volume qui aveuglent les algorithmes de détection classiques.


2. L'Architecture Algorithmique : Le "Pure Code" Déterministe


Plutôt que de déporter les calculs sur une couche applicative, nous avons fait le choix de l’efficience maximale : exécuter 100 % du traitement statistique directement dans le moteur de base de données PostgreSQL via une seule requête de fenêtrage analytique.

L'algorithme capture les volumes sur une fenêtre macro stratégique (du 3ème vendredi d'août - échéance OPEX - au 1er octobre) et discrétise l'espace en 20 strates de prix ajustées à la volatilité du titre.

La Méthodologie en 3 Étapes Clés :

  1. Extraction Symétrique des Modes : Le script calcule le prix médian de la période et isole dynamiquement le Point of Control (POC) de l'ancien régime (au-dessus du milieu) et le POC du nouveau régime (en bas, zone d'accumulation de panique).
  2. Filtre Passe-Bas par Moyenne Glissante SQL : Pour gommer le bruit et neutraliser les anomalies parasites de saisie, nous appliquons une fonction analytique OVER avec un fenêtrage physique des lignes (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING).
  3. Capture Chirurgicale du NLV (No-Liquidity Volume) : En effectuant un tri par volume de tendance lissé ascendant restreint à l'espace situé strictement entre les deux POCs principaux, le moteur extrait le creux de rupture mathématique exact.


3. L'Implémentation Technique (Le Script SQL de la Soute)


Voici la requête SQL native et optimisée que notre soute Odoo exécute en une fraction de milliseconde :


with p0 as (
  select '{to_date_s}'::date as to_date

       , '{dt_s}'::date as from_date
       , {pt_id.id} as ref_pt_id
),
p as (
  select * from p0
),
-- 1. Construction des 20 strates de l'histogramme
ticker_stats as (
  select ppi.product_tmpl_id as pt_id
       , min(ppi.close) as min_close
       , max(ppi.close) as max_close
       , nullif((max(ppi.close) - min(ppi.close)) / 20.0, 0) as bucket_size 
       , (min(ppi.close) + max(ppi.close)) / 2.0 as prix_milieu
  from product_pricelist_item ppi
  cross join p
  where ppi.product_tmpl_id = p.ref_pt_id
    and ppi.date >= p.from_date
    and ppi.date <= p.to_date
  group by ppi.product_tmpl_id
),
histogramme_rose as (
  select ppi.product_tmpl_id as pt_id
       , ts.bucket_size
       , ts.prix_milieu
       , floor(ppi.close / ts.bucket_size) * ts.bucket_size as price_bucket_start
       , sum(ppi.volume) as volume_barre_rose
       , count(*) as jours_dans_strate
  from product_pricelist_item ppi
  join ticker_stats ts on ppi.product_tmpl_id = ts.pt_id
  cross join p
  where ppi.date >= p.from_date
    and ppi.date <= p.to_date
  group by ppi.product_tmpl_id, ts.bucket_size, ts.prix_milieu, floor(ppi.close / ts.bucket_size)
),
bornes_extremes as (
  select max(price_bucket_start) as max_price_bucket
       , min(price_bucket_start) as min_price_bucket
  from histogramme_rose
),
-- 2. Sélection des deux POCs sur les volumes bruts (en excluant les extrémités parasites)
premier_poc_haut as (
  select price_bucket_start as prix_poc_haut
  from histogramme_rose h
  cross join bornes_extremes b
  where h.price_bucket_start >= h.prix_milieu
    and h.price_bucket_start < b.max_price_bucket
  order by volume_barre_rose desc
  limit 1
),
second_poc_bas as (
  select price_bucket_start as prix_poc_bas
  from histogramme_rose h
  cross join bornes_extremes b
  where h.price_bucket_start < h.prix_milieu
    and h.price_bucket_start > b.min_price_bucket
  order by volume_barre_rose desc
  limit 1
),
-- 3. ÉTAPE CLÉ : Lissage de tendance pour gommer le bruit local
histogramme_lisse as (
  select price_bucket_start
       , volume_barre_rose
       , avg(volume_barre_rose) over(
           order by price_bucket_start 
           rows between 1 preceding and 1 following
         ) as volume_tendance
  from histogramme_rose
),
-- 4. Recherche du CREUX (NLV) sur la tendance lissée
creux_entre_les_deux as (
  select hl.price_bucket_start as prix_creux
  from histogramme_lisse hl
  cross join premier_poc_haut ph
  cross join second_poc_bas pb
  where hl.price_bucket_start > pb.prix_poc_bas
    and hl.price_bucket_start < ph.prix_poc_haut
  order by hl.volume_tendance asc
  limit 1
),
-- 5. Restitution de la structure propre avec les vrais volumes réels
jalons_structurels as (
  select h.*
       , concat_ws(' / '
           , case when h.price_bucket_start = (select max_price_bucket from bornes_extremes) then 'max' end
           , case when h.price_bucket_start = (select min_price_bucket from bornes_extremes) then 'min' end
           , case when h.price_bucket_start = (select prix_poc_haut from premier_poc_haut) then 'poc_ancien_regime (Haut)' end
           , case when h.price_bucket_start = (select prix_poc_bas from second_poc_bas) then 'poc_nouveau_regime (Bas)' end
           , case when h.price_bucket_start = (select prix_creux from creux_entre_les_deux) then 'creux_rupture' end
         ) as info_text
  from histogramme_rose h
), data as (
select pt_id
     , round(price_bucket_start::numeric, 2) as niveau_prix_regime
     , round((price_bucket_start + bucket_size)::numeric, 2) as fin_zone_regime
     , info_text
     , round(volume_barre_rose::numeric, 0) as volume_total_strate
     , jours_dans_strate as jours_dans_la_zone
from jalons_structurels
where info_text <> ''
order by niveau_prix_regime desc
)
select * from data;


4. L'Impact Opérationnel & Validation Fondamentale


En couplant cette approche statistique froide avec notre lecture fondamentale des cycles d'exploitation (détection des anomalies de stocks saisonniers), le moteur a immédiatement validé une asymétrie de marché majeure :

  • Zéro dépendance applicative : Un temps d'exécution inférieur à la milliseconde lors des grands recalculs d'univers (+500 tickers).
  • Précision Chirurgicale des Niveaux : L'isolation du creux_rupture (NLV à 55.50 $ sur BLBD) a identifié la zone d'épuisement total des vendeurs (absence de liquidité de flux). Le cours y a dessiné une stabilisation et un pivot parfait, offrant un point d'entrée contrarien à fort ratio gain/risque (Risk/Reward).

Ce cas démontre l'immense valeur ajoutée d'une architecture informatique épurée mise au service de l'analyse quantitative.

Pattern Recognition & Secteurs Virtuels
Inverser l’Ingénierie des Flux par Matching Temporel